From 4a5360b97ebea97ebe47e9c9e97c3fda4f579734 Mon Sep 17 00:00:00 2001 From: nobody Date: Fri, 10 Aug 2001 15:05:27 +0000 Subject: [PATCH 1/1] This commit was manufactured by cvs2svn to create branch 'busybox_0_60_stable'. --- busybox/.cvsignore | 4 + busybox/.indent.pro | 33 + busybox/AUTHORS | 81 + busybox/Changelog | 1177 ++ busybox/Config.h | 476 + busybox/INSTALL | 8 + busybox/LICENSE | 378 + busybox/Makefile | 443 + busybox/README | 152 + busybox/TODO | 57 + busybox/adjtimex.c | 176 + busybox/applets.c | 113 + busybox/applets.h | 481 + busybox/applets/applets.c | 113 + busybox/applets/busybox.c | 181 + busybox/applets/busybox.mkll | 24 + busybox/applets/busybox.sh | 16 + busybox/applets/install.sh | 52 + busybox/applets/usage.c | 10 + busybox/applets/usage.h | 1829 +++ busybox/ar.c | 89 + busybox/archival/ar.c | 89 + busybox/archival/cpio.c | 95 + busybox/archival/dpkg.c | 1445 ++ busybox/archival/dpkg_deb.c | 130 + busybox/archival/gunzip.c | 183 + busybox/archival/gzip.c | 2550 +++ .../archival/libunarchive/decompress_unzip.c | 1026 ++ busybox/archival/libunarchive/unzip.c | 1026 ++ busybox/archival/rpm2cpio.c | 95 + busybox/archival/tar.c | 1148 ++ busybox/ash.c | 12880 ++++++++++++++++ busybox/basename.c | 52 + busybox/busybox.c | 181 + busybox/busybox.h | 106 + busybox/busybox.mkll | 24 + busybox/busybox.sh | 16 + busybox/busybox.spec | 44 + busybox/cat.c | 53 + busybox/chgrp.c | 91 + busybox/chmod.c | 85 + busybox/chown.c | 113 + busybox/chroot.c | 75 + busybox/chvt.c | 43 + busybox/clear.c | 34 + busybox/cmdedit.c | 1521 ++ busybox/cmdedit.h | 6 + busybox/cmp.c | 80 + busybox/console-tools/chvt.c | 43 + busybox/console-tools/clear.c | 34 + busybox/console-tools/deallocvt.c | 43 + busybox/console-tools/dumpkmap.c | 95 + busybox/console-tools/loadacm.c | 357 + busybox/console-tools/loadfont.c | 209 + busybox/console-tools/loadkmap.c | 89 + busybox/console-tools/reset.c | 35 + busybox/console-tools/setkeycodes.c | 72 + busybox/coreutils/basename.c | 52 + busybox/coreutils/cat.c | 53 + busybox/coreutils/chgrp.c | 91 + busybox/coreutils/chmod.c | 85 + busybox/coreutils/chown.c | 113 + busybox/coreutils/chroot.c | 75 + busybox/coreutils/cmp.c | 80 + busybox/coreutils/cp.c | 114 + busybox/coreutils/cut.c | 356 + busybox/coreutils/date.c | 247 + busybox/coreutils/dd.c | 154 + busybox/coreutils/df.c | 158 + busybox/coreutils/dirname.c | 40 + busybox/coreutils/dos2unix.c | 188 + busybox/coreutils/du.c | 257 + busybox/coreutils/echo.c | 152 + busybox/coreutils/env.c | 106 + busybox/coreutils/expr.c | 535 + busybox/coreutils/head.c | 99 + busybox/coreutils/hostid.c | 32 + busybox/coreutils/id.c | 97 + busybox/coreutils/length.c | 13 + busybox/coreutils/ln.c | 131 + busybox/coreutils/logname.c | 41 + busybox/coreutils/ls.c | 937 ++ busybox/coreutils/md5sum.c | 1074 ++ busybox/coreutils/mkdir.c | 64 + busybox/coreutils/mkfifo.c | 60 + busybox/coreutils/mknod.c | 92 + busybox/coreutils/mv.c | 168 + busybox/coreutils/printf.c | 455 + busybox/coreutils/pwd.c | 44 + busybox/coreutils/rm.c | 77 + busybox/coreutils/rmdir.c | 45 + busybox/coreutils/sleep.c | 38 + busybox/coreutils/sort.c | 106 + busybox/coreutils/stty.c | 1376 ++ busybox/coreutils/sync.c | 35 + busybox/coreutils/tail.c | 251 + busybox/coreutils/tee.c | 68 + busybox/coreutils/test.c | 579 + busybox/coreutils/touch.c | 75 + busybox/coreutils/tr.c | 248 + busybox/coreutils/tty.c | 44 + busybox/coreutils/uname.c | 156 + busybox/coreutils/uniq.c | 89 + busybox/coreutils/usleep.c | 38 + busybox/coreutils/uudecode.c | 353 + busybox/coreutils/uuencode.c | 180 + busybox/coreutils/wc.c | 156 + busybox/coreutils/whoami.c | 44 + busybox/coreutils/yes.c | 53 + busybox/cp.c | 114 + busybox/cpio.c | 95 + busybox/cut.c | 356 + busybox/date.c | 247 + busybox/dc.c | 182 + busybox/dd.c | 154 + busybox/deallocvt.c | 43 + busybox/debian/Config.h-deb | 473 + busybox/debian/Config.h-static | 473 + busybox/debian/Config.h-udeb | 473 + busybox/debian/README.debian | 10 + busybox/debian/changelog | 175 + busybox/debian/control | 66 + busybox/debian/copyright | 7 + busybox/debian/rules | 193 + busybox/df.c | 158 + busybox/dirname.c | 40 + busybox/dmesg.c | 95 + busybox/docs/.cvsignore | 8 + busybox/docs/autodocifier.pl | 287 + busybox/docs/busybox.net/.cvsignore | 1 + busybox/docs/busybox.net/busybox-growth.ps | 404 + busybox/docs/busybox.net/images/busybox.jpeg | Bin 0 -> 9023 bytes busybox/docs/busybox.net/images/busybox2.jpg | Bin 0 -> 8204 bytes busybox/docs/busybox.net/images/fm.mini.png | Bin 0 -> 7708 bytes .../docs/busybox.net/images/gfx_by_gimp.png | Bin 0 -> 3955 bytes busybox/docs/busybox.net/images/ltbutton2.png | Bin 0 -> 6799 bytes busybox/docs/busybox.net/images/sdsmall.png | Bin 0 -> 1593 bytes .../docs/busybox.net/images/written.in.vi.png | Bin 0 -> 4394 bytes busybox/docs/busybox.net/index.html | 376 + busybox/docs/busybox.net/oldnews.html | 465 + busybox/docs/busybox.net/screenshot.html | 54 + busybox/docs/busybox.sgml | 3974 +++++ busybox/docs/busybox_footer.pod | 169 + busybox/docs/busybox_header.pod | 73 + busybox/docs/contributing.txt | 476 + busybox/docs/new-applet-HOWTO.txt | 138 + busybox/docs/style-guide.txt | 680 + busybox/dos2unix.c | 188 + busybox/dpkg.c | 1445 ++ busybox/dpkg_deb.c | 130 + busybox/du.c | 257 + busybox/dumpkmap.c | 95 + busybox/dutmp.c | 64 + busybox/echo.c | 152 + busybox/editors/sed.c | 800 + busybox/editors/vi.c | 3947 +++++ busybox/env.c | 106 + busybox/examples/bootfloppy/bootfloppy.txt | 185 + busybox/examples/bootfloppy/display.txt | 4 + busybox/examples/bootfloppy/etc/fstab | 2 + busybox/examples/bootfloppy/etc/init.d/rcS | 3 + busybox/examples/bootfloppy/etc/inittab | 5 + busybox/examples/bootfloppy/etc/profile | 8 + busybox/examples/bootfloppy/mkdevs.sh | 62 + busybox/examples/bootfloppy/mkrootfs.sh | 106 + busybox/examples/bootfloppy/mksyslinux.sh | 48 + busybox/examples/bootfloppy/quickstart.txt | 15 + busybox/examples/bootfloppy/syslinux.cfg | 7 + busybox/examples/busybox.spec | 44 + busybox/examples/depmod.pl | 227 + busybox/examples/inittab | 86 + .../kernel-patches/Will_devps_GoIntoTheKernel | 104 + .../kernel-patches/devps.patch.9_25_2000 | 1478 ++ busybox/examples/mk2knr.pl | 84 + busybox/examples/undeb | 53 + busybox/examples/unrpm | 48 + busybox/expr.c | 535 + busybox/fbset.c | 424 + busybox/fdflush.c | 47 + busybox/find.c | 200 + busybox/findutils/find.c | 200 + busybox/findutils/grep.c | 359 + busybox/findutils/which.c | 80 + busybox/findutils/xargs.c | 105 + busybox/free.c | 69 + busybox/freeramdisk.c | 65 + busybox/fsck_minix.c | 1484 ++ busybox/getopt.c | 402 + busybox/grep.c | 359 + busybox/gunzip.c | 183 + busybox/gzip.c | 2550 +++ busybox/halt.c | 38 + busybox/head.c | 99 + busybox/hostid.c | 32 + busybox/hostname.c | 128 + busybox/hush.c | 2692 ++++ busybox/id.c | 97 + busybox/ifconfig.c | 504 + busybox/include/applets.h | 481 + busybox/include/busybox.h | 106 + busybox/include/grp.h | 37 + busybox/include/libbb.h | 325 + busybox/include/pwd.h | 40 + busybox/include/usage.h | 1829 +++ busybox/init.c | 1040 ++ busybox/init/halt.c | 38 + busybox/init/init.c | 1040 ++ busybox/init/poweroff.c | 38 + busybox/init/reboot.c | 46 + busybox/insmod.c | 3503 +++++ busybox/install.sh | 52 + busybox/kill.c | 142 + busybox/klogd.c | 153 + busybox/lash.c | 1632 ++ busybox/length.c | 13 + busybox/libbb/.cvsignore | 1 + busybox/libbb/Makefile | 11 + busybox/libbb/README | 15 + busybox/libbb/arith.c | 263 + busybox/libbb/ask_confirmation.c | 53 + busybox/libbb/chomp.c | 49 + busybox/libbb/concat_path_file.c | 26 + busybox/libbb/copy_file.c | 237 + busybox/libbb/copy_file_chunk.c | 74 + busybox/libbb/copyfd.c | 59 + busybox/libbb/create_icmp_socket.c | 37 + busybox/libbb/device_open.c | 59 + busybox/libbb/dirname.c | 49 + busybox/libbb/error_msg.c | 52 + busybox/libbb/error_msg_and_die.c | 53 + busybox/libbb/fgets_str.c | 64 + busybox/libbb/find_mount_point.c | 81 + busybox/libbb/find_pid_by_name.c | 199 + busybox/libbb/find_root_device.c | 84 + busybox/libbb/full_read.c | 70 + busybox/libbb/full_write.c | 66 + busybox/libbb/get_console.c | 129 + busybox/libbb/get_last_path_component.c | 71 + busybox/libbb/get_line_from_file.c | 72 + busybox/libbb/gz_open.c | 35 + busybox/libbb/herror_msg.c | 50 + busybox/libbb/herror_msg_and_die.c | 51 + busybox/libbb/human_readable.c | 88 + busybox/libbb/inode_hash.c | 106 + busybox/libbb/interface.c | 2168 +++ busybox/libbb/isdirectory.c | 71 + busybox/libbb/kernel_version.c | 66 + busybox/libbb/last_char_is.c | 40 + busybox/libbb/libbb.h | 325 + busybox/libbb/libc5.c | 167 + busybox/libbb/loop.c | 128 + busybox/libbb/make_directory.c | 67 + busybox/libbb/messages.c | 67 + busybox/libbb/mk_loop_h.sh | 37 + busybox/libbb/mode_string.c | 82 + busybox/libbb/module_syscalls.c | 88 + busybox/libbb/mtab.c | 95 + busybox/libbb/mtab_file.c | 52 + busybox/libbb/my_getgrgid.c | 55 + busybox/libbb/my_getgrnam.c | 56 + busybox/libbb/my_getpwnam.c | 56 + busybox/libbb/my_getpwnamegid.c | 61 + busybox/libbb/my_getpwuid.c | 55 + busybox/libbb/parse_mode.c | 138 + busybox/libbb/parse_number.c | 74 + busybox/libbb/perror_msg.c | 51 + busybox/libbb/perror_msg_and_die.c | 52 + busybox/libbb/print_file.c | 61 + busybox/libbb/process_escape_sequence.c | 80 + busybox/libbb/read_package_field.c | 91 + busybox/libbb/real_loop.h | 37 + busybox/libbb/recursive_action.c | 150 + busybox/libbb/remove_file.c | 129 + busybox/libbb/safe_read.c | 54 + busybox/libbb/safe_strncpy.c | 48 + busybox/libbb/simplify_path.c | 78 + busybox/libbb/syscalls.c | 115 + busybox/libbb/syslog_msg_with_name.c | 51 + busybox/libbb/time_string.c | 68 + busybox/libbb/trim.c | 53 + busybox/libbb/u_signal_names.c | 166 + busybox/libbb/unarchive.c | 601 + busybox/libbb/unzip.c | 1026 ++ busybox/libbb/vdprintf.c | 53 + busybox/libbb/verror_msg.c | 49 + busybox/libbb/vherror_msg.c | 44 + busybox/libbb/vperror_msg.c | 51 + busybox/libbb/wfopen.c | 50 + busybox/libbb/xfuncs.c | 112 + busybox/libbb/xgetcwd.c | 52 + busybox/libbb/xgethostbyname.c | 37 + busybox/libbb/xreadlink.c | 37 + busybox/libbb/xregcomp.c | 53 + busybox/ln.c | 131 + busybox/loadacm.c | 357 + busybox/loadfont.c | 209 + busybox/loadkmap.c | 89 + busybox/logger.c | 200 + busybox/logname.c | 41 + busybox/logread.c | 144 + busybox/ls.c | 937 ++ busybox/lsmod.c | 166 + busybox/makedevs.c | 95 + busybox/md5sum.c | 1074 ++ busybox/miscutils/adjtimex.c | 176 + busybox/miscutils/dc.c | 182 + busybox/miscutils/dutmp.c | 64 + busybox/miscutils/makedevs.c | 95 + busybox/miscutils/mktemp.c | 40 + busybox/miscutils/mt.c | 121 + busybox/miscutils/readlink.c | 48 + busybox/miscutils/update.c | 112 + busybox/miscutils/watchdog.c | 49 + busybox/mk_loop_h.sh | 37 + busybox/mkdir.c | 64 + busybox/mkfifo.c | 60 + busybox/mkfs_minix.c | 853 + busybox/mknod.c | 92 + busybox/mkswap.c | 422 + busybox/mktemp.c | 40 + busybox/modprobe.c | 121 + busybox/modutils/insmod.c | 3503 +++++ busybox/modutils/lsmod.c | 166 + busybox/modutils/modprobe.c | 121 + busybox/modutils/rmmod.c | 62 + busybox/more.c | 217 + busybox/mount.c | 472 + busybox/msh.c | 4868 ++++++ busybox/mt.c | 121 + busybox/mv.c | 168 + busybox/nc.c | 137 + busybox/networking/hostname.c | 128 + busybox/networking/ifconfig.c | 504 + busybox/networking/nc.c | 137 + busybox/networking/nslookup.c | 183 + busybox/networking/ping.c | 555 + busybox/networking/route.c | 428 + busybox/networking/telnet.c | 711 + busybox/networking/tftp.c | 414 + busybox/networking/traceroute.c | 652 + busybox/networking/wget.c | 834 + busybox/nfsmount.c | 977 ++ busybox/nfsmount.h | 242 + busybox/nslookup.c | 183 + busybox/pidof.c | 79 + busybox/ping.c | 555 + busybox/pivot_root.c | 35 + busybox/poweroff.c | 38 + busybox/printf.c | 455 + busybox/pristine_setup.sh | 46 + busybox/procps/free.c | 69 + busybox/procps/kill.c | 142 + busybox/procps/pidof.c | 79 + busybox/procps/ps.c | 266 + busybox/procps/renice.c | 54 + busybox/procps/uptime.c | 77 + busybox/ps.c | 266 + busybox/pwd.c | 44 + busybox/rdate.c | 116 + busybox/readlink.c | 48 + busybox/reboot.c | 46 + busybox/renice.c | 54 + busybox/reset.c | 35 + busybox/rm.c | 77 + busybox/rmdir.c | 45 + busybox/rmmod.c | 62 + busybox/route.c | 428 + busybox/rpm2cpio.c | 95 + busybox/scripts/depmod.pl | 227 + busybox/scripts/inittab | 86 + busybox/scripts/mk2knr.pl | 84 + busybox/scripts/undeb | 53 + busybox/scripts/unrpm | 48 + busybox/sed.c | 800 + busybox/setkeycodes.c | 72 + busybox/shell/ash.c | 12880 ++++++++++++++++ busybox/shell/cmdedit.c | 1521 ++ busybox/shell/cmdedit.h | 6 + busybox/shell/hush.c | 2692 ++++ busybox/shell/lash.c | 1632 ++ busybox/shell/msh.c | 4868 ++++++ busybox/sleep.c | 38 + busybox/sort.c | 106 + busybox/stty.c | 1376 ++ busybox/swaponoff.c | 115 + busybox/sync.c | 35 + busybox/sysklogd/klogd.c | 153 + busybox/sysklogd/logger.c | 200 + busybox/sysklogd/logread.c | 144 + busybox/sysklogd/syslogd.c | 639 + busybox/syslogd.c | 639 + busybox/tail.c | 251 + busybox/tar.c | 1148 ++ busybox/tee.c | 68 + busybox/telnet.c | 711 + busybox/test.c | 579 + busybox/tests/.cvsignore | 16 + busybox/tests/Makefile | 34 + busybox/tests/cp_tests.mk | 360 + busybox/tests/ln_tests.mk | 71 + busybox/tests/multibuild.pl | 73 + busybox/tests/multifeat.pl | 83 + busybox/tests/mv_tests.mk | 167 + busybox/tests/sh.testcases | 89 + busybox/tests/syslog_test.c | 19 + busybox/tests/testcases | 406 + busybox/tests/tester.sh | 158 + busybox/tests/tst-syslogd.c | 44 + busybox/tftp.c | 414 + busybox/touch.c | 75 + busybox/tr.c | 248 + busybox/traceroute.c | 652 + busybox/true_false.c | 39 + busybox/tty.c | 44 + busybox/umount.c | 298 + busybox/uname.c | 156 + busybox/uniq.c | 89 + busybox/update.c | 112 + busybox/uptime.c | 77 + busybox/usage.c | 10 + busybox/usage.h | 1829 +++ busybox/usleep.c | 38 + busybox/util-linux/dmesg.c | 95 + busybox/util-linux/fbset.c | 424 + busybox/util-linux/fdflush.c | 47 + busybox/util-linux/freeramdisk.c | 65 + busybox/util-linux/fsck_minix.c | 1484 ++ busybox/util-linux/getopt.c | 402 + busybox/util-linux/mkfs_minix.c | 853 + busybox/util-linux/mkswap.c | 422 + busybox/util-linux/more.c | 217 + busybox/util-linux/mount.c | 472 + busybox/util-linux/nfsmount.c | 977 ++ busybox/util-linux/nfsmount.h | 242 + busybox/util-linux/pivot_root.c | 35 + busybox/util-linux/rdate.c | 116 + busybox/util-linux/swaponoff.c | 115 + busybox/util-linux/umount.c | 298 + busybox/uudecode.c | 353 + busybox/uuencode.c | 180 + busybox/vi.c | 3947 +++++ busybox/watchdog.c | 49 + busybox/wc.c | 156 + busybox/wget.c | 834 + busybox/which.c | 80 + busybox/whoami.c | 44 + busybox/xargs.c | 105 + busybox/yes.c | 53 + 448 files changed, 165065 insertions(+) create mode 100644 busybox/.cvsignore create mode 100644 busybox/.indent.pro create mode 100644 busybox/AUTHORS create mode 100644 busybox/Changelog create mode 100644 busybox/Config.h create mode 100644 busybox/INSTALL create mode 100644 busybox/LICENSE create mode 100644 busybox/Makefile create mode 100644 busybox/README create mode 100644 busybox/TODO create mode 100644 busybox/adjtimex.c create mode 100644 busybox/applets.c create mode 100644 busybox/applets.h create mode 100644 busybox/applets/applets.c create mode 100644 busybox/applets/busybox.c create mode 100755 busybox/applets/busybox.mkll create mode 100755 busybox/applets/busybox.sh create mode 100755 busybox/applets/install.sh create mode 100644 busybox/applets/usage.c create mode 100644 busybox/applets/usage.h create mode 100644 busybox/ar.c create mode 100644 busybox/archival/ar.c create mode 100644 busybox/archival/cpio.c create mode 100644 busybox/archival/dpkg.c create mode 100644 busybox/archival/dpkg_deb.c create mode 100644 busybox/archival/gunzip.c create mode 100644 busybox/archival/gzip.c create mode 100644 busybox/archival/libunarchive/decompress_unzip.c create mode 100644 busybox/archival/libunarchive/unzip.c create mode 100644 busybox/archival/rpm2cpio.c create mode 100644 busybox/archival/tar.c create mode 100644 busybox/ash.c create mode 100644 busybox/basename.c create mode 100644 busybox/busybox.c create mode 100644 busybox/busybox.h create mode 100755 busybox/busybox.mkll create mode 100755 busybox/busybox.sh create mode 100644 busybox/busybox.spec create mode 100644 busybox/cat.c create mode 100644 busybox/chgrp.c create mode 100644 busybox/chmod.c create mode 100644 busybox/chown.c create mode 100644 busybox/chroot.c create mode 100644 busybox/chvt.c create mode 100644 busybox/clear.c create mode 100644 busybox/cmdedit.c create mode 100644 busybox/cmdedit.h create mode 100644 busybox/cmp.c create mode 100644 busybox/console-tools/chvt.c create mode 100644 busybox/console-tools/clear.c create mode 100644 busybox/console-tools/deallocvt.c create mode 100644 busybox/console-tools/dumpkmap.c create mode 100644 busybox/console-tools/loadacm.c create mode 100644 busybox/console-tools/loadfont.c create mode 100644 busybox/console-tools/loadkmap.c create mode 100644 busybox/console-tools/reset.c create mode 100644 busybox/console-tools/setkeycodes.c create mode 100644 busybox/coreutils/basename.c create mode 100644 busybox/coreutils/cat.c create mode 100644 busybox/coreutils/chgrp.c create mode 100644 busybox/coreutils/chmod.c create mode 100644 busybox/coreutils/chown.c create mode 100644 busybox/coreutils/chroot.c create mode 100644 busybox/coreutils/cmp.c create mode 100644 busybox/coreutils/cp.c create mode 100644 busybox/coreutils/cut.c create mode 100644 busybox/coreutils/date.c create mode 100644 busybox/coreutils/dd.c create mode 100644 busybox/coreutils/df.c create mode 100644 busybox/coreutils/dirname.c create mode 100644 busybox/coreutils/dos2unix.c create mode 100644 busybox/coreutils/du.c create mode 100644 busybox/coreutils/echo.c create mode 100644 busybox/coreutils/env.c create mode 100644 busybox/coreutils/expr.c create mode 100644 busybox/coreutils/head.c create mode 100644 busybox/coreutils/hostid.c create mode 100644 busybox/coreutils/id.c create mode 100644 busybox/coreutils/length.c create mode 100644 busybox/coreutils/ln.c create mode 100644 busybox/coreutils/logname.c create mode 100644 busybox/coreutils/ls.c create mode 100644 busybox/coreutils/md5sum.c create mode 100644 busybox/coreutils/mkdir.c create mode 100644 busybox/coreutils/mkfifo.c create mode 100644 busybox/coreutils/mknod.c create mode 100644 busybox/coreutils/mv.c create mode 100644 busybox/coreutils/printf.c create mode 100644 busybox/coreutils/pwd.c create mode 100644 busybox/coreutils/rm.c create mode 100644 busybox/coreutils/rmdir.c create mode 100644 busybox/coreutils/sleep.c create mode 100644 busybox/coreutils/sort.c create mode 100644 busybox/coreutils/stty.c create mode 100644 busybox/coreutils/sync.c create mode 100644 busybox/coreutils/tail.c create mode 100644 busybox/coreutils/tee.c create mode 100644 busybox/coreutils/test.c create mode 100644 busybox/coreutils/touch.c create mode 100644 busybox/coreutils/tr.c create mode 100644 busybox/coreutils/tty.c create mode 100644 busybox/coreutils/uname.c create mode 100644 busybox/coreutils/uniq.c create mode 100644 busybox/coreutils/usleep.c create mode 100644 busybox/coreutils/uudecode.c create mode 100644 busybox/coreutils/uuencode.c create mode 100644 busybox/coreutils/wc.c create mode 100644 busybox/coreutils/whoami.c create mode 100644 busybox/coreutils/yes.c create mode 100644 busybox/cp.c create mode 100644 busybox/cpio.c create mode 100644 busybox/cut.c create mode 100644 busybox/date.c create mode 100644 busybox/dc.c create mode 100644 busybox/dd.c create mode 100644 busybox/deallocvt.c create mode 100644 busybox/debian/Config.h-deb create mode 100644 busybox/debian/Config.h-static create mode 100644 busybox/debian/Config.h-udeb create mode 100644 busybox/debian/README.debian create mode 100644 busybox/debian/changelog create mode 100644 busybox/debian/control create mode 100644 busybox/debian/copyright create mode 100755 busybox/debian/rules create mode 100644 busybox/df.c create mode 100644 busybox/dirname.c create mode 100644 busybox/dmesg.c create mode 100644 busybox/docs/.cvsignore create mode 100755 busybox/docs/autodocifier.pl create mode 100644 busybox/docs/busybox.net/.cvsignore create mode 100644 busybox/docs/busybox.net/busybox-growth.ps create mode 100644 busybox/docs/busybox.net/images/busybox.jpeg create mode 100644 busybox/docs/busybox.net/images/busybox2.jpg create mode 100644 busybox/docs/busybox.net/images/fm.mini.png create mode 100644 busybox/docs/busybox.net/images/gfx_by_gimp.png create mode 100644 busybox/docs/busybox.net/images/ltbutton2.png create mode 100644 busybox/docs/busybox.net/images/sdsmall.png create mode 100644 busybox/docs/busybox.net/images/written.in.vi.png create mode 100644 busybox/docs/busybox.net/index.html create mode 100644 busybox/docs/busybox.net/oldnews.html create mode 100644 busybox/docs/busybox.net/screenshot.html create mode 100644 busybox/docs/busybox.sgml create mode 100644 busybox/docs/busybox_footer.pod create mode 100644 busybox/docs/busybox_header.pod create mode 100644 busybox/docs/contributing.txt create mode 100644 busybox/docs/new-applet-HOWTO.txt create mode 100644 busybox/docs/style-guide.txt create mode 100644 busybox/dos2unix.c create mode 100644 busybox/dpkg.c create mode 100644 busybox/dpkg_deb.c create mode 100644 busybox/du.c create mode 100644 busybox/dumpkmap.c create mode 100644 busybox/dutmp.c create mode 100644 busybox/echo.c create mode 100644 busybox/editors/sed.c create mode 100644 busybox/editors/vi.c create mode 100644 busybox/env.c create mode 100644 busybox/examples/bootfloppy/bootfloppy.txt create mode 100644 busybox/examples/bootfloppy/display.txt create mode 100644 busybox/examples/bootfloppy/etc/fstab create mode 100755 busybox/examples/bootfloppy/etc/init.d/rcS create mode 100644 busybox/examples/bootfloppy/etc/inittab create mode 100644 busybox/examples/bootfloppy/etc/profile create mode 100755 busybox/examples/bootfloppy/mkdevs.sh create mode 100755 busybox/examples/bootfloppy/mkrootfs.sh create mode 100755 busybox/examples/bootfloppy/mksyslinux.sh create mode 100644 busybox/examples/bootfloppy/quickstart.txt create mode 100644 busybox/examples/bootfloppy/syslinux.cfg create mode 100644 busybox/examples/busybox.spec create mode 100755 busybox/examples/depmod.pl create mode 100644 busybox/examples/inittab create mode 100644 busybox/examples/kernel-patches/Will_devps_GoIntoTheKernel create mode 100644 busybox/examples/kernel-patches/devps.patch.9_25_2000 create mode 100755 busybox/examples/mk2knr.pl create mode 100644 busybox/examples/undeb create mode 100644 busybox/examples/unrpm create mode 100644 busybox/expr.c create mode 100644 busybox/fbset.c create mode 100644 busybox/fdflush.c create mode 100644 busybox/find.c create mode 100644 busybox/findutils/find.c create mode 100644 busybox/findutils/grep.c create mode 100644 busybox/findutils/which.c create mode 100644 busybox/findutils/xargs.c create mode 100644 busybox/free.c create mode 100644 busybox/freeramdisk.c create mode 100644 busybox/fsck_minix.c create mode 100644 busybox/getopt.c create mode 100644 busybox/grep.c create mode 100644 busybox/gunzip.c create mode 100644 busybox/gzip.c create mode 100644 busybox/halt.c create mode 100644 busybox/head.c create mode 100644 busybox/hostid.c create mode 100644 busybox/hostname.c create mode 100644 busybox/hush.c create mode 100644 busybox/id.c create mode 100644 busybox/ifconfig.c create mode 100644 busybox/include/applets.h create mode 100644 busybox/include/busybox.h create mode 100644 busybox/include/grp.h create mode 100644 busybox/include/libbb.h create mode 100644 busybox/include/pwd.h create mode 100644 busybox/include/usage.h create mode 100644 busybox/init.c create mode 100644 busybox/init/halt.c create mode 100644 busybox/init/init.c create mode 100644 busybox/init/poweroff.c create mode 100644 busybox/init/reboot.c create mode 100644 busybox/insmod.c create mode 100755 busybox/install.sh create mode 100644 busybox/kill.c create mode 100644 busybox/klogd.c create mode 100644 busybox/lash.c create mode 100644 busybox/length.c create mode 100644 busybox/libbb/.cvsignore create mode 100644 busybox/libbb/Makefile create mode 100644 busybox/libbb/README create mode 100644 busybox/libbb/arith.c create mode 100644 busybox/libbb/ask_confirmation.c create mode 100644 busybox/libbb/chomp.c create mode 100644 busybox/libbb/concat_path_file.c create mode 100644 busybox/libbb/copy_file.c create mode 100644 busybox/libbb/copy_file_chunk.c create mode 100644 busybox/libbb/copyfd.c create mode 100644 busybox/libbb/create_icmp_socket.c create mode 100644 busybox/libbb/device_open.c create mode 100644 busybox/libbb/dirname.c create mode 100644 busybox/libbb/error_msg.c create mode 100644 busybox/libbb/error_msg_and_die.c create mode 100644 busybox/libbb/fgets_str.c create mode 100644 busybox/libbb/find_mount_point.c create mode 100644 busybox/libbb/find_pid_by_name.c create mode 100644 busybox/libbb/find_root_device.c create mode 100644 busybox/libbb/full_read.c create mode 100644 busybox/libbb/full_write.c create mode 100644 busybox/libbb/get_console.c create mode 100644 busybox/libbb/get_last_path_component.c create mode 100644 busybox/libbb/get_line_from_file.c create mode 100644 busybox/libbb/gz_open.c create mode 100644 busybox/libbb/herror_msg.c create mode 100644 busybox/libbb/herror_msg_and_die.c create mode 100644 busybox/libbb/human_readable.c create mode 100644 busybox/libbb/inode_hash.c create mode 100644 busybox/libbb/interface.c create mode 100644 busybox/libbb/isdirectory.c create mode 100644 busybox/libbb/kernel_version.c create mode 100644 busybox/libbb/last_char_is.c create mode 100644 busybox/libbb/libbb.h create mode 100644 busybox/libbb/libc5.c create mode 100644 busybox/libbb/loop.c create mode 100644 busybox/libbb/make_directory.c create mode 100644 busybox/libbb/messages.c create mode 100755 busybox/libbb/mk_loop_h.sh create mode 100644 busybox/libbb/mode_string.c create mode 100644 busybox/libbb/module_syscalls.c create mode 100644 busybox/libbb/mtab.c create mode 100644 busybox/libbb/mtab_file.c create mode 100644 busybox/libbb/my_getgrgid.c create mode 100644 busybox/libbb/my_getgrnam.c create mode 100644 busybox/libbb/my_getpwnam.c create mode 100644 busybox/libbb/my_getpwnamegid.c create mode 100644 busybox/libbb/my_getpwuid.c create mode 100644 busybox/libbb/parse_mode.c create mode 100644 busybox/libbb/parse_number.c create mode 100644 busybox/libbb/perror_msg.c create mode 100644 busybox/libbb/perror_msg_and_die.c create mode 100644 busybox/libbb/print_file.c create mode 100644 busybox/libbb/process_escape_sequence.c create mode 100644 busybox/libbb/read_package_field.c create mode 100644 busybox/libbb/real_loop.h create mode 100644 busybox/libbb/recursive_action.c create mode 100644 busybox/libbb/remove_file.c create mode 100644 busybox/libbb/safe_read.c create mode 100644 busybox/libbb/safe_strncpy.c create mode 100644 busybox/libbb/simplify_path.c create mode 100644 busybox/libbb/syscalls.c create mode 100644 busybox/libbb/syslog_msg_with_name.c create mode 100644 busybox/libbb/time_string.c create mode 100644 busybox/libbb/trim.c create mode 100644 busybox/libbb/u_signal_names.c create mode 100644 busybox/libbb/unarchive.c create mode 100644 busybox/libbb/unzip.c create mode 100644 busybox/libbb/vdprintf.c create mode 100644 busybox/libbb/verror_msg.c create mode 100644 busybox/libbb/vherror_msg.c create mode 100644 busybox/libbb/vperror_msg.c create mode 100644 busybox/libbb/wfopen.c create mode 100644 busybox/libbb/xfuncs.c create mode 100644 busybox/libbb/xgetcwd.c create mode 100644 busybox/libbb/xgethostbyname.c create mode 100644 busybox/libbb/xreadlink.c create mode 100644 busybox/libbb/xregcomp.c create mode 100644 busybox/ln.c create mode 100644 busybox/loadacm.c create mode 100644 busybox/loadfont.c create mode 100644 busybox/loadkmap.c create mode 100644 busybox/logger.c create mode 100644 busybox/logname.c create mode 100644 busybox/logread.c create mode 100644 busybox/ls.c create mode 100644 busybox/lsmod.c create mode 100644 busybox/makedevs.c create mode 100644 busybox/md5sum.c create mode 100644 busybox/miscutils/adjtimex.c create mode 100644 busybox/miscutils/dc.c create mode 100644 busybox/miscutils/dutmp.c create mode 100644 busybox/miscutils/makedevs.c create mode 100644 busybox/miscutils/mktemp.c create mode 100644 busybox/miscutils/mt.c create mode 100644 busybox/miscutils/readlink.c create mode 100644 busybox/miscutils/update.c create mode 100644 busybox/miscutils/watchdog.c create mode 100755 busybox/mk_loop_h.sh create mode 100644 busybox/mkdir.c create mode 100644 busybox/mkfifo.c create mode 100644 busybox/mkfs_minix.c create mode 100644 busybox/mknod.c create mode 100644 busybox/mkswap.c create mode 100644 busybox/mktemp.c create mode 100644 busybox/modprobe.c create mode 100644 busybox/modutils/insmod.c create mode 100644 busybox/modutils/lsmod.c create mode 100644 busybox/modutils/modprobe.c create mode 100644 busybox/modutils/rmmod.c create mode 100644 busybox/more.c create mode 100644 busybox/mount.c create mode 100644 busybox/msh.c create mode 100644 busybox/mt.c create mode 100644 busybox/mv.c create mode 100644 busybox/nc.c create mode 100644 busybox/networking/hostname.c create mode 100644 busybox/networking/ifconfig.c create mode 100644 busybox/networking/nc.c create mode 100644 busybox/networking/nslookup.c create mode 100644 busybox/networking/ping.c create mode 100644 busybox/networking/route.c create mode 100644 busybox/networking/telnet.c create mode 100644 busybox/networking/tftp.c create mode 100644 busybox/networking/traceroute.c create mode 100644 busybox/networking/wget.c create mode 100644 busybox/nfsmount.c create mode 100644 busybox/nfsmount.h create mode 100644 busybox/nslookup.c create mode 100644 busybox/pidof.c create mode 100644 busybox/ping.c create mode 100644 busybox/pivot_root.c create mode 100644 busybox/poweroff.c create mode 100644 busybox/printf.c create mode 100755 busybox/pristine_setup.sh create mode 100644 busybox/procps/free.c create mode 100644 busybox/procps/kill.c create mode 100644 busybox/procps/pidof.c create mode 100644 busybox/procps/ps.c create mode 100644 busybox/procps/renice.c create mode 100644 busybox/procps/uptime.c create mode 100644 busybox/ps.c create mode 100644 busybox/pwd.c create mode 100644 busybox/rdate.c create mode 100644 busybox/readlink.c create mode 100644 busybox/reboot.c create mode 100644 busybox/renice.c create mode 100644 busybox/reset.c create mode 100644 busybox/rm.c create mode 100644 busybox/rmdir.c create mode 100644 busybox/rmmod.c create mode 100644 busybox/route.c create mode 100644 busybox/rpm2cpio.c create mode 100755 busybox/scripts/depmod.pl create mode 100644 busybox/scripts/inittab create mode 100755 busybox/scripts/mk2knr.pl create mode 100644 busybox/scripts/undeb create mode 100644 busybox/scripts/unrpm create mode 100644 busybox/sed.c create mode 100644 busybox/setkeycodes.c create mode 100644 busybox/shell/ash.c create mode 100644 busybox/shell/cmdedit.c create mode 100644 busybox/shell/cmdedit.h create mode 100644 busybox/shell/hush.c create mode 100644 busybox/shell/lash.c create mode 100644 busybox/shell/msh.c create mode 100644 busybox/sleep.c create mode 100644 busybox/sort.c create mode 100644 busybox/stty.c create mode 100644 busybox/swaponoff.c create mode 100644 busybox/sync.c create mode 100644 busybox/sysklogd/klogd.c create mode 100644 busybox/sysklogd/logger.c create mode 100644 busybox/sysklogd/logread.c create mode 100644 busybox/sysklogd/syslogd.c create mode 100644 busybox/syslogd.c create mode 100644 busybox/tail.c create mode 100644 busybox/tar.c create mode 100644 busybox/tee.c create mode 100644 busybox/telnet.c create mode 100644 busybox/test.c create mode 100644 busybox/tests/.cvsignore create mode 100644 busybox/tests/Makefile create mode 100644 busybox/tests/cp_tests.mk create mode 100644 busybox/tests/ln_tests.mk create mode 100755 busybox/tests/multibuild.pl create mode 100755 busybox/tests/multifeat.pl create mode 100644 busybox/tests/mv_tests.mk create mode 100644 busybox/tests/sh.testcases create mode 100644 busybox/tests/syslog_test.c create mode 100644 busybox/tests/testcases create mode 100755 busybox/tests/tester.sh create mode 100644 busybox/tests/tst-syslogd.c create mode 100644 busybox/tftp.c create mode 100644 busybox/touch.c create mode 100644 busybox/tr.c create mode 100644 busybox/traceroute.c create mode 100644 busybox/true_false.c create mode 100644 busybox/tty.c create mode 100644 busybox/umount.c create mode 100644 busybox/uname.c create mode 100644 busybox/uniq.c create mode 100644 busybox/update.c create mode 100644 busybox/uptime.c create mode 100644 busybox/usage.c create mode 100644 busybox/usage.h create mode 100644 busybox/usleep.c create mode 100644 busybox/util-linux/dmesg.c create mode 100644 busybox/util-linux/fbset.c create mode 100644 busybox/util-linux/fdflush.c create mode 100644 busybox/util-linux/freeramdisk.c create mode 100644 busybox/util-linux/fsck_minix.c create mode 100644 busybox/util-linux/getopt.c create mode 100644 busybox/util-linux/mkfs_minix.c create mode 100644 busybox/util-linux/mkswap.c create mode 100644 busybox/util-linux/more.c create mode 100644 busybox/util-linux/mount.c create mode 100644 busybox/util-linux/nfsmount.c create mode 100644 busybox/util-linux/nfsmount.h create mode 100644 busybox/util-linux/pivot_root.c create mode 100644 busybox/util-linux/rdate.c create mode 100644 busybox/util-linux/swaponoff.c create mode 100644 busybox/util-linux/umount.c create mode 100644 busybox/uudecode.c create mode 100644 busybox/uuencode.c create mode 100644 busybox/vi.c create mode 100644 busybox/watchdog.c create mode 100644 busybox/wc.c create mode 100644 busybox/wget.c create mode 100644 busybox/which.c create mode 100644 busybox/whoami.c create mode 100644 busybox/xargs.c create mode 100644 busybox/yes.c diff --git a/busybox/.cvsignore b/busybox/.cvsignore new file mode 100644 index 000000000..71269c542 --- /dev/null +++ b/busybox/.cvsignore @@ -0,0 +1,4 @@ +busybox +busybox.links +_install +applet_source_list diff --git a/busybox/.indent.pro b/busybox/.indent.pro new file mode 100644 index 000000000..492ecf1c7 --- /dev/null +++ b/busybox/.indent.pro @@ -0,0 +1,33 @@ +--blank-lines-after-declarations +--blank-lines-after-procedures +--break-before-boolean-operator +--no-blank-lines-after-commas +--braces-on-if-line +--braces-on-struct-decl-line +--comment-indentation25 +--declaration-comment-column25 +--no-comment-delimiters-on-blank-lines +--cuddle-else +--continuation-indentation4 +--case-indentation0 +--else-endif-column33 +--space-after-cast +--line-comments-indentation0 +--declaration-indentation1 +--dont-format-first-column-comments +--dont-format-comments +--honour-newlines +--indent-level4 +/* changed from 0 to 4 */ +--parameter-indentation4 +--line-length78 /* changed from 75 */ +--continue-at-parentheses +--no-space-after-function-call-names +--dont-break-procedure-type +--dont-star-comments +--leave-optional-blank-lines +--dont-space-special-semicolon +--tab-size4 +/* additions by Mark */ +--case-brace-indentation0 +--leave-preprocessor-space diff --git a/busybox/AUTHORS b/busybox/AUTHORS new file mode 100644 index 000000000..4258e5aa5 --- /dev/null +++ b/busybox/AUTHORS @@ -0,0 +1,81 @@ +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 + +----------- + +Erik Andersen , + Tons of new stuff, major rewrite of most of the + core apps, tons of new apps as noted in header files. + +Edward Betts + expr, hostid, logname, tty, wc, whoami, yes + +John Beppu + du, head, nslookup, sort, tee, uniq + +Brian Candler + tiny-ls(ls) + +Randolph Chung + fbset, ping, hostname, and mkfifo + +Dave Cinege + more(v2), makedevs, dutmp, modularization, auto links file, + various fixes, Linux Router Project maintenance + +Larry Doolittle + pristine source directory compilation, lots of patches and fixes. + +Karl M. Hegbloom + cp_mv.c, the test suite, various fixes to utility.c, &c. + +Daniel Jacobowitz + mktemp.c + +Matt Kraai + documentation, bugfixes + +John Lombardo + dirname, tr + +Glenn McGrath + ar, dpkg, dpkg-deb + +Bruce Perens + Original author of BusyBox. His code is still in many apps. + +Kent Robotti + reset, tons and tons of bug reports and patchs. + +Chip Rosenthal , + wget - Contributed by permission of Covad Communications + +Pavel Roskin + Lots of bugs fixes and patches. + +Gyepi Sam + Remote logging feature for syslogd + +Linus Torvalds + mkswap, fsck.minix, mkfs.minix + +Mark Whitley + grep, sed, cut, xargs, style-guide, new-applet-HOWTO, bug fixes, etc. + +Charles P. Wright + gzip, mini-netcat(nc) + +Enrique Zanardi + tarcat (since removed), loadkmap, various fixes, Debian maintenance + +Vladimir Oleynik + cmdedit; ports: ash, stty, traceroute; locale, various fixes + and irreconcilable critic of everything not perfect. + +Tim Riker + bug fixes, member of fan club diff --git a/busybox/Changelog b/busybox/Changelog new file mode 100644 index 000000000..ee8bef926 --- /dev/null +++ b/busybox/Changelog @@ -0,0 +1,1177 @@ +0.60.0 + + Note: + + For this release I have bumped the version number to 0.60.0. 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 + stabablize and the new build system is working for everyone, then I + will release that as a new stable release series. + + Critical Bugfixes: + * Matt Kraai + -- Fixed wget output file opening (wget failed in 0.52). + -- Fixed a memory leak in syslogd (found by Adam Slattery). + * Vladimir Oleynik, Matt Kraai, Erik Andersen + -- several nasty bugs in ash and msh. msh could not assign + any variables and had debug code still enabled. ash + had several compile errors (depending on selected options) + and variable assignment problems as well. + + New Applets: + * David McCullough -- modprobe + * Vladimir Oleynik -- traceroute + * Erik Andersen -- pidof + + New Scripts: + * David Schleef, Erik Andersen, Stuart Hughes -- depmod.pl + This is a replacement for the depmod program from the modutils + package, but is fully cross platform and is designed to run on + your host system (not on the target). + + Other Changes: + * Erik Andersen + -- fixed busybox.spec so it should now work on redhat systems + -- fixed dos2unix and unix2dos so they should work once again + -- Adjustments to make busybox more uClinux friendly. Busybox + should now work on uClinux systems without needing and source + code changes (applets that won't work on uClinux systems are + now automagicaly disabled). + -- various things (cleanups, libc compatibility work, etc, etc) + * Jim Gleason + -- Fixed for sed, where it failed to preserve whether or not the + line was previously altered when running a subst command. + * Matt Kraai + -- Made tar read 20 512byte blocks at a time (like GNU tar) + -- Allow msh.c assignments with the export and readonly commands. + -- Added BB_FEATURE_DEVFS to enable devfs device names. + -- Better devfs support + -- Don't save/restore vi readonly flag if vi is compiled read-only. + -- Reworked rdate option handling (is now smaller). + -- Size reduction in ping + -- Always write dd counts to stderr + -- Allow multiple shells to be enabled + * Aaron Lehmann + -- slimmed down md5sum + -- contributed a nice new (hand written, not lex/yacc) Posix math + support for ash, which is once again a full posix shell. + * Felix von Leitner -- patches to make busybox + work with dietlibc. + * David McCullough + -- Adjustments to make busybox more uClinux friendly + * Glenn McGrath + -- Fixed gzip so when a filename is '-' it will use stdin/stdout + -- dpkg rewrite. Should now be compatable with the real dpkg, + but needs more testing. + -- Updates to archiving tools (gunzip/gzip/cpio/ar/etc) + -- Rewrote uuencode, will allow base64 encoding to be used by wget + * Vladimir Oleynik + -- Fixed tr to support 'tr a-z A-Z' syntax, + -- Many ash corrections, optimizations, and cleanups. + -- optimizations for traceroute, md5sum, chown, ping + -- cmdedit updates and API change + -- Namespace cleanup (i.e. adding 'static' private function calls) + -- added "stopped jobs" warning to ash on exit + * Adam Slattery + -- Fixed ping compile problem + * Robert J. Osborne + -- fixed a vi bug with delete and escape sequences on empty files. + + + -Erik Andersen, 31 July 2001 + + + +0.52 + + Critical Bugfixes: + * Glenn McGrath -- Fixed gunzip, zcat when reading from stdin + * Marc Karasek and Kanoj (kernel serial.c maintainer) -- fixed init + problem on serial consoles with 2.4.3+ kernels. + + New Applets: + * Laurence Anderson -- rpm2cpio applet, this obsoletes rpmunpack + which has now been removed from BusyBox + * Laurence Anderson and Glenn McGrath -- cpio applet, currently + only supports unpacking the ascii cpio format. + * Vladimir Oleynik and Erik Andersen -- added ash, the most correct + busybox shell. + * Larry Doolittle -- hush, small shell designed specifically + for busybox. Quite usable but still a work in progress. + * Erik Andersen -- msh, minix shell. A very small but capable shell + that only uses vfork, so it can be used on uClinux systems. + + Other Changes: + * Sterling Huxley -- Several bugfixes for the vi applet. + * Glenn McGrath -- Restructure unarchiving code to make more code + common to the ar, cpio, dpkg, dpkg-deb applets. + tar applet has not yet been assimilated... + * Matt Kraai -- Rewrote cp, dirname, mkdir, mv, and rm. + * Paul J.Y. Lahaie -- Fixed an endian-ness + bug in md5sum (in 0.51, md5sum on big endian machines was broken) + * Mark Whitley -- rewrote cut, major updates to grep and sed. + * Erik Andersen -- bunches of insmod fixes. It should now always + work (no more segfault or missing symbols problems). + * Bernhard Kuhn and Jinux Kim + added uClinux/m68k insmod support. + * Manuel Novoa III -- rewrote make_human_readable so ls, du, and df + should work as expected. Eliminated use of floats. + * Aaron Lehmann -- Scrubbed gzip.c + * Alan Modra -- fixed an hard to spot + bug breaking gunzip checksum checking. + * Gennady Feldman -- Fixed 'syslog -C' + * Gernot Poerner -- Added mount bind support. + * Adam Heath -- wget arbitrary header support + * John Beppu -- updated the automagical doc generator + * Zillions of other bugfixes, optimizations, and cleanups. + + + + -Erik Andersen, 7 July 2001 + + +0.51 + Critical Bugfixes: + * Erik Andersen -- Fixed a bug that could crash the shell in 0.50 + when pressing on an empty line. + * Gennady Feldman -- Fixed a bug that could crash the shell in 0.50 + when performing an 'export' in the shell. + * Gennady Feldman -- fixed a syslogd bug where syslogd could cause + the init process to block (which can break systems badly). + + New Applets: + * Sterling Huxley -- contributed a new vi applet! This is a very + functional vi implementation in Only 22k. + * Erik Andersen -- added env applet + + Other changes: + * Erik Andersen -- Split utility.c into libbb, which provides a + much cleaner was for us to include shared functionality. + * Erik Andersen -- Reorganized how and when busybox includes + syscalls, aiding portability and (in this case) making the + busybox work on ia64 systems. + * Erik Andersen -- dpkg.c cleanup to use the updated gunzip interface. + * Erik Andersen -- Cleanups for libc5, glibc, and uClibc. + * Erik Andersen and Matt Kraai -- Cleanups for the human-readable + output from ls, du, and df. + * Laurence Anderson -- Fixed wget HTTP 1.1 + support and added chunked encoding so it is now RFC compliant. + * John Beppu -- The busybox.pod documentation is now automagically + generated from the source code. This makes it _much_ simpler. + Now to update the docs, just update the usage message... + * Dirk Behme -- Adjusted MIPS insmod + support a bit for Mips RS3. + * Christophe Boyanique -- egrep invoked the "init" applet in 0.50! + * Larry Doolittle -- Added -Wshadow and fixed a number of shadowed + variables + * David Douthitt -- fixed 'find -print' + * Gennady Feldman -- fixes for the syslogd circular buffer code + * Jeff Garzik -- a number of structural cleanups, fixes for -Wshadow + bugs, and similar problems. + * Matt Kraai -- Added a new 'shutdown' action to busybox init. Now + you can specify arbitrary behavior for 'ctrlaltdel' so now + pressing CTL-ALT-DEL can do something else (or nothing). + * Andreas Neuhaus -- fix for merging + kernel command line environment variables into child environment + for init.c + * Glenn McGrath -- Fixed problems with dpkg and dpkg-deb applets + * Glenn McGrath -- Don't try to automount devfs + * Vladimir Oleynik -- optimizations for more.c + * Vladimir Oleynik -- Added locale support to the shell, and fixed + locale support in several other places + * Vladimir Oleynik -- moved struct applet from busybox.c to applets.c + * Vladimir Oleynik -- A size optimization for rdate + * Vladimir Oleynik -- Fixed printf applets's locale handling + * Vladimir Oleynik -- More cmdedit updates + * Vladimir Oleynik -- Fixed `du' applet so it continues running + after permission errors. + * Vladimir Oleynik -- Reduced stack usage in recursive_action() + * Pierre Peiffer -- made + find_pid_by_name() cope with swapped out processes. + * Jari Ruusu -- updates so that setting + D_FILE_OFFSET_BITS=64 now works as expected. + * Anthony Towns -- fixed a bug with + sed address range handling + * Dmitry Zakharov -- a number of updates + to wget: support for ftp downloads, basic HTTP basic auth, handling + of http redirects, when attempting to continue an aborted download + but server doesn't support restarts then reopen output file in + write mode, bugfix: when content-length not given, wget didn't + download anything, if -c is not specified, it no longer default to + restarting an aborted download. + + + -Erik Andersen, 10 April 2001 + + +0.50 + * Erik Andersen -- added ifconfig interface status reporting + * Erik Andersen -- Debian packaging updates + * Erik Andersen -- lash environment variable expansion rewritten, + with lots of help/fixes/testing from Larry Doolittle. + * Erik Andersen -- Fix use of busybox with dmalloc debugging lib + * Erik Andersen -- fixed ls behavior for broken or very narrow terminals + * Erik Andersen -- stub umount2 and pivot_root if they are not available + * Erik Andersen -- libc5 fixes + * Erik Andersen -- make init work with devfsd + * Erik Andersen -- fixed df for nfs and dos where blksize = 512 + * Erik Andersen -- Make sure libpwd.a is linked _last_ so it + overrides the system pwd/grp + * Christophe Boyanique -- added an optional egrep alias for grep. + * Christophe Boyanique -- added optional 'rm -i' support. + * Kenneth Chalmers and Erik Andersen -- fixed ln so it + behaves when given no arguments (prints usage) and when + given just one arg (tries to make a link in the cwd). + * Magnus Damm -- added a tftp applet + * Magnus Damm -- powerpc support for busybox insmod. + * David Douthitt -- fixed a build error in df.c when + BB_FEATURE_HUMAN_READABLE was disabled + * John Beppu -- wrote autodocifier.pl, which will be used to auto- + generate the documentation from the source code, making life + much simpler for all. + * Magnus Damm -- Fixed an 'inner scope var + masking outer scope var with same name' bug that prevented + the loopback device from being unmounted if mount() failed. + * Larry Doolittle -- rewrote ifconfig to make it smaller + * Larry Doolittle and Erik Andersen -- cleanups to pristine source + * Larry Doolittle -- many bugfixes resulting from regression testing + * Gennady Feldman -- split syslogd.c into syslogd and klogd + * Gennady Feldman -- make syslogd single threaded -- no more forking + * Jeff Garzik -- getopt-ified rmmod. + * Jeff Garzik -- glibc 2.2 warning cleanups + * Jeff Garzik -- namespace pollution cleanup (staticified variables). + * Erik Gustavsson -- allow env variables set on the + kernel command line to be inherited into init and its children. + * Erik Habbinga -- fixed an uninitialized substitution delimiter in sed. + * Chris Jaeger -- Makefile cleanup to make option setting less error-prone + * Chris Jaeger -- Carefully check NFS_MOUNT_VERSION + depending on what kernel is being used. + * Quinn Jensen -- MIPS support for busybox insmod. + * Evin Robertson -- new pivot_root applet + * Kent Robotti -- usage message cleanups + * Kent Robotti -- reworked dos2unix/unix2dos + * Evin Robertson and Manuel Novoa III -- reworked how usage messages + are stored to save several k of space. + * Matt Kraai -- Keep trying if an NFS mount fails + * Matt Kraai -- fixed insmod so it won't try to insmod directories. + * Matt Kraai -- added nc listening support + * Matt Kraai and David Douthitt -- reworked fine to support -type, + -perm, -mtime, and other improvements. + * Matt Kraai -- added find_applet_by_name and saved some memory thereby + * Matt Kraai -- added chomp to reduce redundant code elsewhere + * Matt Kraai -- Removed trailing \n chars from error_msg{,_and_die} messages. + * John Lombardo -- fixed OOM in insmod. + * Glenn McGrath -- bypass /proc in mount, now uses sysfs. + * Glenn McGrath -- several updates to dpkg and dpkg-deb. + * Manuel Novoa III -- several size optimizations: parse_mode, + process_escape_sequence, format, and get_kernel_revision. + * Manuel Novoa III -- rewrote ifconfig again to make it smaller still + * Manuel Novoa III -- added ifconfig -a, updated interface reporting + * Vladimir N. Oleynik -- Fixed a bug where init set PATH incorrectly + * Vladimir N. Oleynik -- cleanups to route, cmdedit, mkdir, + mkfs_minix, mkswap, chmod_chown_chgrp and utility.c + * Vladimir N. Oleynik -- many fixes to cmdedit. so tab completion + is now working and general editing is much improved, and to + improve complex prompt handling. + * Vladimir N. Oleynik -- added route status reporting. + * Vladimir N. Oleynik -- fixed wget to use xfopen + * Vladimir N. Oleynik -- new stty applet + * Vladimir N. Oleynik -- fixed find, it used to stop on perm errors. + * Vladimir N. Oleynik -- locale forced to posix for scripts + * Vladimir N. Oleynik -- saved 128 bytes by moving error checking + for several my_* functions into utility.c + * Bjorn Wesen -- new ifconfig and route applet (taken from + work done by Axis Communications). + * Mark Whitley -- Added a 'How to contribute to Busybox' doc + and updated the style guide. + * Mark Whitley -- implemented grep -A, -B, and -C + * Mark Whitley -- overhauled the test suite. + + + -Erik Andersen, 15 March 2001 + +0.49 + + * Matt Kraai -- new sort.c + * Matt Kraai -- new tail.c + * Glenn McGrath -- new 'dpkg-deb' applet + * Glenn McGrath -- new ar code + * spoon -- new watchdog applet + * Vladimir N. Oleynik -- fixed cmdedit.c so now + scrolling and tab completion in lash work properly. Also several + byte saving optimizations. + * Erik Andersen -- disabled many less commonly used applets by default + * Mark Whitley -- more thrashing about to get clean perror_msg usage + * Matt Kraai -- new command line munging + * Larry Doolittle -- keep some locales from messing up busybox.sh + * Matt Kraai -- cleaned up dd and tail with new parse_number routine + * Mark Whitley -- remove debugging messages from deallocvt + * Matt Kraai and Mark Whitley -- new document "How to Add a New Applet + to BusyBox" + * David Douthitt -- fixed "grep -qv" bug + * Larry Doolittle -- fixed insmod bug with old kernels + * Matt Kraai -- logger remixed to use getopt, selection of stdin made + util-linux compatible + * Erik Andersen -- many more internal symbols classified static to + avoid namespace pollution + * Matt Kraai -- nc listening support + * Erik Andersen -- made sed understand arbitrary regexp delimiters + * Matt Kraai et al. -- more tar improvements and bug fixes, now + handles regexp file exclusion + * Larry Doolittle -- new script (multibuild.pl) to automate build rule + checking + * Matt Kraai -- update/cleanup of the docs on how to use init + * Erik Andersen -- renamed all sh.c symbols per the style guide, + better if-then-else-fi handling + * Erik Andersen -- cleaner division of labor between cmdedit.c and sh.c + * Larry Doolittle -- shell data structure cleanup, fixed buglets + in read, exec, and piped builtins + * Erik Andersen -- md5sum was broken in 0.48. Now fixed (and doesn't + use getline, shrinking static compiles (since nothing else used it). + * ?? -- squashed memory leak in shell prompt handling + * Mark Whitley -- Updates to style guide + * Mark Whitley -- Big cleanup in utility.c: style guide compliance, + de-macro-ifying some variables and functions + * Erik Andersen -- ls now honors BB_FEATURE_AUTOWIDTH so it can find + the width and height of the console. + * Erik Andersen -- insmod now ignores -L and accepts the -o option. + * Erik Andersen -- updates so you can now select from the Makefile + whether or not to use the system's passwd and group functions. + Since most systems use GNU libc, this can save you from having to + install the /etc/nsswitch.conf configuration file and the required + libnss_* libraries. Adds 1.5k. You can now, also, disable this, + causing busybox to use the system's pwd.h and grp.h functions. + + + -Erik Andersen, 27 January 2001 + +0.48 + + * Glenn McGrath -- tar now supports uncompressing tar files, + define BB_FEATURE_TAR_GZIP to use the -z option. + * Matt Kraai -- fix all usage of TRUE and FALSE so all apps now + return EXIT_SUCCESS or EXIT_FAILURE to the system. + Now TRUE and FALSE are set to the C standard where TRUE=1. + * me -- Fixed uname problem causing the kernel version to be + mis-detected (causing problems with poweroff, init, + and other things). + * Alcove, Julien Gaulmin and + Nicolas Ferre -- insmod support on ARM + and StrongArm, and suport for lsmod on older 2.0.x kernels. + * Kent Robotti -- Renamed unrpm to original rpmunpack, so you can use + an included shell script called unrpm as a front end to it. There's + also a shell script called undeb included for debian packages. + * Matt Kraai -- fix an infinite loop with ls -aR + * Larry Doolittle -- Shaved off about 100 bytes and 200 bytes heap + from date.c. Also document the "-d" option in the usage message. + * Gennady Feldman -- fixed dd to use blocksize when reading/writing, + (it was reading the whole thing and then writing it out). Also + updated usage information (was missing conv=notrunc) and added + conv=sync feature. + * Larry Doolittle (in collaboration with Matt Kraai) -- allow for a + pristine source directory -- where all the .o files and such are + not placed into the source tree. Thanks Larry! + * Larry Doolittle -- use the applet definitions in applets.h + to autogenerate the applet function and usage prototypes. + * Sebastien Huet, Arne Bernin, and Kent Robotti -- Add in tar -X and + fixed a bug breaking tar --exclude. + * Jonas Holmberg -- echo option handling made GNU-echo compatible + * Aleksey Demidov -- date option handling made + GNU-date compatible + * me -- Progress meter (optional) in wget + * Doolittle/me -- programs invoked by full path name take + precedence over applets unless BB_FEATURE_SH_BUILTINS_ALWAYS_WIN + * Gaute B Strokkenes -- applets found using a + binary search instead of linear search. Much faster! + * new applets: cmp readlink + * Mark Whitley -- Removed advertising clause of Berkeley license + according to decision by the Regents of the University of + California; included reference + * tail's confusing special treatment of single digit options removed; + people should use -n instead + * Larry Doolittle -- \r handled now in echo and tr + * Matt Kraai -- rewrite of uniq + * Mark Whitley -- remix of xargs + * Jim Gleason -- fixed tar so it no longer breaks + hard links. + * Matt Kraai -- logger now logs all arguments, not just the first + * Gennady Feldman -- syslogd no longer logs to localhost if compiled + for remote logging... + * Richard June -- support for 'gzip -d' + * various artists -- Other good stuff that I forgot to document. + + + -Erik Andersen, 13 December 2000 + +0.47 + + * A bug in syslogd was fixed that allowed it to potentially fork-bomb + your system. Anyone using 0.46 syslogd should upgrade. + * Renamed busybox.defs.h to the more sensible "Config.h" + * Improved portability between different libcs. + * Many apps ported to use getopt() + * Common handling of '--help' + * All usage messages centralized. + * Added a bunch of new commands: + * 'rdate' contributed by Sterling Huxley + * 'wget' contributed by Chip Rosenthal , + and Covad Communications + * 'getopt' from "Alfred M. Szmidt" + * dos2unix, unix2dos, reset, and unrpm.c (and lots of help + debugging) thanks to Kent Robotti . + * 'renice' command, thanks to Dave Cinege + * 'xargs' (written by me) + * 'expr' contributed by Edward Betts , based + on GNY expr + * lsmod now uses the query_module syscall, rather then /proc (me) + * syslogd can now log messages to remote hosts -- patch thanks + to Gyepi Sam + * chroot can now call the builtin shell - Pavel Roskin + * 'make install' now creates relative symlinks, and added a new + 'make install-hardlinks' target to (tada) install hardlinks. + * Rewrite of 'tail' to make it simpler, smaller, and more robust. + It now weighs only 2.25k (3k when full featured). The code is + cleaner too, thanks to Allen Soard + * Add optional ls file sorting, thanks to a patch from + Sterling Huxley + * Fixed chmod option parsing so things like 'chmod -r /tmp/file' + now work (previously it thought -r was an option). Doh! + * Fixed tar handling of stdin and stdout + * Renamed "internal.h" to the more sensible "busybox.h" + * Preliminary support for GNU HURD. + * Updated my devps and devmtab kernel patches for the latest 2.2.x + kernel, for those wanting to go proc-less. + * Tons of other bugfixes. + + + -Erik Andersen, 25 September 2000 + + +0.46 + + * Better portability. Now should compile cleanly with libc5, + GNU libc 2.0 and 2.1, and various Linux kernels including + 2.0.x, 2.2.x, and to 2.4.0-test*. (patch for 2.4.x kernels + to make /proc/mounts behave included in the kernel-patches dir). + * Fixed a _horrible_ bug where 'tar -tvf' could unlink + local files that matched tarball contents!!! Fix thanks + to Marius Groeger + * Fixed a nasty bug in tar when could mess up saved symlinks. + * Fixed tar creation support when reading from stdin ('tar -cf - . ') + thanks to Daniel Quinlan + * Updates to handle Linux 2.4.0 kernels (kludged around the + "none" entries in /proc/mounts, added a hack to make sysinfo + work with both old and new kernels). + * Fixed insmod module option parsing for options lacking an '='. + Fix thanks to Marc Nijdam + * Fixed segfault with 'cut -f 1 -d:' and added 'cut -s' suport. + Fix thanks to Arne Bernin + * Several fixes from Marius Groeger + - Added support for "sh -c command args..." + - Fixed globbing, i.e. 'echo * *' and 'echo "******"' now work. + - Added shell environment variable substitution + - Added the "read" shell builtin. + * Fixed cursor editing in cmdedit.c. The following keyboard sequence + used to create an infinite loop: ls, cursor up, left, down. + * Added support for being a login shell, so things like + '-su' or '-sh' (stuff where argv[0][0]=='-') will now always + invoke the shell. Now you can use BusyBox as a login shell. + * ls.c now ignores '-g', since some ftp clients like that sort + of thing. Patch thanks to David Vrabel + * Fix to init.c from Stuart Menefy so that + it always sets the controlling terminal before running any programs + * Several fixes from Matt Kraai + - Fixed tr so it recognizes standard escape sequences. + Merged common escape seq. code from tr and echo into utility.c. + - Major work in updating/cleaning up the docs, and getting the + new SGML based docs into shape. + - cleanup of ar.c + - BusyBox should now poweroff when asked to do so. + - Fixed 'ln -n' and 'ln -s' so they both work properly. + * Reorganized signal names in kill.c for better architecture support + -- patch thanks to simon wood + * In 0.43, backspace and delete worked properly, but with 0.45, + it just echoed a ^? for backspace, and ^H for control-h. This + was due to a broken macro in init.c, that is now fixed. + * Removed sfdisk from BusyBox. It was buggy, fat, and we really + couldn't maintain it very well, so including it was not really + very appropriate. Those wanting an fdisk are invited to + grab a copy from util-linux. + * Added 'dumpkmap' to allow people to dump a binary keymap, which can + then be loaded in by 'loadkmap' -- submitted by + Arne Bernin + * Fixed NFS so it supports 2.4.x kernels and NFSv3. + * Brand, new versions of grep and sed which use libc regex routines, + thanks to Mark Whitley . The hand-tooled + "regexp.[ch]" files have been removed. Much help on these from + Matt Kraai as well. + + + -Erik Andersen, 11 July 2000 + + +0.45 + * Now compiles vs libc5 (which can save lots of space for + embedded systems). + * Added BB_FEATURE_TRIVIAL_HELP which compiles out most all of the + help messages (i.e --help). Saves 17k over a full compile. + * Added cut and tr from minix, since due to the license change, + we can now use minix code. Minix tr saves 4k. + * insmod now works. It costs 29k, but imagine an initrd with a + staticly linked busybox containing only insmod and sh, a few /dev + entries, and a kernel module or two... It doesn't get smaller + then this folks (I pity the fool that writes insmod in asm ;-). + Many kudos go to Ron Alder for finishing this off! + * Added a mini ar archive utility, especially written for BusyBox by + Glenn McGrath + * Added mktemp, contributed by Daniel Jacobowitz + * Added setkeycodes, for those that have wierd keyboard buttons. + * Added md5sum, uuencode and uudecode -- thanks to Alfred M. Szmidt + for contributing these. + * Added 'grep -v' option (inverted search) and updated + docs accordingly. -beppu + * Wrote which + * Replaced the telnet implementation with one written by + Tomi Ollila It works great and costs 3k. + * BusyBox sh (lash) now supports being used as a standalone shell. When + BB_FEATURE_SH_STANDALONE_SHELL is defined, all the busybox commands may + be invoked as shell internals. Best used when compiling staticly + (i.e. DOSTATIC=true) + * BusyBox sh (lash) internals now behave as expected wrt pipes + and redirects. + * Fixed ping warnings -- fix from Sascha Ziemann + * Fixed update segfault + * Fixed mknod -- minor number was always 0 + * Fixed tar option parsing, so both "tar xvf foo.tar" and + "tar -xvf foo.tar" now work (i.e. no "-" before options) + (this was very broken in 0.43). + * Several contributions from Randolph Chung . + * cp/mv now accepts the -f flag + * tail can now accept - commands (e.g. -10) for better + compatibility with the standard tail command + * added a simple id implementation; doesn't support sup. groups yet + * logname used getlogin(3) which uses utmp. Now it doesn't. + * whoami used getpwuid(3) which uses libc NSS. Now it behaves. + * Add support for "noatime" and "nodiratime" mount flags to mount. + * Changed 'umount -f' to mean force, and actually use umount2. + * Changed 'umount -l' to mean "Do not free loop device". + * Fixed basename to support stripping of suffixes. Patch thanks + to xiong jianxin + * cp -fa now works as expected for symlinks (it didn't before) + * zcat now works (wasn't working since option parsing was broken) + * Renamed "mnc" to the more correct "nc" (for netcat). + * Makefile intelligence updates + * Changed the way init parses /etc/inittab entries to avoid problems + with commands that contain colons in them. Fix thanks to + Pavel Roskin + * Fixed a warning in utility.c due to char being unsigned on Linux/PPC, + Fix thanks to Pavel Roskin + * Made "killall" complain (not error and exit) about processes that it + cannot find by name -- Pavel Roskin + * Fixed more and ps to have sensible terminal width defaults, thanks + to Pavel Roskin. + * Fixed all fatalError() calls lacking a "\n", thanks to Pavel Roskin. + * Fixed a segfault in yes when no args were given -- Pavel Roskin. + * Simplified freeramdisk and added argument checking -- Pavel Roskin. + * Fixed segfault caused by "touch -c" + * Fixed segfault caused by "rm -f" + * Fixed segfault caused by "ln -s -s" and similar abuses. Further fixes + and "--" support from Pavel Roskin. + * Fixed segfault caused by "cp -a -a" and similar abuses. + * Implemented "rm -- ". Implementation fixed by Pavel Roskin. + * "which" rewritten to use stat(). Fixes to improve its compatability + with traditional implementations -- Pavel Roskin. + * "mount" now reports errors from nfsmount() and assumes NFS mount + if ':' is present in the device name - Pavel Roskin + * Fixed exit status for killall - Pavel Roskin + * Fixed 'swapon -a' and 'swapoff -a', which were broken. + * Fixed 'mount -a' so it works as expected. + * Implemented 'ls -R' (enabled by enabling BB_FEATURE_LS_RECURSIVE) + * Implemented "ping -s", fixed error messages and argument parsing - + Pavel Roskin + * Syslogd will not go to background if "-n" is given. Better help + and argument checking -- Pavel Roskin + * Fixed a small bug that could cause tar to emit warning messages + and not extract the first file in a directory in some cases + of nested directories. Thanks to Kevin Traas + for helping track this one down. + * More doc updates + * Fixed grep "Line too long" problem -- John Beppu + * Fixed 'grep -q -i B some_file' so it works + * math takes input from stdin if no args are given. -- John Beppu + * math was renamed to dc. Although it deviates from dc's behaviour, + this will probably be remedied in the future. -- John Beppu + + + -Erik Andersen, June 21, 2000 + + +0.44 + Previously, an erronous announcement of BusyBox 0.44 was made, so to + avoid possible confusion, we are skipping straight to 0.45, and calling + it good. + + -Erik Andersen + + +0.43 + * Major update to the provided documentation. + * Busybox now includes a shell! It currently costs 7.5 k (plus an + additional 2.5 k if you compile in command line editing). Handles + job control, has the usual set of builtins, and does everything + except for handling programming statements (if, while, etc...) + * Busybox can now work perfectly when /proc is disabled, thereby + saving a bunch of memory (kernel /proc support is not thin). This + is done by making use of some nice kernel patches I wrote up to + support the features that busybox requires and that /proc usually + provides. To enable this, turn on BB_FEATURE_USE_DEVPS_PATCH and + patch your kernel with the devps patch in the kernel-patches/ + directory. + * Wrote basename, dirname, killall, and uptime. + * tar has been completely rewritten by me. Both tar creation and + extraction are now well behaved. Costs 7.6k with all optional + tar features enabled, and 5k for just tar extraction support. + * Added freeramdisk, which will free up all memory associated + with a ram disk. Contributed by Emanuele Caratti + and then adjusted a bit by me. + * Added tr from John Lombardo + * Added echo and test (from me). + * Added usleep contributed by Nicolas Pitre + * BusyBox's bss size has been majorly reduced (was 384668, is now 28740). + * Several fixes from Pavel Roskin : + - When `tail' fails to open a file it now exits. + - When `syslogd' is given the `-n' option it should still use + fork() for running klogd. + * nslookup types are now changed to u_int32_t (instead of uint32_t) + changed per a patch from Pascal Bellard + * Fixed "du" so it gives the same answers as GNU "du" (busybox du used + to count hard-linked files more then once). Many thanks to + Friedrich Vedder for the fix. + * Removed /proc dependancies for init and free (while maintaining + exactly the same functionality). /proc takes up 90k of kernel + space, so it is nice to avoid using it at all costs. + * init no longer tries to mount /proc (unless there is less the 1 meg + free). Use of /proc (or not) is policy that should be set up in + /etc/fstab (or in hardcoded scripts), not in init. + * Fixed rebooting when init runs as an initrd. + * Fixes and updates from Karl M. Hegbloom + - update.c rewritten to look more like update-2.11 + - moveed the inode hash out of du.c and into utility.c to make + it a common resource that can be used by other apps. + - cp_mv.c now checks inodes to see if a source and dest are + the same, and prints an error (instead of endlessly looping). + - mv now attempts to do a rename, and will fall back to doing + a copy only if the rename fails. + - Syslogd now supports multiple concurrent connections + * Several fixes from Pavel Roskin : + - Fixes to sort. Removed "-g", fixed and added "-r" + - Fixes to the makefile for handling "strip" + * An initial telnet implementation was added by + Randolph Chung . + * Fixed a bug where "sed 's/foo/bar/g'" (i.e. a script w/o a "-e") + * ps now supports BB_FEATURE_AUTOWIDTH, and can adjust its width + to match the terminal (defaults to width=79 when this is off). + * ps now accepts (and ignores) all options except for "--help" (which + as would be expected displays help). + * Fixed mount'ing loop devices when the filesystem type was not + specified. It used to revert to non-loop after the first try. + * all mallocs now use xmalloc (and so are OOM error safe), and + the common error handling saves a few bytes. Thanks to + Bob Tinsley for the patch. + * Fix "+" parsing bug in date, from "Merle F. McClelland" . + * Fix symlink following bug in chmod -R and friends. + * Now allows SYSV style 'chown foo:bar' in addition to 'chown foo.bar' + * Fixed a bug in the busybox globbing routine such that 'find /dir -name [i]' + no longer segfaults. + + + -Erik Andersen + + +0.42 + + * Fairly massive restructuring of umount.c to deal with remounting + busy devices read-only. Adds a -r option to control that; it is + optionally compiled in with BB_FEATURE_REMOUNT + * Added a bunch of functions to mtab.c to interact with the + {get,set,end}mntent interface; as it turns out, those functions do + not appear to be re-entrant, and that causes a lot of problems with + the way umount was originally written. + * Makes init send TERM and KILL (instead of HUP and KILL) on reboot + to be more consistent with sysvinit + * Changes to init.c to use the new -r option to umount. Also increased + the sleep time between the time the TERM and KILL signals are sent + + - Randolph Chung + + + * cp.c, mv.c: removed, replaced by cp_mv.c which has been + extensively rewritten from the original cp.c. + * Fixed cp and mv so if the source and destination are a the + same directory it will print an error and continue. + * Also added a warning message to the `mv' usage string saying that + this is not GNU mv, and it will break hard links. cp also breaks + hard links. + * ln.c: implemented `-n' switch, no-deref symlinks. + * include: and use PATH_MAX everywhere. busybox: File + * name buffer overrun guards to prevent future crashes. + - Always check exit status. + - Purge all use of `creat()', replace with `open()'. + * utility.c + - recursiveAction was overriding the value of followLinks thus + ignoring it. + - isDirectory now takes a followLinks boolean, updated all callers + - copyFile had the followLinks logic reversed. + * messages.c: New file. Put common error message strings all in + one place in an attempt to shrink the binary a little. + + -Karl M. Hegbloom + + + * changed fsck_minix.c to reduce its .bss size significantly + -beppu -piptigger + * Made tar creation support in busybox tar optional. You no longer + * _have_ to put a "-" in front of tar options. Tar could inadvertently + * change permissions and ownership on + certain directories pointed to by symlinks. + * Made grep and grep -h do the right thing wrt printing + the file name (it failed to print files names in many cases). + * Fix a namespace aliasing problem wereby if du was built in, the + symlink for both du and dutmp would be installed, or then rm was + built in, the symlinks for both rm and rmmod would be installed. + * Added a closelog() to init.c after loging -- fix thanks to + Taketoshi Sano + * Rewrote and simplified logger. Added the "-t" option, and made it + behave itself a bit better. + * Optional support contributed by Ben Collins + for the kernel init chroot patch by Werner Almesberger, which + allows init to chroot to a new device, and umount the old one. + * Fixed bug that wouldn't let one chown a symlink -- it would + always dereference before. -beppu + * Fixed a bug where init could have reference already freed memory. + Found and fixed by Taketoshi Sano + * Several contributions from Friedrich Vedder + * Added (and documented) "-n" option for head + * Cleanup for a number of usage messages -- also + contributed Friedrich Vedder + * Cosmetic fix to busybox.c (Don't print a comma at the + end of line if there are no more application names). + * Fixed a stupid bug in "head" option handling ("head -n" + would segfault). + * Moved commonly used functions "xmalloc()" and "exit()" + to utility.c (with proper #ifdef's). + * Created a tiny tail implementation, removing -c, -q, -v, and making + tail -f work only with a single file. This reduced tail from 6k to + 2.4k. The bigger/more featured tail can still be had by disabling + BB_FEATURE_SIMPLE_TAIL in busybox.defs.h + * Ping now falls back to doing the right thing if /etc/protocols + turns up missing. + * Fixed mount and umount. Previously they could leak loop device + allocations, causing the system to quickly run out. Fix for umount + by Ben Collins , and mount was fixed by me. + * ls formatting on eight charactor user names fixed by + Randolph Chung . + * cp could, when copying symlinks, change permissions of the + files pointed to by the symlinks. + * Several fixes from Pavel Roskin : + - `chown' with 1 argument displayed the error incorrectly + - `fdflush', `length' and `printf' crashed if run without arguments + - `fdflush' tried to flush itself using *argv + - added "skip" and "seek" to dd. + - ls no longer messus up output when combining files and + directories on the command line + * swapoff -a was not working. Now it is. + * init did not cleanly unmount filesystems on reboot. Now it does. + * "sed -ne s/foo/bar/" worked but "sed -n -e s/foo/bar/" didn't. + Now both work. + * Some architectures (PowerPc) assume chars are unsigned, so they could + not distinguish between EOF and '\0xFF' in sed. Sed now uses ints. + * Began converting error handling to use some common routines + in utility.c + * syslogd now has better message handling and ignores SIGHUP. + * install.sh had a bug preventing installation to the specified + target directory. Fix from Gilbert Coville + * You can now spefify alternative strip commands -- change + also from Gilbert Coville. + + + -Erik Andersen + +0.41 + * New Apps: wc, hostid, logname, tty, whoami, yes -- all contributed + by Edward Betts + * Fixed a bug in both cp and mv preventing 'cp foo/README bar' + type commands (file in a directory to another directory) from + working. + * Fixed a logger bug that caused garbage to be written to the syslog + (unless you used busybox syslog, which hid the bug). Thanks to + Alex Holden for the fix. + * /bin/true and /bin/false were echoing a blank line when run. + Now fixed. + * mkdir -p would print an error when asked to mkdir an existing dir + with no interveining subdirectories. + * Fixed "syslogd -O" so that it works. Added -o loop option for mount, + * and support in umount for loop + devices. Support is toggled by MOUNT_LOOP feature -- Ben Collins + + * Several fixes from Marco Pantaleoni compile in + * fullWrite() not only if BB_TAR is defined, but also + if BB_CP or BB_MV are (fullWrite() is referenced by copyFile()) + * add some compiler optimizations to further reduce executable size + (as a side note, on my machines the largest code is generated + by gcc 2.95.2 with -Os ! The smallest by plain gcc 2.7.2.3 with + -O2 -m386 ...) + * Compile now won't fail if busybox.def.h defines + BB_FEATURE_LINUXRC but not BB_INIT. (init_main used to be + referenced, but not compiled) + * Fixed a bug in setting TERM for serial console support. TERM now + defaults to "ansi" for serial consoles. + * Fixed a bug in handling the CONSOLE env. variable for serial + * consoles. + + -Erik Andersen, Jan 15, 2000 + +0.40 + * New Apps: sort, uniq. -beppu New Apps: lsmod, rmmod -erik New Apps: + * fbset contributed by Randolph Chung . New App:: + * loadacm contributed by Peter Novodvorsky + for loading application character maps for Unicode fonts. + * Major init re-work. init now supports inittab (slightly different + but similar to sysvinit), allowing me to get all the policy out of + init and into the conf file. It works just fine without inittab + being present, but if you dont like the default behavior you can + now do something about it. Init is much cleaner as a result. + * Fixed an bug in syslogd causing it to stop after 20 minutes. -erik + * Fixed an embarrasing segfault in head -beppu Fixed the embarrasing + * failure of 'logger -p'. -erik Added the -s option to du -beppu + * Re-worked the source tree a bit so it will compile under glibc 2.0.7 + with the 2.0.x Linux kernel. + * Added 'grep -q' thanks to a patch from "Konstantin Boldyshev" + . + * Grep -i previously failed on UPPER CASE patterns due to a silly + regexp implementation bug that is now fixed. + * Fixed a bug where tar would set, and then clear SGID and SUID bits. + * Fixed a bug where tar would not set the user and group on device + special files. + * Fixed a bug where tar would not restore the time to files. Fixed a + * major security problem with tar -- it changed ownership + of any file pointed to by a symlink to 777 (like say libc....) + Ouch!!! + * cp and mv were very broken when moving directories. I have rewritten + them so they should now work as expected. + * sed now supports addresses (numeric or regexp, with negation) and + has an append command, thanks to Marco Pantaleoni + * Fixed dmesg. It wasn't parsing its options (-n or -s) properly. + * Some cosmetic fixes to ls output formatting to make it behave more + like GNU ls. + * Fixed a stupid segfault in kill. Several fixes from Friedrich Vedder + * : + - Added gunzip -t, removed gunzip.c dead code, + - fixed several typos + - Glibc 2.0.7 and libc5 compile fixes + * Fixed a bug where 'mknod --help' would segfault. + + + -Erik Andersen, Jan 07, 2000 + +0.39 + * New Apps: ping, hostname, and mkfifo contributed by Randolph Chung + . 3 items off the TODO list! + * I wrote free (just calls "cat /proc/meminfo"). Added tail, based on + * tail from GNU textutils-1.19, but adjusted + to suit my evil purposes. Costs 6k. I'll make it smaller + sometime. + * on reboot, init called 'umount -a -n', which caused errors + when BB_MTAB was not enabled. Changed to 'umount -a', which does + the right thing. + * init will now try to run /sbin/getty if it is present (for easy + integration with the about-to-be-released tinylogin.) + * kill now behaves itself properly, added 'kill -l' to list signals 'ls + * -l' was failing on long directories, since my_getid was leaking + one file descriptor per file. Oops. + * Fixed rebooting from init. I'd accidently left some debugging code + * in + which blocked reboots. + * Fixed reboot, halt (and added poweroff) such that they handle it when + init is not at PID 1 (like when running in an initrd). + * Added a prelinary du implementation. Some parameter parsing + stuff still needs to be added. -beppu (John Beppu + ) + * Implemented tee. -beppu Implemented head. -beppu + + -Erik Andersen, Dec 10, 1999 + +0.38 + * Fixed a segfault in 'umount -a' when a badly formed /etc/fstab + file existed. + * df will not exit on error, but will stat all mounted filesystems. + * Fixed tar so uid/gid/permissions on extracted tarballs will be + correct. + * Fixed find -name so it properly uses shell wildcard patterns + (i.e. `*', `?', and `[]') instead of regular expressions, which + was causing some confusing and unexpected behavior. + * Added klogd to syslogd, so now the log will contain both system and + kernel messages. + * syslogd now creates the /dev/log socket to make sure it is there, and + is actually a socket with the right permissions. + * I've taken a first step to making busybox not need the /proc + filesystem. Most apps don't need it. Those that _require_ it, + will complain if you enable them when you disable + BB_FEATURE_USE_PROCFS. + + -Erik Andersen, Dec 5, 1999 + +0.37 + * Wrote a micro syslogd, and a logger util (to log things to the syslog + from the command line or scripts) With both compiled in, costs 4k. + * Fixed 'make install' so symlinks are installed in their proper + * locations. Changed the build system slightly so that features can + * now be enabled + or disabled from the busybox.defs.h header file, without trying to + compile in a source file named after that featue (unless that file + exists). + * Several options are now moved into busybox.defs.h Now 'rm -R' and 'rm + * -r' both work. dd now properly handles input beyond 1 block from + * stdin. Fixed a bug where tar unpacked everything a directories. + * Moved some code + from createPath into mkdir where it belonged, thereby making tar + work properly. + * Fixed an off-by-one bug in cat. Given a list of file it wouldn't cat + * out the + last file in the list. + * Fixed 'ls -ln' so numeric group/uid are presented properly, and fixed + * 'ls -l' + so when uid/gid is not in /etc/{passwd,group} the numeric group/uid + are presented properly. + * Also added a TODO. + + + -Erik Andersen, Nov 25, 1999 + +0.36 + * fixed dd so it properly defaults to stdin and stdout when no + if= and of= are set (fix thanks to Eric Delaunay). + * Don't try to close the file descriptor of a pipein tar. (fix also + * from + Eric Delaunay). + * Made createPath be quiet (again thanks to Eric Delaunay). If + * BB_CONSOLE_CMD_IF_RC_SCRIPT_EXITS is defined, then whatever + command you define it as will be run if the init script exits. + * Updated install.sh to make it more robust (thanks to Adam Di Carlo) + * NFS support added to mount by Eric Delaunay. It costs 10k when + * compiled + in, but that is still a big win for those that use NFS. + * Made 'rm -f' be silent for non-existant files (thanks to Eric + * Delaunay). changed zcat.c to gunzip.c. It now obeys the principle + * of least surprise + and acts as god intended gunzip and zcat to act. They answer + --help and obey the '-c' flag. + * Fixed a bug in mv which caused it to not move files when the + * destination + was a directory. + * Fixed a decimal-instead-of-octal bug causing mkdir to make + * directories + with very wrong permissions. + * chmod would overwrite file permissions instead of modifying them. + Now it properly modifies permissions. + * Init now sends warnings destined for the console to /dev/console to + * ensure + they show up on whatever the active console it. Otherwise + important messages (for example that the system is rebooting) were + not seen when switched to a different VT. + + -Erik Andersen, Nov 17, 1999 + +0.35 + * gzip now obeys the principle of least surprise and acts like god + * intended + (i.e. it accepts a file name, answers --help, and obeys the '-c' + flag and only then outputs to stdout). + * Fixed more.c to compile autowidth on sparc and set initial winsize + to 0,0 in case the TIOCGWINSZ ioctl fails. Fix thanks to Eric + Delaunay. + * Fixed tar so it now works as expected (it had TRUE/FALSE backwards) + * tar now accepts --help chmod, chown, and chgrp usage now works + * General usage (i.e. --help) cleanups for most apps umount now parses + * options correctly tar can now unpack tarballs containing device + * special files, + sockets, and fifos (though it can't pack them up) thanks to Matt + Porter. Creating archives containing these is still left to the + interested student. + * fixed up the license in more.c to properly point to Bruce Perens. + + -Erik Andersen, Nov 11, 1999 + +0.34 + * ls -l now displays link names outside the current directory, + Patch thanks to Eric Delaunay + * init now properly handles sparc serial consoles and does a + better job of finding the real console device rather than using + /dev/console which doesn't support job control. Patch also thanks + to Eric Delaunay. + * more started to read from stdin after the last file was finished, and + options were not parsed correctly (fix thanks to Eric Delaunay). + * more will now use the terminal size if BB_FEATURE_AUTOWIDTH is on. + * rm wouldn't remove a symlink unless the symlink was valid. This was + a side effect of the busybox 0.32 recursiveAction() fix. Things + should now work correctly. + * grep wouldn't grep stdin. Now it does. sed wouldn't sed stdin. Now + * it does. sed was appending a \n to the end of lines with + * replacements. + Now it doesn't do that. + * ls -l now bypasses libc6 nss when displaying user/group names. + Now uses my_getpwuid and my_getgrgid. + + -Erik Andersen, Nov 8, 1999 + +0.33 + * Fixed a bug where init could hang instead of rebooting. + * Removed some debugging noise from init.c + * Fixed ln so it works now (it was very broken). + * Fixed df so it won't segfault when there is no /etc/fstab, + * If BB_MTAB is not defined, df and mount will whine if /etc/fstab + is not installed (since they cannot fixup "/dev/root" to + state the real root device name) + * merged some redundant code from mtab.c/df.c into utility.c + + -Erik Andersen, Nov 5, 1999 + +0.32 + * More changes -- many thanks to Lineo for paying me to work on + busybox. If you have any problems please let me know ASAP at + andersen@lineo.com or andersee@debian.org + * usage() now prints the BusyBox version. This will help folks + realize that they are not in Kansas anymore. + * Fixed mkdir -m option so that it works. kill segfaulted w/o any + * arguments. Now it doesn't do that. kill wasn't properly accepting + * signal names. It does now. Added new apps chvt and deallocvt (I + * should probably add open) Major rewrite of init.c. Code is now + * readable by mere mortals IMHO. Wrote sed -- weighs only 1.8k (5.8k + * with full regular expressions!). Fixed a stupid seg-fault in sync + * Fixed mount -- mount -a failed to parse and apply mount options Fixed + * umount -n (patch thanks to Matthew Grant ) + * umount -a no longer umounts /proc Added BB_MTAB, allowing (at the + * cost of ~1.5k and the need for a rw /etc) + folks to use a real /etc/mtab file instead of a symlink to + /proc/mounts. mount, and umount will add/remove entries and df + will now use /etc/mtab if BB_MTAB is defined. + * Fixed a nice bug in recursiveAction() which caused it to infinitely + hunt through /proc/../fd/* creating new file descriptors if it + followed the /dev/fd link over to /proc. recursiveAction() now + lstat's the file when followLinks==FALSE so it won't follow links + as the name suggests. Fix thanks to Matt Porter + . + + + -Erik Andersen, Nov 4, 1999 + +0.31 + * I added a changelog for version 0.30. adjusted find internals to + * make it smaller, and removed + some redundancy. + * Fixed a segfault in ps when /etc/passwd or /etc/group + are absent. Now will warn you and carry on. + * Added in optional _real_ regular expression support (to be + the basis for a future sed utility). When compiled in it adds + 3.9k, but makes grep much more capable. + * Checked out using nftw(3) for recursive stuff, but unfortunatly + it wasn't supported before GNU libc 2.1, and some folks use glibc + 2.0.7 since it is much smaller than that latest and greatest. + + -Erik Andersen, Oct 21, 1999 + +0.30 + Major changes -- lots of stuff rewritten. Many thanks to Lineo for + paying me to make these updates. If you have any problems with busybox, + or notice any bugs -- please let me know so I can fix it. These + changes include: + + Core Changes: + * busybox can now invoke apps in two ways: via symlinks to the + busybox binary, and as 'busybox [function] [arguments]...' + * When invoked as busybox, the list of currently compiled in + functions is printed out (no this is not bloat -- the list has + to be there anyway to map invocation name to function). + * busybox no longer parses command lines for apps or displays their + usage info. Each app gets to handle (or not handle) this for + itself. + * Eliminated monadic, dyadic, descend, block_device, and + postprocess. It was cumbersome to have so many programs + cobbled together in this way. Without them, the app is much + more granular. + * All shared code now lives in utility.c, and is properly + ifdef'ed to be only included for those apps requiring it. + * Eliminated struct FileInfo (the basis of monadic, dyadic, etc) + so now each app has the function prototype of (da-dum): extern + int foo_main(int argc, char** argv); which speeds integration + of new apps. + * Adjusted the Makefile to make it easier to + {en|dis}able debugging. + * Changed default compiler optimization to -Os + (optimize for smaller binaries). + + App Changes: + * To cope with the new app function prototype and the removal of + monadic, dyadic, etc, the following apps were re-written: + * cat - Works same as always. chgrp, chmod, chown - + * rewrite. Combined into a single + source file. Absorbed patches from Enrique Zanardi + that removes the dependency on + libc6 libnss* libraries. + * cp - Can now do 'cp -a' can can copy devices, + pipes, symlinks, as well as recursive or non-recursive + dir copies. + * fdflush - adjusted to remove dependancy on struct + * FileInfo. find - Now includes some basic regexp matching + which will be the basic of a future mini-sed. + * ln - Same functionality. mkdir - Added -p flag to + * feature set. mv - rewrite. rm - Added -f flag to + * feature set. rmdir - Same functionality. swapon, + * swapoff - Combined into a single binary. No longer + uses /etc/swaps. swap{on|off} -a uses /etc/fstab + instead. + * touch - Same functionality. date - adjusted with a patch + * from Matthew Grant + to accomodate glibc timezone support. I then ripped out GNU + getopt. + * mkswap -- new version merged from util-linux. Can now make + >128Meg swaps. + * Replaced the old and star, unstar, and tarcat with the tar + implementation from sash. Now tar behaves as god intended it + to (i.e. tar -xvf and tar -cf work). + * dd -- rewritten. Can with with files, stdin, stdout. Added the + * following new apps: loadfont -- added from debian boot floppies + * chroot -- added based on a patch from Paolo Molaro + * grep -- I just wrote it. Only matches + * simple strings ps -- I just wrote it. Has _no_ options at all, + * but works. fsck_minix, mkfs_minix -- added from util-linux, but + * I ripped out + internationalization and such to make them smaller. + * sfdisk -- Added from util-linux (minus + * internationalization and such). Probably some other + * changes that I forgot to document... + + -Erik Andersen, Oct 20, 1999 + +0.29 + This version was a messy pre-alpha. stay away or it will bite you. + -Erik Andersen, Sep 24, 1999 + +0.28 + mini-netcat (mnc) rewritten. + +0.27 + Mount now supports -a, and -t auto. + Mount now updates mtab correctly for 'ro'. + More checks screen rows size, outputs bytes percentage. + Printf added as module. +0.26 + Touch now creates files. -c option for no create. + diff --git a/busybox/Config.h b/busybox/Config.h new file mode 100644 index 000000000..b0b57b04e --- /dev/null +++ b/busybox/Config.h @@ -0,0 +1,476 @@ +/* vi: set sw=4 ts=4: */ +// This file defines the feature set to be compiled into busybox. +// When you turn things off here, they won't be compiled in at all. +// +//// This file is parsed by sed. You MUST use single line comments. +// i.e., //#define BB_BLAH +// +// +// BusyBox Applications +//#define BB_ADJTIMEX +//#define BB_AR +//#define BB_ASH +#define BB_BASENAME +#define BB_CAT +#define BB_CHGRP +#define BB_CHMOD +#define BB_CHOWN +#define BB_CHROOT +#define BB_CHVT +#define BB_CLEAR +//#define BB_CMP +#define BB_CP +//#define BB_CPIO +#define BB_CUT +#define BB_DATE +//#define BB_DC +#define BB_DD +//#define BB_DEALLOCVT +#define BB_DF +#define BB_DIRNAME +#define BB_DMESG +//#define BB_DOS2UNIX +//#define BB_DPKG +//#define BB_DPKG_DEB +//#define BB_DUTMP +#define BB_DU +//#define BB_DUMPKMAP +#define BB_ECHO +#define BB_ENV +//#define BB_EXPR +//#define BB_FBSET +//#define BB_FDFLUSH +#define BB_FIND +#define BB_FREE +//#define BB_FREERAMDISK +//#define BB_FSCK_MINIX +//#define BB_GETOPT +#define BB_GREP +#define BB_GUNZIP +#define BB_GZIP +#define BB_HALT +#define BB_HEAD +//#define BB_HOSTID +//#define BB_HOSTNAME +//#define BB_HUSH +#define BB_ID +//#define BB_IFCONFIG +#define BB_INIT +//#define BB_INSMOD +#define BB_KILL +#define BB_KILLALL +#define BB_KLOGD +//#define BB_LASH +//#define BB_LENGTH +#define BB_LN +//#define BB_LOADACM +//#define BB_LOADFONT +//#define BB_LOADKMAP +#define BB_LOGGER +//#define BB_LOGNAME +#define BB_LS +#define BB_LSMOD +//#define BB_MAKEDEVS +//#define BB_MD5SUM +#define BB_MKDIR +//#define BB_MKFIFO +//#define BB_MKFS_MINIX +#define BB_MKNOD +#define BB_MKSWAP +//#define BB_MKTEMP +#define BB_MODPROBE +#define BB_MORE +#define BB_MOUNT +#define BB_MSH +//#define BB_MT +#define BB_MV +//#define BB_NC +//#define BB_NSLOOKUP +#define BB_PIDOF +//#define BB_PING +//#define BB_PIVOT_ROOT +#define BB_POWEROFF +//#define BB_PRINTF +#define BB_PS +#define BB_PWD +//#define BB_RDATE +//#define BB_READLINK +#define BB_REBOOT +//#define BB_RENICE +#define BB_RESET +#define BB_RM +#define BB_RMDIR +//#define BB_RMMOD +//#define BB_ROUTE +//#define BB_RPM2CPIO +#define BB_SED +//#define BB_SETKEYCODES +#define BB_SLEEP +#define BB_SORT +//#define BB_STTY +#define BB_SWAPONOFF +#define BB_SYNC +#define BB_SYSLOGD +#define BB_TAIL +#define BB_TAR +//#define BB_TEE +//#define BB_TEST +//#define BB_TELNET +//#define BB_TFTP +#define BB_TOUCH +//#define BB_TR +//#define BB_TRACEROUTE +#define BB_TRUE_FALSE +#define BB_TTY +//#define BB_UNIX2DOS +//#define BB_UUENCODE +//#define BB_UUDECODE +#define BB_UMOUNT +#define BB_UNIQ +#define BB_UNAME +//#define BB_UPDATE +#define BB_UPTIME +//#define BB_USLEEP +//#define BB_VI +//#define BB_WATCHDOG +#define BB_WC +//#define BB_WGET +#define BB_WHICH +#define BB_WHOAMI +#define BB_XARGS +#define BB_YES +// End of Applications List +// +// +// +// --------------------------------------------------------- +// This is where feature definitions go. Generally speaking, +// turning this stuff off makes things a bit smaller (and less +// pretty/useful). +// +// +// If you enabled one or more of the shells, you may select which one +// should be run when sh is invoked: +//#define BB_FEATURE_SH_IS_ASH +//#define BB_FEATURE_SH_IS_HUSH +//#define BB_FEATURE_SH_IS_LASH +#define BB_FEATURE_SH_IS_MSH +// +// BusyBox will, by default, malloc space for its buffers. This costs code +// size for the call to xmalloc. You can use the following feature to have +// them put on the stack. For some very small machines with limited stack +// space, this can be deadly. For most folks, this works just fine... +//#define BB_FEATURE_BUFFERS_GO_ON_STACK +// The third alternative for buffer allocation is to use 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. +//#define BB_FEATURE_BUFFERS_GO_IN_BSS +// +// Turn this on to use Erik's very cool devps, and devmtab kernel drivers, +// thereby eliminating the need for the /proc filesystem and thereby saving +// lots and lots memory for more important things. NOTE: If you enable this +// feature, you _must_ have patched the kernel to include the devps patch that +// is included in the busybox/kernel-patches directory. You will also need to +// create some device special files in /dev on your embedded system: +// mknod /dev/mtab c 10 22 +// mknod /dev/ps c 10 21 +// I emailed Linus and this patch will not be going into the stock kernel. +//#define BB_FEATURE_USE_DEVPS_PATCH +// +// show verbose usage messages +//#define BB_FEATURE_VERBOSE_USAGE +// +// Use termios to manipulate the screen ('more' is prettier with this on) +//#define BB_FEATURE_USE_TERMIOS +// +// calculate terminal & column widths (for more and ls) +#define BB_FEATURE_AUTOWIDTH +// +// show username/groupnames for ls +#define BB_FEATURE_LS_USERNAME +// +// show file timestamps in ls +#define BB_FEATURE_LS_TIMESTAMPS +// +// enable ls -p and -F +#define BB_FEATURE_LS_FILETYPES +// +// sort the file names +#define BB_FEATURE_LS_SORTFILES +// +// enable ls -R +#define BB_FEATURE_LS_RECURSIVE +// +// enable ls -L +#define BB_FEATURE_LS_FOLLOWLINKS +// +// Disable for a smaller (but less functional) ping +#define BB_FEATURE_FANCY_PING +// +// Make init use a simplified /etc/inittab file (recommended). +#define BB_FEATURE_USE_INITTAB +// +//Enable init being called as /linuxrc +#define BB_FEATURE_LINUXRC +// +//Have init enable core dumping for child processes (for debugging only) +//#define BB_FEATURE_INIT_COREDUMPS +// +//Make sure nothing is printed to the console on boot +//#define BB_FEATURE_EXTRA_QUIET +// +// enable syslogd -R remotehost +#define BB_FEATURE_REMOTE_LOG +// +// enable syslogd -C +//#define BB_FEATURE_IPC_SYSLOG +// +//Disable for a simple tail implementation (2.34k vs 3k for the full one). +//Both provide 'tail -f', but this cuts out -c, -q, -s, and -v. +#define BB_FEATURE_FANCY_TAIL +// +// Enable support for loop devices in mount +#define BB_FEATURE_MOUNT_LOOP +// +// Enable support for a real /etc/mtab file instead of /proc/mounts +//#define BB_FEATURE_MTAB_SUPPORT +// +// Enable support for mounting remote NFS volumes. +// You may need to mount with "-o nolock" if you are +// not running a local portmapper daemon... +//#define BB_FEATURE_NFSMOUNT +// +// Enable support forced filesystem unmounting +// (i.e., in case of an unreachable NFS system). +#define BB_FEATURE_MOUNT_FORCE +// +// Enable support for creation of tar files. +#define BB_FEATURE_TAR_CREATE +// +// Enable support for "--exclude" and "-X" for excluding files +#define BB_FEATURE_TAR_EXCLUDE +// +// Enable support for tar -z option (currently only works for inflating) +#define BB_FEATURE_TAR_GZIP +// +// Enable reverse sort +#define BB_FEATURE_SORT_REVERSE +// +// Enable uniqe sort +#define BB_FEATURE_SORT_UNIQUE +// +// Enable command line editing in the shell. +// Only relevant if a shell is enabled. On by default. +#define BB_FEATURE_COMMAND_EDITING +// +// Enable tab completion in the shell. This is now working quite nicely. +// This feature adds a bit over 4k. Only relevant if a shell is enabled. +#define BB_FEATURE_COMMAND_TAB_COMPLETION +// +// Attempts to match usernames in a ~-prefixed path +//#define BB_FEATURE_COMMAND_USERNAME_COMPLETION +// +//Allow the shell to invoke all the compiled in BusyBox applets as if they +//were shell builtins. Nice for staticly linking an emergency rescue shell, +//among other things. Off by default. +// Only relevant if a shell is enabled. +//#define BB_FEATURE_SH_STANDALONE_SHELL +// +//When this is enabled, busybox shell applets can be called using full path +//names. This causes applets (i.e., most busybox commands) to override +//real commands on the filesystem. For example, if you run run /bin/cat, it +//will use BusyBox cat even if /bin/cat exists on the filesystem and is _not_ +//busybox. Some systems want this, others do not. Choose wisely. :-) This +//only has meaning when BB_FEATURE_SH_STANDALONE_SHELL is enabled. +// Only relevant if a shell is enabled. Off by default. +//#define BB_FEATURE_SH_APPLETS_ALWAYS_WIN +// +// Uncomment this option for a fancy shell prompt that includes the +// current username and hostname. On systems that don't have usernames +// or hostnames, this can look hideous. +// Only relevant if a shell is enabled. +//#define BB_FEATURE_SH_FANCY_PROMPT +// +//Turn on extra fbset options +//#define BB_FEATURE_FBSET_FANCY +// +//Turn on fbset readmode support +//#define BB_FEATURE_FBSET_READMODE +// +// Support insmod/lsmod/rmmod for post 2.1 kernels +//#define BB_FEATURE_NEW_MODULE_INTERFACE +// +// Support insmod/lsmod/rmmod for pre 2.1 kernels +//#define BB_FEATURE_OLD_MODULE_INTERFACE +// +// Support module version checking +//#define BB_FEATURE_INSMOD_VERSION_CHECKING +// +// Support for uClinux memory usage optimization, which will load the image +// directly into the kernel memory. This divides memory requrements by three. +// If you are not running uClinux (i.e., your CPU has an MMU) leave this +// disabled... +//#define BB_FEATURE_INSMOD_LOADINKMEM +// +// Support for Minix filesystem, version 2 +//#define BB_FEATURE_MINIX2 +// +// Enable ifconfig status reporting output -- this feature adds 12k. +//#define BB_FEATURE_IFCONFIG_STATUS +// +// Enable ifconfig slip-specific options "keepalive" and "outfill" +//#define BB_FEATURE_IFCONFIG_SLIP +// +// Enable ifconfig options "mem_start", "io_addr", and "irq". +//#define BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ +// +// Enable ifconfig option "hw". Currently works for only with "ether". +//#define BB_FEATURE_IFCONFIG_HW +// +// Enable busybox --install [-s] +// to create links (or symlinks) for all the commands that are +// compiled into the binary. (needs /proc filesystem) +//#define BB_FEATURE_INSTALLER +// +// Enable a nifty progress meter in wget (adds just under 2k) +#define BB_FEATURE_WGET_STATUSBAR +// +// Enable HTTP authentication in wget +#define BB_FEATURE_WGET_AUTHENTICATION +// +// Clean up all memory before exiting -- usually not needed +// as the OS can clean up... Don't enable this unless you +// have a really good reason for cleaning things up manually. +//#define BB_FEATURE_CLEAN_UP +// +// Support for human readable output by ls, du, etc.(example 13k, 23M, 235G) +#define BB_FEATURE_HUMAN_READABLE +// +// Support for the find -type option. +#define BB_FEATURE_FIND_TYPE +// +// Support for the find -perm option. +#define BB_FEATURE_FIND_PERM +// +// Support for the find -mtine option. +#define BB_FEATURE_FIND_MTIME +// +// Support for the -A -B and -C context flags in grep +//#define BB_FEATURE_GREP_CONTEXT +// +// Support for the EGREP applet (alias to the grep applet) +//#define BB_FEATURE_GREP_EGREP_ALIAS +// +// Tell tftp what commands that should be supported. +#define BB_FEATURE_TFTP_PUT +#define BB_FEATURE_TFTP_GET +// +// features for vi +#define BB_FEATURE_VI_COLON // ":" colon commands, no "ex" mode +#define BB_FEATURE_VI_YANKMARK // Yank/Put commands and Mark cmds +#define BB_FEATURE_VI_SEARCH // search and replace cmds +#define BB_FEATURE_VI_USE_SIGNALS // catch signals +#define BB_FEATURE_VI_DOT_CMD // remember previous cmd and "." cmd +#define BB_FEATURE_VI_READONLY // vi -R and "view" mode +#define BB_FEATURE_VI_SETOPTS // set-able options, ai ic showmatch +#define BB_FEATURE_VI_SET // :set +#define BB_FEATURE_VI_WIN_RESIZE // handle window resize +// +// Enable a if you system have setuped locale +//#define BB_LOCALE_SUPPORT +// +// Support for TELNET to pass TERM type to remote host. Adds 384 bytes. +#define BB_FEATURE_TELNET_TTYPE +// +// Support for devfs. +//#define BB_FEATURE_DEVFS +// +// End of Features List +// +// +// +// +// +// +//--------------------------------------------------- +// Nothing beyond this point should ever be touched by +// mere mortals so leave this stuff alone. +// +#include +#if defined __UCLIBC__ && ! defined __UCLIBC_HAS_MMU__ + #undef BB_RPM2CPIO /* Uses gz_open(), which uses fork() */ + #undef BB_DPKG_DEB /* Uses gz_open(), which uses fork() */ + #undef BB_ASH /* Uses fork() */ + #undef BB_HUSH /* Uses fork() */ + #undef BB_LASH /* Uses fork() */ + #undef BB_INIT /* Uses fork() */ + #undef BB_FEATURE_TAR_GZIP /* Uses fork() */ + #undef BB_SYSLOGD /* Uses daemon() */ + #undef BB_KLOGD /* Uses daemon() */ + #undef BB_UPDATE /* Uses daemon() */ +#endif +#if defined BB_ASH || defined BB_HUSH || defined BB_LASH || defined BB_MSH + #if defined BB_FEATURE_COMMAND_EDITING + #define BB_CMDEDIT + #else + #undef BB_FEATURE_COMMAND_EDITING + #undef BB_FEATURE_COMMAND_TAB_COMPLETION + #undef BB_FEATURE_COMMAND_USERNAME_COMPLETION + #undef BB_FEATURE_SH_FANCY_PROMPT + #endif +#else + #undef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + #undef BB_FEATURE_SH_STANDALONE_SHELL + #undef BB_FEATURE_SH_FANCY_PROMPT +#endif +// +#ifdef BB_KILLALL + #ifndef BB_KILL + #define BB_KILL + #endif +#endif +// +#ifndef BB_INIT + #undef BB_FEATURE_LINUXRC +#endif +// +#if defined BB_MOUNT && defined BB_FEATURE_NFSMOUNT + #define BB_NFSMOUNT +#endif +// +#if defined BB_FEATURE_AUTOWIDTH + #ifndef BB_FEATURE_USE_TERMIOS + #define BB_FEATURE_USE_TERMIOS + #endif +#endif +// +#if defined BB_INSMOD || defined BB_LSMOD + #if ! defined BB_FEATURE_NEW_MODULE_INTERFACE && ! defined BB_FEATURE_OLD_MODULE_INTERFACE + #define BB_FEATURE_NEW_MODULE_INTERFACE + #endif +#endif +// +#ifdef BB_UNIX2DOS + #define BB_DOS2UNIX +#endif +// +#ifdef BB_SYSLOGD + #if defined BB_FEATURE_IPC_SYSLOG + #define BB_LOGREAD + #endif +#endif +// +#if defined BB_ASH && defined BB_FEATURE_SH_IS_ASH +# define BB_SH +# define shell_main ash_main +#elif defined BB_HUSH && defined BB_FEATURE_SH_IS_HUSH +# define BB_SH +# define shell_main hush_main +#elif defined BB_LASH && defined BB_FEATURE_SH_IS_LASH +# define BB_SH +# define shell_main lash_main +#elif defined BB_MSH && defined BB_FEATURE_SH_IS_MSH +# define BB_SH +# define shell_main msh_main +#endif diff --git a/busybox/INSTALL b/busybox/INSTALL new file mode 100644 index 000000000..e17bd80d3 --- /dev/null +++ b/busybox/INSTALL @@ -0,0 +1,8 @@ +1) Check Config.h and adjust if you need a different functionality than + defined by default. + +2) Check the Makefile + +3) make + +4) make install diff --git a/busybox/LICENSE b/busybox/LICENSE new file mode 100644 index 000000000..8e5a143d0 --- /dev/null +++ b/busybox/LICENSE @@ -0,0 +1,378 @@ +Original release code (unless otherwise noted) +Copyright 1995, 1996 Bruce Perens + +mkswap +Copyright 1991 Linus Torvalds + +tiny-ls(ls) +Copyright 1996 Brian Candler + +tarcat, loadkmap, various fixes, Debian maintenance +Copyright 1998 Enrique Zanardi + +more(v2), makedevs, dutmp, modularization, auto links file, +various fixes, Linux Router Project maintenance +Copyright 1998 Dave Cinege + +mini-gzip(gzip), mini-netcat(mnc) +Copyright 1998 Charles P. Wright + +Tons of new stuff as noted in header files +Copyright (C) 1999,2000,2001 by Lineo, inc. and written by +Erik Andersen , + + + +Please feed suggestions, bug reports, insults, and bribes back to: + Erik Andersen + + + + + +Busybox may be used and distributed under the GNU General Public License. + +--------------------------------------------------------------------------- + + + 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. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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. + + , 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 index 000000000..44d5b5e93 --- /dev/null +++ b/busybox/Makefile @@ -0,0 +1,443 @@ +# Makefile for busybox +# +# Copyright (C) 1999,2000,2001 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 +# + +PROG := busybox +VERSION := 0.60.0 +BUILDTIME := $(shell TZ=UTC date -u "+%Y.%m.%d-%H:%M%z") +export VERSION + +# 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 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 = + +# If you want a static binary, turn this on. +DOSTATIC = false + +# Set the following to `true' to make a debuggable build. +# Leave this set to `false' for production use. +DODEBUG = false + +# Setting this to `true' will cause busybox to directly use the system's +# password and group functions. Assuming you use GNU libc, when this is +# `true', you will need to install the /etc/nsswitch.conf configuration file +# and the required libnss_* libraries. This generally makes your embedded +# system quite a bit larger... If you leave this off, busybox will directly use +# the /etc/password, /etc/group files (and your system will be smaller, and I +# will get fewer emails asking about how glibc NSS works). Enabling this adds +# just 1.4k to the binary size (which is a _lot_ less then glibc NSS costs). +# Note that if you want hostname resolution to work with glibc, you still need +# the libnss_* libraries. Most people will want to leave this set to false. +USE_SYSTEM_PWD_GRP = true + +# 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 first set up your environment. +# eg: `export DMALLOC_OPTIONS=debug=0x14f47d83,inter=100,log=logfile` +# Do not enable this for production builds... +DODMALLOC = false + +# Electric-fence is another very useful malloc debugging library. +# Do not enable this for production builds... +DOEFENCE = false + +# If you want large file summit support, turn this on. +# This has no effect if you don't have a kernel with lfs +# support, and a system with libc-2.1.3 or later. +# Some of the programs that can benefit from lfs support +# are dd, gzip, mount, tar, and mkfs_minix. +# LFS allows you to use the above programs for files +# larger than 2GB! +DOLFS = false + +# If you have a "pristine" source directory, point BB_SRC_DIR to it. +# Experimental and incomplete; tell the mailing list +# if you do or don't like it so far. +BB_SRC_DIR = + +# If you are running a cross compiler, you may want to set this +# to something more interesting, like "powerpc-linux-". +CROSS = +CC = $(CROSS)gcc +AR = $(CROSS)ar +STRIPTOOL = $(CROSS)strip + +# To compile vs uClibc, just use the compiler wrapper built by uClibc... +# This make things very easy? Everything should compile and work as +# expected these days... +#CC = ../uClibc/extra/gcc-uClibc/i386-uclibc-gcc + +# 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 +# +# The following is used for libc5 (if you install altgcc and libc5-altdev +# on a Debian system). +#LIBCDIR=/usr/i486-linuxlibc1 +# +# For other libraries, you are on your own... +#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") + +# use '-Os' optimization if available, else use -O2 +OPTIMIZATION := $(shell if $(CC) -Os -S -o /dev/null -xc /dev/null >/dev/null 2>&1; \ + then echo "-Os"; else echo "-O2" ; fi) + +WARNINGS = -Wall -Wshadow + +ARFLAGS = -r + +# +#-------------------------------------------------------- +# 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 $(DOLFS)),true) + # For large file summit support + CFLAGS+=-D_FILE_OFFSET_BITS=64 +endif +ifeq ($(strip $(DODMALLOC)),true) + # For testing mem leaks with dmalloc + CFLAGS+=-DDMALLOC + LIBRARIES = -ldmalloc + # Force debug=true, since this is useless when not debugging... + DODEBUG = true +else + ifeq ($(strip $(DOEFENCE)),true) + LIBRARIES = -lefence + # Force debug=true, since this is useless when not debugging... + DODEBUG = true + endif +endif +ifeq ($(strip $(DODEBUG)),true) + CFLAGS += $(WARNINGS) -g -D_GNU_SOURCE + LDFLAGS += -Wl,-warn-common + STRIP = +else + CFLAGS += $(WARNINGS) $(OPTIMIZATION) -fomit-frame-pointer -D_GNU_SOURCE + LDFLAGS += -s -Wl,-warn-common + STRIP = $(STRIPTOOL) --remove-section=.note --remove-section=.comment $(PROG) +endif +ifeq ($(strip $(DOSTATIC)),true) + LDFLAGS += --static + # + #use '-ffunction-sections -fdata-sections' and '--gc-sections' (if they + # work) to try and strip out any unused junk. Doesn't do much for me, + # but you may want to give it a shot... + # + #ifeq ($(shell $(CC) -ffunction-sections -fdata-sections -S \ + # -o /dev/null -xc /dev/null 2>/dev/null && $(LD) \ + # --gc-sections -v >/dev/null && echo 1),1) + # CFLAGS += -ffunction-sections -fdata-sections + # LDFLAGS += --gc-sections + #endif +endif + +ifndef $(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 . +# +ifneq ($(strip $(BB_SRC_DIR)),) + VPATH = $(BB_SRC_DIR) +endif +#ifneq ($(strip $(VPATH)),) +# CFLAGS += -I- -I. $(patsubst %,-I%,$(subst :, ,$(VPATH))) +#endif + +# We need to set APPLET_SOURCES to something like +# $(shell busybox.sh Config.h) +# but in a manner that works with VPATH and BB_SRC_DIR. +# Possible ways to approach this: +# +# 1. Explicitly search through .:$(VPATH) for busybox.sh and config.h, +# then $(shell $(BUSYBOX_SH) $(CONFIG_H) $(BB_SRC_DIR)) +# +# 2. Explicity search through .:$(VPATH) for slist.mk, +# then $(shell $(MAKE) -f $(SLIST_MK) VPATH=$(VPATH) BB_SRC_DIR=$(BB_SRC_DIR)) +# +# 3. Create slist.mk in this directory, with commands embedded in +# a $(shell ...) command, and $(MAKE) it immediately. +# +# 4. Use a real rule within this makefile to create a file that sets +# APPLET_SOURCE_LIST, then include that file. Has complications +# with the first trip through the makefile (before processing the +# include) trying to do too much, and a spurious warning the first +# time make is run. +# +# This is option 3: +# +#APPLET_SOURCES = $(shell \ +# echo -e 'all: busybox.sh Config.h\n\t@ $$(SHELL) $$^ $$(BB_SRC_DIR)' >slist.mk; \ +# make -f slist.mk VPATH=$(VPATH) BB_SRC_DIR=$(BB_SRC_DIR) \ +#) +# And option 4: +-include applet_source_list + +OBJECTS = $(APPLET_SOURCES:.c=.o) busybox.o usage.o applets.o +CFLAGS += $(CROSS_CFLAGS) +CFLAGS += -DBB_VER='"$(VERSION)"' +CFLAGS += -DBB_BT='"$(BUILDTIME)"' +ifdef BB_INIT_SCRIPT + CFLAGS += -DINIT_SCRIPT='"$(BB_INIT_SCRIPT)"' +endif + +ifneq ($(strip $(USE_SYSTEM_PWD_GRP)),true) + PWD_GRP = pwd_grp + PWD_GRP_DIR = $(BB_SRC_DIR:=/)$(PWD_GRP) + PWD_LIB = libpwd.a + PWD_CSRC=__getpwent.c pwent.c getpwnam.c getpwuid.c putpwent.c getpw.c \ + fgetpwent.c __getgrent.c grent.c getgrnam.c getgrgid.c fgetgrent.c \ + initgroups.c setgroups.c + PWD_OBJS=$(patsubst %.c,$(PWD_GRP)/%.o, $(PWD_CSRC)) +ifneq ($(strip $(BB_SRC_DIR)),) + PWD_CFLAGS = -I- -I. +endif + PWD_CFLAGS += -I$(PWD_GRP_DIR) +else + CFLAGS += -DUSE_SYSTEM_PWD_GRP +endif + +LIBBB = libbb +LIBBB_LIB = libbb.a +LIBBB_CSRC= ask_confirmation.c chomp.c concat_path_file.c copy_file.c \ +copy_file_chunk.c libc5.c device_open.c error_msg.c \ +error_msg_and_die.c fgets_str.c find_mount_point.c find_pid_by_name.c \ +find_root_device.c full_read.c full_write.c get_console.c \ +get_last_path_component.c get_line_from_file.c gz_open.c human_readable.c \ +isdirectory.c kernel_version.c loop.c mode_string.c module_syscalls.c mtab.c \ +mtab_file.c my_getgrnam.c my_getgrgid.c my_getpwnam.c my_getpwnamegid.c \ +my_getpwuid.c parse_mode.c parse_number.c perror_msg.c perror_msg_and_die.c \ +print_file.c process_escape_sequence.c read_package_field.c recursive_action.c \ +safe_read.c safe_strncpy.c syscalls.c syslog_msg_with_name.c time_string.c \ +trim.c unzip.c vdprintf.c verror_msg.c vperror_msg.c wfopen.c xfuncs.c \ +xgetcwd.c xreadlink.c xregcomp.c interface.c remove_file.c last_char_is.c \ +copyfd.c vherror_msg.c herror_msg.c herror_msg_and_die.c xgethostbyname.c \ +dirname.c make_directory.c create_icmp_socket.c u_signal_names.c arith.c +LIBBB_OBJS=$(patsubst %.c,$(LIBBB)/%.o, $(LIBBB_CSRC)) +ifeq ($(strip $(BB_SRC_DIR)),) + LIBBB_CFLAGS += -I$(LIBBB) +else + LIBBB_CFLAGS = -I- -I. -I./$(LIBBB) -I$(BB_SRC_DIR)/$(LIBBB) -I$(BB_SRC_DIR) +endif + +LIBBB_MSRC=libbb/messages.c +LIBBB_MESSAGES= full_version name_too_long omitting_directory not_a_directory \ +memory_exhausted invalid_date invalid_option io_error dash_dash_help \ +write_error too_few_args name_longer_than_foo unknown can_not_create_raw_socket +LIBBB_MOBJ=$(patsubst %,$(LIBBB)/%.o, $(LIBBB_MESSAGES)) + +LIBBB_ARCSRC=libbb/unarchive.c +LIBBB_ARCOBJ= archive_offset seek_sub_file extract_archive unarchive \ +get_header_ar get_header_cpio get_header_tar deb_extract +LIBBB_AROBJS=$(patsubst %,$(LIBBB)/%.o, $(LIBBB_ARCOBJ)) + + +# Put user-supplied flags at the end, where they +# have a chance of winning. +CFLAGS += $(CFLAGS_EXTRA) + +.EXPORT_ALL_VARIABLES: + +all: applet_source_list busybox busybox.links doc + +applet_source_list: busybox.sh Config.h + (echo -n "APPLET_SOURCES := "; BB_SRC_DIR=$(BB_SRC_DIR) $(SHELL) $^) > $@ + +doc: olddoc + +# Old Docs... +olddoc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html + +docs/busybox.pod : docs/busybox_header.pod usage.h docs/busybox_footer.pod + - ( cat docs/busybox_header.pod; \ + docs/autodocifier.pl usage.h; \ + cat 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.lineo.com/BusyBox.html + - mkdir -p docs + -@ rm -f docs/BusyBox.html + -@ ln -s busybox.lineo.com/BusyBox.html docs/BusyBox.html + +docs/busybox.lineo.com/BusyBox.html: docs/busybox.pod + -@ mkdir -p docs/busybox.lineo.com + - pod2html --noindex $< > \ + docs/busybox.lineo.com/BusyBox.html + -@ rm -f pod2htm* + + +# New docs based on DOCBOOK SGML +newdoc: docs/busybox.txt docs/busybox.pdf docs/busybox/busyboxdocumentation.html + +docs/busybox.txt: docs/busybox.sgml + @echo + @echo BusyBox Documentation + @echo + - mkdir -p docs + (cd docs; sgmltools -b txt busybox.sgml) + +docs/busybox.dvi: docs/busybox.sgml + - mkdir -p docs + (cd docs; sgmltools -b dvi busybox.sgml) + +docs/busybox.ps: docs/busybox.sgml + - mkdir -p docs + (cd docs; sgmltools -b ps busybox.sgml) + +docs/busybox.pdf: docs/busybox.ps + - mkdir -p docs + (cd docs; ps2pdf busybox.ps) + +docs/busybox/busyboxdocumentation.html: docs/busybox.sgml + - mkdir -p docs + (cd docs/busybox.lineo.com; sgmltools -b html ../busybox.sgml) + + +busybox: $(PWD_LIB) $(LIBBB_LIB) $(OBJECTS) + $(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBBB_LIB) $(PWD_LIB) $(LIBRARIES) + $(STRIP) + +# Without VPATH, rule expands to "/bin/sh busybox.mkll Config.h applets.h" +# but with VPATH, some or all of those file names are resolved to the +# directories in which they live. +busybox.links: busybox.mkll Config.h applets.h + - $(SHELL) $^ >$@ + +nfsmount.o cmdedit.o: %.o: %.h +ash.o hush.o lash.o msh.o: cmdedit.h +$(OBJECTS): %.o: %.c Config.h busybox.h applets.h Makefile +ifeq ($(strip $(BB_SRC_DIR)),) + $(CC) $(CFLAGS) -I. $(patsubst %,-I%,$(subst :, ,$(BB_SRC_DIR))) -c $< -o $*.o +else + $(CC) $(CFLAGS) -I- -I. $(patsubst %,-I%,$(subst :, ,$(BB_SRC_DIR))) -c $< -o $*.o +endif + +$(PWD_OBJS): %.o: %.c Config.h busybox.h applets.h Makefile + - mkdir -p $(PWD_GRP) + $(CC) $(CFLAGS) $(PWD_CFLAGS) -c $< -o $*.o + +$(LIBBB_OBJS): %.o: %.c Config.h busybox.h applets.h Makefile libbb/libbb.h + - mkdir -p $(LIBBB) + $(CC) $(CFLAGS) $(LIBBB_CFLAGS) -c $< -o $*.o + +$(LIBBB_MOBJ): $(LIBBB_MSRC) + - mkdir -p $(LIBBB) + $(CC) $(CFLAGS) $(LIBBB_CFLAGS) -DL_$(patsubst libbb/%,%,$*) -c $< -o $*.o + +$(LIBBB_AROBJS): $(LIBBB_ARCSRC) + - mkdir -p $(LIBBB) + $(CC) $(CFLAGS) $(LIBBB_CFLAGS) -DL_$(patsubst libbb/%,%,$*) -c $< -o $*.o + +libpwd.a: $(PWD_OBJS) + $(AR) $(ARFLAGS) $@ $^ + +libbb.a: $(LIBBB_MOBJ) $(LIBBB_AROBJS) $(LIBBB_OBJS) + $(AR) $(ARFLAGS) $@ $^ + +usage.o: usage.h + +libbb/loop.o: libbb/loop.h + +libbb/loop.h: mk_loop_h.sh + @ $(SHELL) $< > $@ + +test tests: + # old way of doing it + #cd tests && $(MAKE) all + # new way of doing it + cd tests && ./tester.sh + +clean: + - cd tests && $(MAKE) clean + - rm -f docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html \ + docs/busybox.lineo.com/BusyBox.html + - rm -f docs/busybox.txt docs/busybox.dvi docs/busybox.ps \ + docs/busybox.pdf docs/busybox.lineo.com/busybox.html + - rm -f multibuild.log Config.h.orig *.gdb *.elf + - rm -rf docs/busybox _install libpwd.a libbb.a pod2htm* + - rm -f busybox.links libbb/loop.h *~ slist.mk core applet_source_list + - find -name \*.o -exec rm -f {} \; + +distclean: clean + - rm -f busybox applet_source_list + - cd tests && $(MAKE) distclean + +install: install.sh busybox busybox.links + $(SHELL) $< $(PREFIX) + +install-hardlinks: install.sh busybox busybox.links + $(SHELL) $< $(PREFIX) --hardlinks + +debug_pristine: + @ echo VPATH=\"$(VPATH)\" + @ echo OBJECTS=\"$(OBJECTS)\" + +dist release: distclean doc + cd ..; \ + rm -rf busybox-$(VERSION); \ + cp -a busybox busybox-$(VERSION); \ + \ + find busybox-$(VERSION)/ -type d \ + -name CVS \ + -print \ + -exec rm -rf {} \; ; \ + \ + find busybox-$(VERSION)/ -type f \ + -name .\#* \ + -print \ + -exec rm -f {} \; ; \ + \ + tar -cvzf busybox-$(VERSION).tar.gz busybox-$(VERSION)/; + +.PHONY: tags +tags: + ctags -R . diff --git a/busybox/README b/busybox/README new file mode 100644 index 000000000..b45ef57f4 --- /dev/null +++ b/busybox/README @@ -0,0 +1,152 @@ +Please see the LICENSE file for copyright information. + +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 +embedded system. 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 kernel. + +BusyBox was originally written to support the Debian Rescue/Install disks, but +it also makes an excellent environment for any small or embedded system. + +As of version 0.20 there is now a version number. : ) Also as of version 0.20, +BusyBox is now modularized to easily allow you to build only the components you +need, thereby reducing binary size. To turn off unwanted BusyBox components, +simply edit the file "Config.h" and comment out the components you do not need +using C++ style (//) comments. + +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') + +---------------- + +Supported architectures: + + Busybox in general will build on any architecture supported by gcc. It has + a few specialized features added for __sparc__ and __alpha__. insmod + functionality is currently limited to x86, ARM, SH3/4, powerpc, m68k, + and MIPS. + +Supported libcs: + + glibc-2.0.x, glibc-2.1.x, Linux-libc5, uClibc. People are looking at + newlib and diet-libc, but consider them unsupported, untested, or worse. + +Supported kernels: + + Full functionality requires Linux 2.0.x, 2.2.x, or 2.4.x. A large fraction + of the code should run on just about anything. + +---------------- + +Shells: + +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 Bourne shell grammer. It does handle pipes, redirects, and +job control though. Adding in command editing makes it a very nice +lightweight command prompt. + +hush is also quite small (just 18k) and it has very complete Bourne shell +grammer. It handles if/then/else/fi just fine, but doesn't handle loops +like for/do/done or case/esac and such. It also currently has a problem +with job control. Using hush is not yet recommended. + +msh: 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 grammer (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. This was only recently added, so there is still +room to shrink it further... + +ash: This adds about 60k in the default configuration and is the most +complete and most pedantically correct shell included with busybox. This +shell was also recently added, and several people (mainly Vladimir and Erik) +have been working on it. There are a number of configurable things at the +top of ash.c as well, so check those out if you want to tweak things. + +---------------- + +Getting help: + +When you find you need help, you can check out the BusyBox mailing list +archives at http://opensource.lineo.com/lists/busybox/ or even join +the mailing list if you are interested. + +---------------- + +Bugs: + +If you find bugs, please submit a bug report. Full instructions on how to +report a bug are found at http://bugs.lineo.com/Reporting.html. + +For the impatient: To submit a bug, simply send an email describing the problem +to submit@bugs.lineo.com. Bug reports should look something like this: + + To: submit@bugs.lineo.com + From: diligent@testing.linux.org + Subject: /bin/true doesn't work + + Package: busybox + Version: 0.51 + + When I invoke '/bin/true' it doesn't work. I expected it to return + a "0" but it returned a "1" instead. Here is the transcript: + $ /bin/true ; echo $? + 1 + With GNU /bin/true, I get the following output: + $ /bin/true ; echo $? + 0 + I am using Debian 2.2r2, kernel version 2.2.18, 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 take a _long_ time to be fixed... Thanks for +understanding. + +---------------- + +FTP: + +Source for the latest released version can always be downloaded from + ftp://ftp.lineo.com/pub/busybox. + +---------------- + +CVS: + +BusyBox now has its own publicly browsable CVS tree at: + http://opensource.lineo.com/cgi-bin/cvsweb/busybox/ + +Anonymous CVS access is available. For instructions, check out: + http://opensource.lineo.com/cvs_anon.html + +For those that are actively contributing there is even CVS write access: + http://opensource.lineo.com/cvs_write.html + +---------------- + +Please feed suggestions, bug reports, insults, and bribes back to: + Erik Andersen + + + + + +Many thanks to go to Lineo for paying me to work on busybox. + + diff --git a/busybox/TODO b/busybox/TODO new file mode 100644 index 000000000..3d9af20a7 --- /dev/null +++ b/busybox/TODO @@ -0,0 +1,57 @@ +TODO list for busybox in no particular order. Just because something +is listed here doesn't mean that it is going to be added to busybox, +or that doing so is even a good idea. It just means that I _might_ get +around to it some time. If you have any good ideas, please let me know. + +* login/sulogin/passwd/getty/etc are part of tinylogin, and so are not + needed or wanted in busybox (or else I'd have to link to libcrypt). + +* We _were_ going to split networking apps into a new package called + netkit-tiny. Per discussions on the mailing list, this isn't going + to happen. False alarm. Sorry about the confusion. + + + -Erik + +----------- + +Possible apps to include some time: + +* hwclock +* start-stop-daemon +* group/commonize strings, remove dups (for i18n, l10n) + +----------- + +With sysvinit, reboot, poweroff and halt all used a named pipe, +/dev/initctl, to communicate with the init process. Busybox +currently uses signals to communicate with init. This makes +busybox incompatible with sysvinit. We should probably use +a named pipe as well so we can be compatible. + +----------------------- + +Run the following: + + rm -f busybox && make LDFLAGS+=-nostdlib 2>&1 | \ + sed -ne 's/.*undefined reference to `\(.*\)..*/\1/gp' | sort | uniq + +reveals the list of all external (i.e., libc) things that BusyBox depends on. +It would be a very nice thing to reduce this list to an absolute minimum, to +reduce the footprint of busybox, especially when staticly linking with +libraries such as uClibc. + +----------------------- + +Compile with debugging on, run 'nm --size-sort ./busybox' +and then start with the biggest things and make them smaller... + +----------------------- + + du.c probably ought to have an -x switch like GNU du does... + +----------------------- + +xargs could use a -l option + +------------------------------------------------------------------ diff --git a/busybox/adjtimex.c b/busybox/adjtimex.c new file mode 100644 index 000000000..e3c160d87 --- /dev/null +++ b/busybox/adjtimex.c @@ -0,0 +1,176 @@ +/* + * 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 + * + * 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 and Jim Van Zandt + * (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 + * It will autosense if it is built in a busybox environment, based + * on the BB_VER preprocessor macro. + */ + +#include +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +#include +extern int adjtimex(struct timex *buf); +#else +#include +#endif + +#ifdef BB_VER +#include "busybox.h" +#endif + +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 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: + show_usage(); + exit(1); + } + } + if (argc != optind) { /* no valid non-option parameters */ + 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/applets.c b/busybox/applets.c new file mode 100644 index 000000000..ca2de87d4 --- /dev/null +++ b/busybox/applets.c @@ -0,0 +1,113 @@ +/* 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 +#include +#include +#include "busybox.h" + +#undef APPLET +#undef APPLET_NOUSAGE +#undef PROTOTYPES +#include "applets.h" + +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); + +extern void show_usage(void) +{ + const char *format_string; + const char *usage_string = usage_messages; + int i; + /* From busybox.c */ + extern int been_there_done_that; + + if (strcmp(applet_using->name, "busybox")==0) { + been_there_done_that=1; + busybox_main(0, NULL); + } + + for (i = applet_using - applets; i > 0; ) { + if (!*usage_string++) { + --i; + } + } + format_string = "%s\n\nUsage: %s %s\n\n"; + if(*usage_string == 0) + format_string = "%s\n\nNo help available.\n\n"; + fprintf(stderr, format_string, + 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; + + recurse_level++; + /* Do a binary search to find the applet entry given the name. */ + if ((applet_using = find_applet_by_name(name)) != NULL) { + applet_name = applet_using->name; + if (argv[1] && strcmp(argv[1], "--help") == 0) { + show_usage(); + } + 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 = 0; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/applets.h b/busybox/applets.h new file mode 100644 index 000000000..a26a06e21 --- /dev/null +++ b/busybox/applets.h @@ -0,0 +1,481 @@ +/* + * 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) extern int b(int argc, char **argv); + #define APPLET_NOUSAGE(a,b,c) extern int b(int argc, char **argv); + #define APPLET_ODDNAME(a,b,c,d) extern int b(int argc, char **argv); + extern const char usage_messages[]; +#elif defined(MAKE_USAGE) + #ifdef BB_FEATURE_VERBOSE_USAGE + #define APPLET(a,b,c) a##_trivial_usage "\n\n" a##_full_usage "\0" + #define APPLET_NOUSAGE(a,b,c) "\0" + #define APPLET_ODDNAME(a,b,c,d) d##_trivial_usage "\n\n" d##_full_usage "\0" + #else + #define APPLET(a,b,c) a##_trivial_usage "\0" + #define APPLET_NOUSAGE(a,b,c) "\0" + #define APPLET_ODDNAME(a,b,c,d) d##_trivial_usage "\0" + #endif +#elif defined(MAKE_LINKS) +# define APPLET(a,b,c) LINK c a +# define APPLET_NOUSAGE(a,b,c) LINK c a +# define APPLET_ODDNAME(a,b,c,d) LINK c a +#else + const struct BB_applet applets[] = { + #define APPLET(a,b,c) {#a,b,c}, + #define APPLET_NOUSAGE(a,b,c) {a,b,c}, + #define APPLET_ODDNAME(a,b,c,d) {a,b,c}, +#endif + + + +#ifdef BB_TEST + APPLET_NOUSAGE("[", test_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_ADJTIMEX + APPLET(adjtimex, adjtimex_main, _BB_DIR_SBIN) +#endif +#ifdef BB_AR + APPLET(ar, ar_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_ASH + APPLET_NOUSAGE("ash", ash_main, _BB_DIR_BIN) +#endif +#ifdef BB_BASENAME + APPLET(basename, basename_main, _BB_DIR_USR_BIN) +#endif + APPLET_NOUSAGE("busybox", busybox_main, _BB_DIR_BIN) +#ifdef BB_CAT + APPLET(cat, cat_main, _BB_DIR_BIN) +#endif +#ifdef BB_CHGRP + APPLET(chgrp, chgrp_main, _BB_DIR_BIN) +#endif +#ifdef BB_CHMOD + APPLET(chmod, chmod_main, _BB_DIR_BIN) +#endif +#ifdef BB_CHOWN + APPLET(chown, chown_main, _BB_DIR_BIN) +#endif +#ifdef BB_CHROOT + APPLET(chroot, chroot_main, _BB_DIR_USR_SBIN) +#endif +#ifdef BB_CHVT + APPLET(chvt, chvt_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_CLEAR + APPLET(clear, clear_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_CMP + APPLET(cmp, cmp_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_CP + APPLET(cp, cp_main, _BB_DIR_BIN) +#endif +#ifdef BB_CPIO + APPLET(cpio, cpio_main, _BB_DIR_BIN) +#endif +#ifdef BB_CUT + APPLET(cut, cut_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DATE + APPLET(date, date_main, _BB_DIR_BIN) +#endif +#ifdef BB_DC + APPLET(dc, dc_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DD + APPLET(dd, dd_main, _BB_DIR_BIN) +#endif +#ifdef BB_DEALLOCVT + APPLET(deallocvt, deallocvt_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DF + APPLET(df, df_main, _BB_DIR_BIN) +#endif +#ifdef BB_DIRNAME + APPLET(dirname, dirname_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DMESG + APPLET(dmesg, dmesg_main, _BB_DIR_BIN) +#endif +#ifdef BB_DOS2UNIX + APPLET(dos2unix, dos2unix_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DPKG + APPLET(dpkg, dpkg_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DPKG_DEB + APPLET_ODDNAME("dpkg-deb", dpkg_deb_main, _BB_DIR_USR_BIN, dpkg_deb) +#endif +#ifdef BB_DU + APPLET(du, du_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DUMPKMAP + APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN) +#endif +#ifdef BB_DUTMP + APPLET(dutmp, dutmp_main, _BB_DIR_USR_SBIN) +#endif +#ifdef BB_ECHO + APPLET(echo, echo_main, _BB_DIR_BIN) +#endif +#if defined(BB_FEATURE_GREP_EGREP_ALIAS) && defined(BB_GREP) + APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN) +#endif +#ifdef BB_ENV + APPLET(env, env_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_EXPR + APPLET(expr, expr_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TRUE_FALSE + APPLET(false, false_main, _BB_DIR_BIN) +#endif +#ifdef BB_FBSET + APPLET(fbset, fbset_main, _BB_DIR_USR_SBIN) +#endif +#ifdef BB_FDFLUSH + APPLET(fdflush, fdflush_main, _BB_DIR_BIN) +#endif +#ifdef BB_FIND + APPLET(find, find_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_FREE + APPLET(free, free_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_FREERAMDISK + APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN) +#endif +#ifdef BB_FSCK_MINIX + APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, fsck_minix) +#endif +#ifdef BB_GETOPT + APPLET(getopt, getopt_main, _BB_DIR_BIN) +#endif +#ifdef BB_GREP + APPLET(grep, grep_main, _BB_DIR_BIN) +#endif +#ifdef BB_GUNZIP + APPLET(gunzip, gunzip_main, _BB_DIR_BIN) +#endif +#ifdef BB_GZIP + APPLET(gzip, gzip_main, _BB_DIR_BIN) +#endif +#ifdef BB_HALT + APPLET(halt, halt_main, _BB_DIR_SBIN) +#endif +#ifdef BB_HEAD + APPLET(head, head_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_HOSTID + APPLET(hostid, hostid_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_HOSTNAME + APPLET(hostname, hostname_main, _BB_DIR_BIN) +#endif +#ifdef BB_HUSH + APPLET_NOUSAGE("hush", hush_main, _BB_DIR_BIN) +#endif +#ifdef BB_ID + APPLET(id, id_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_IFCONFIG + APPLET(ifconfig, ifconfig_main, _BB_DIR_SBIN) +#endif +#ifdef BB_INIT + APPLET(init, init_main, _BB_DIR_SBIN) +#endif +#ifdef BB_INSMOD + APPLET(insmod, insmod_main, _BB_DIR_SBIN) +#endif +#ifdef BB_KILL + APPLET(kill, kill_main, _BB_DIR_BIN) +#endif +#ifdef BB_KILLALL + APPLET(killall, kill_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_KLOGD + APPLET(klogd, klogd_main, _BB_DIR_SBIN) +#endif +#ifdef BB_LASH + APPLET(lash, lash_main, _BB_DIR_BIN) +#endif +#ifdef BB_LENGTH + APPLET(length, length_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_FEATURE_LINUXRC + APPLET_NOUSAGE("linuxrc", init_main, _BB_DIR_ROOT) +#endif +#ifdef BB_LN + APPLET(ln, ln_main, _BB_DIR_BIN) +#endif +#ifdef BB_LOADACM + APPLET(loadacm, loadacm_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_LOADFONT + APPLET(loadfont, loadfont_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_LOADKMAP + APPLET(loadkmap, loadkmap_main, _BB_DIR_SBIN) +#endif +#ifdef BB_LOGGER + APPLET(logger, logger_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_LOGNAME + APPLET(logname, logname_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_LOGREAD + APPLET(logread, logread_main, _BB_DIR_SBIN) +#endif +#ifdef BB_LS + APPLET(ls, ls_main, _BB_DIR_BIN) +#endif +#ifdef BB_LSMOD + APPLET(lsmod, lsmod_main, _BB_DIR_SBIN) +#endif +#ifdef BB_MAKEDEVS + APPLET(makedevs, makedevs_main, _BB_DIR_SBIN) +#endif +#ifdef BB_MD5SUM + APPLET(md5sum, md5sum_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_MKDIR + APPLET(mkdir, mkdir_main, _BB_DIR_BIN) +#endif +#ifdef BB_MKFIFO + APPLET(mkfifo, mkfifo_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_MKFS_MINIX + APPLET_ODDNAME("mkfs.minix", mkfs_minix_main, _BB_DIR_SBIN, mkfs_minix) +#endif +#ifdef BB_MKNOD + APPLET(mknod, mknod_main, _BB_DIR_BIN) +#endif +#ifdef BB_MKSWAP + APPLET(mkswap, mkswap_main, _BB_DIR_SBIN) +#endif +#ifdef BB_MKTEMP + APPLET(mktemp, mktemp_main, _BB_DIR_BIN) +#endif +#ifdef BB_MODPROBE + APPLET(modprobe, modprobe_main, _BB_DIR_SBIN) +#endif +#ifdef BB_MORE + APPLET(more, more_main, _BB_DIR_BIN) +#endif +#ifdef BB_MOUNT + APPLET(mount, mount_main, _BB_DIR_BIN) +#endif +#ifdef BB_MSH + APPLET_NOUSAGE("msh", msh_main, _BB_DIR_BIN) +#endif +#ifdef BB_MT + APPLET(mt, mt_main, _BB_DIR_BIN) +#endif +#ifdef BB_MV + APPLET(mv, mv_main, _BB_DIR_BIN) +#endif +#ifdef BB_NC + APPLET(nc, nc_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_NSLOOKUP + APPLET(nslookup, nslookup_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_PIDOF + APPLET(pidof, pidof_main, _BB_DIR_BIN) +#endif +#ifdef BB_PING + APPLET(ping, ping_main, _BB_DIR_BIN) +#endif +#ifdef BB_PIVOT_ROOT + APPLET(pivot_root, pivot_root_main, _BB_DIR_SBIN) +#endif +#ifdef BB_POWEROFF + APPLET(poweroff, poweroff_main, _BB_DIR_SBIN) +#endif +#ifdef BB_PRINTF + APPLET(printf, printf_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_PS + APPLET(ps, ps_main, _BB_DIR_BIN) +#endif +#ifdef BB_PWD + APPLET(pwd, pwd_main, _BB_DIR_BIN) +#endif +#ifdef BB_RDATE + APPLET(rdate, rdate_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_READLINK + APPLET(readlink, readlink_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_REBOOT + APPLET(reboot, reboot_main, _BB_DIR_SBIN) +#endif +#ifdef BB_RENICE + APPLET(renice, renice_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_RESET + APPLET(reset, reset_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_RM + APPLET(rm, rm_main, _BB_DIR_BIN) +#endif +#ifdef BB_RMDIR + APPLET(rmdir, rmdir_main, _BB_DIR_BIN) +#endif +#ifdef BB_RMMOD + APPLET(rmmod, rmmod_main, _BB_DIR_SBIN) +#endif +#ifdef BB_ROUTE + APPLET(route, route_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_RPM2CPIO + APPLET(rpm2cpio, rpm2cpio_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_RPMUNPACK + APPLET(rpmunpack, rpmunpack_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_SED + APPLET(sed, sed_main, _BB_DIR_BIN) +#endif +#ifdef BB_SETKEYCODES + APPLET(setkeycodes, setkeycodes_main, _BB_DIR_USR_BIN) +#endif +#if defined(BB_FEATURE_SH_IS_ASH) && defined(BB_ASH) + APPLET_NOUSAGE("sh", ash_main, _BB_DIR_BIN) +#elif defined(BB_FEATURE_SH_IS_HUSH) && defined(BB_HUSH) + APPLET_NOUSAGE("sh", hush_main, _BB_DIR_BIN) +#elif defined(BB_FEATURE_SH_IS_LASH) && defined(BB_LASH) + APPLET_NOUSAGE("sh", lash_main, _BB_DIR_BIN) +#elif defined(BB_FEATURE_SH_IS_MSH) && defined(BB_MSH) + APPLET_NOUSAGE("sh", msh_main, _BB_DIR_BIN) +#endif +#ifdef BB_SLEEP + APPLET(sleep, sleep_main, _BB_DIR_BIN) +#endif +#ifdef BB_SORT + APPLET(sort, sort_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_STTY + APPLET(stty, stty_main, _BB_DIR_BIN) +#endif +#ifdef BB_SWAPONOFF + APPLET(swapoff, swap_on_off_main, _BB_DIR_SBIN) +#endif +#ifdef BB_SWAPONOFF + APPLET(swapon, swap_on_off_main, _BB_DIR_SBIN) +#endif +#ifdef BB_SYNC + APPLET(sync, sync_main, _BB_DIR_BIN) +#endif +#ifdef BB_SYSLOGD + APPLET(syslogd, syslogd_main, _BB_DIR_SBIN) +#endif +#ifdef BB_TAIL + APPLET(tail, tail_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TAR + APPLET(tar, tar_main, _BB_DIR_BIN) +#endif +#ifdef BB_TEE + APPLET(tee, tee_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TELNET + APPLET(telnet, telnet_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TEST + APPLET(test, test_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TFTP + APPLET(tftp, tftp_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TOUCH + APPLET(touch, touch_main, _BB_DIR_BIN) +#endif +#ifdef BB_TR + APPLET(tr, tr_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TRACEROUTE + APPLET(traceroute, traceroute_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TRUE_FALSE + APPLET(true, true_main, _BB_DIR_BIN) +#endif +#ifdef BB_TTY + APPLET(tty, tty_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_UMOUNT + APPLET(umount, umount_main, _BB_DIR_BIN) +#endif +#ifdef BB_UNAME + APPLET(uname, uname_main, _BB_DIR_BIN) +#endif +#ifdef BB_UNIQ + APPLET(uniq, uniq_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_UNIX2DOS + APPLET(unix2dos, dos2unix_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_UPDATE + APPLET(update, update_main, _BB_DIR_SBIN) +#endif +#ifdef BB_UPTIME + APPLET(uptime, uptime_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_USLEEP + APPLET(usleep, usleep_main, _BB_DIR_BIN) +#endif +#ifdef BB_UUDECODE + APPLET(uudecode, uudecode_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_UUENCODE + APPLET(uuencode, uuencode_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_VI + APPLET(vi, vi_main, _BB_DIR_BIN) +#endif +#ifdef BB_WATCHDOG + APPLET(watchdog, watchdog_main, _BB_DIR_SBIN) +#endif +#ifdef BB_WC + APPLET(wc, wc_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_WGET + APPLET(wget, wget_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_WHICH + APPLET(which, which_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_WHOAMI + APPLET(whoami, whoami_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_XARGS + APPLET(xargs, xargs_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_YES + APPLET(yes, yes_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_GUNZIP + APPLET(zcat, gunzip_main, _BB_DIR_BIN) +#endif + +#if !defined(PROTOTYPES) && !defined(MAKE_USAGE) + { 0,NULL,0 } +}; + +#endif diff --git a/busybox/applets/applets.c b/busybox/applets/applets.c new file mode 100644 index 000000000..ca2de87d4 --- /dev/null +++ b/busybox/applets/applets.c @@ -0,0 +1,113 @@ +/* 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 +#include +#include +#include "busybox.h" + +#undef APPLET +#undef APPLET_NOUSAGE +#undef PROTOTYPES +#include "applets.h" + +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); + +extern void show_usage(void) +{ + const char *format_string; + const char *usage_string = usage_messages; + int i; + /* From busybox.c */ + extern int been_there_done_that; + + if (strcmp(applet_using->name, "busybox")==0) { + been_there_done_that=1; + busybox_main(0, NULL); + } + + for (i = applet_using - applets; i > 0; ) { + if (!*usage_string++) { + --i; + } + } + format_string = "%s\n\nUsage: %s %s\n\n"; + if(*usage_string == 0) + format_string = "%s\n\nNo help available.\n\n"; + fprintf(stderr, format_string, + 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; + + recurse_level++; + /* Do a binary search to find the applet entry given the name. */ + if ((applet_using = find_applet_by_name(name)) != NULL) { + applet_name = applet_using->name; + if (argv[1] && strcmp(argv[1], "--help") == 0) { + show_usage(); + } + 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 = 0; +} + + +/* 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 index 000000000..7a220f7b0 --- /dev/null +++ b/busybox/applets/busybox.c @@ -0,0 +1,181 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include "busybox.h" +#ifdef BB_LOCALE_SUPPORT +#include +#endif + +int been_there_done_that = 0; /* Also used in applets.c */ +const char *applet_name; + +#ifdef BB_FEATURE_INSTALLER +/* + * directory table + * this should be consistent w/ the enum, busybox.h::Location, + * or else... + */ +static char* install_dir[] = { + "/", + "/bin", + "/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 char *busybox_fullpath() +{ + 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) { + perror_msg("%s", fpc); + } + free(fpc); + } +} + +#endif /* BB_FEATURE_INSTALLER */ + +int main(int argc, char **argv) +{ + const char *s; + + for (s = applet_name = argv[0]; *s != '\0';) { + if (*s++ == '/') + applet_name = s; + } + + /* Add in a special case hack for a leading hyphen */ + if (**argv == '-' && *(*argv+1)!= '-') { + applet_name = (*argv+1); + } + +#ifdef BB_LOCALE_SUPPORT +#ifdef BB_INIT + if(getpid()!=1) /* Do not set locale for `init' */ +#endif + { + setlocale(LC_ALL, ""); + } +#endif + + run_applet_by_name(applet_name, argc, argv); + error_msg_and_die("applet not found"); +} + + +int busybox_main(int argc, char **argv) +{ + int col = 0, len, i; + +#ifdef BB_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 /* BB_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; + + 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", full_version); + + while (a->name != 0) { + col += + fprintf(stderr, "%s%s", ((col == 0) ? "\t" : ", "), + (a++)->name); + if (col > 60 && 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 */ + 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 index 000000000..4e15e1611 --- /dev/null +++ b/busybox/applets/busybox.mkll @@ -0,0 +1,24 @@ +#!/bin/sh +# Make busybox links list file. + +# input $1: full path to Config.h +# input $2: full path to applets.h +# output (stdout): list of pathnames that should be linked to busybox + +# Maintainer: Larry Doolittle + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +CONFIG_H=${1:-Config.h} +APPLETS_H=${2:-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/busybox.sh b/busybox/applets/busybox.sh new file mode 100755 index 000000000..7c3deb20e --- /dev/null +++ b/busybox/applets/busybox.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +RAW=` \ + gcc -E -dM ${1:-Config.h} | \ + sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\/\1.c/gp;' \ + | tr A-Z a-z | sort +` +test "${RAW}" != "" || exit +if [ -d "$BB_SRC_DIR" ]; then cd $BB_SRC_DIR; fi +# By running $RAW through "ls", we avoid listing +# source files that don't exist. +ls $RAW 2>/dev/null | tr '\n' ' ' + diff --git a/busybox/applets/install.sh b/busybox/applets/install.sh new file mode 100755 index 000000000..d163a2ef8 --- /dev/null +++ b/busybox/applets/install.sh @@ -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/applets/usage.c b/busybox/applets/usage.c new file mode 100644 index 000000000..dfea1f96b --- /dev/null +++ b/busybox/applets/usage.c @@ -0,0 +1,10 @@ +#include "busybox.h" + +const char usage_messages[] = + +#define MAKE_USAGE +#include "usage.h" + +#include "applets.h" + +; diff --git a/busybox/applets/usage.h b/busybox/applets/usage.h new file mode 100644 index 000000000..13759d23f --- /dev/null +++ b/busybox/applets/usage.h @@ -0,0 +1,1829 @@ +#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\n" + +#define ar_trivial_usage \ + "-[ov][ptx] 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\n" + +#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 cat_trivial_usage \ + "[FILE]..." +#define cat_full_usage \ + "Concatenates FILE(s) and prints them to stdout." +#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 \ + "FILE1 [FILE2]" +#define cmp_full_usage \ + "\t-s\tquiet mode - do not print\n" \ + "Compare files." + +#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-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" \ + "\tu\t\tunconditional overwrite\t" \ + "\tF\t\tinput from file\t" + +#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" + +#define date_trivial_usage \ + "[OPTION]... [+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\tdisplay time described by STRING, not `now'\n" \ + "\t-s\t\tSets time described by STRING\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: +, -, /, *, and, or, not, eor.\n" \ + "i.e., 'dc 2 2 add' -> 4, and 'dc 8 8 \\* 2 2 + /' -> 16" +#define dc_example_usage \ + "$ dc 2 2 +\n" \ + "4\n" \ + "$ dc 8 8 \* 2 2 + /\n" \ + "16\n" \ + "$ dc 0 1 and\n" \ + "0\n" \ + "$ dc 0 1 or\n" \ + "1\n" \ + "$ echo 72 9 div 8 mul | dc\n" \ + "64\n" + +#define dd_trivial_usage \ + "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \ + "\t [seek=N] [conv=notrunc|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=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" + + +#ifdef BB_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 \ + "-i package_file\n" + "[-CPru] package_name" +#define dpkg_full_usage \ + "\t-i\tInstall the package\n" \ + "\t-C\tConfigure an unpackaged package\n" \ + "\t-P\tPurge all files of a package\n" \ + "\t-r\tRemove all but the configuration files for a package\n" \ + "\t-u\tUnpack a package, but dont configure it\n" + +#define dpkg_deb_trivial_usage \ + "[-cefItxX] 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-I\tDisplay the control filenamed [argument]\n" \ + "\t-t\tExtract filesystem tree to stdout in tar format\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" + +#define du_trivial_usage \ + "[-ls" USAGE_HUMAN_READABLE("hm") USAGE_NOT_HUMAN_READABLE("") "k] [FILE]..." +#define du_full_usage \ + "Summarizes disk space used for each FILE and/or directory.\n" \ + "Disk space is printed in units of 1024 bytes.\n\n" \ + "Options:\n" \ + "\t-l\tcount sizes many times if hard linked\n" \ + "\t-s\tdisplay only a total for each argument" \ + 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 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 dutmp_trivial_usage \ + "[FILE]" +#define dutmp_full_usage \ + "Dump utmp file format (pipe delimited) from FILE\n" \ + "or stdin to stdout. (i.e., 'dutmp /var/run/utmp')" +#define dutmp_example_usage \ + "$ dutmp /var/run/utmp\n" \ + "8|7||si|||0|0|0|955637625|760097|0\n" \ + "2|0|~|~~|reboot||0|0|0|955637625|782235|0\n" \ + "1|20020|~|~~|runlevel||0|0|0|955637625|800089|0\n" \ + "8|125||l4|||0|0|0|955637629|998367|0\n" \ + "6|245|tty1|1|LOGIN||0|0|0|955637630|998974|0\n" \ + "6|246|tty2|2|LOGIN||0|0|0|955637630|999498|0\n" \ + "7|336|pts/0|vt00andersen|andersen|:0.0|0|0|0|955637763|0|0\n" + +#define echo_trivial_usage \ + "[-neE] [ARG ...]" +#define echo_full_usage \ + "Prints the specified ARGs to stdout\n\n" \ + "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" \ + "$ 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\n" + +#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" + +#ifdef BB_FEATURE_FIND_TYPE + #define USAGE_FIND_TYPE(a) a +#else + #define USAGE_FIND_TYPE(a) +#endif +#ifdef BB_FEATURE_FIND_PERM + #define USAGE_FIND_PERM(a) a +#else + #define USAGE_FIND_PERM(a) +#endif +#ifdef BB_FEATURE_FIND_MTIME + #define USAGE_FIND_MTIME(a) a +#else + #define USAGE_FIND_MTIME(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") +#define find_example_usage \ + "$ find / -name /etc/passwd\n" \ + "/etc/passwd\n" + +#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 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, --unqote 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 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-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 with maximum compression.\n" \ + "When FILE is '-', reads standard input. Implies -c.\n\n" \ + "Options:\n" \ + "\t-c\tWrite output to standard output instead of FILE.gz\n" \ + "\t-d\tdecompress" +#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 \ + "" +#define halt_full_usage \ + "Halt the system." + +#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" +#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 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\t\tShort\n" \ + "\t-i\t\tAddresses for the hostname\n" \ + "\t-d\t\tDNS domain name\n" \ + "\t-F, --file FILE\tUse the contents of FILE to specify the hostname" +#define hostname_example_usage \ + "$ hostname\n" \ + "sage \n" + +#define id_trivial_usage \ + "[OPTIONS]... [USERNAME]" +#define id_full_usage \ + "Print information for USERNAME or the current user\n\n" \ + "Options:\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 (with for -ug)\n" \ + "\t-r\tprints the real user ID instead of the effective ID (with -ug)" +#define id_example_usage \ + "$ id\n" \ + "uid=1000(andersen) gid=1000(andersen)\n" + +#ifdef BB_FEATURE_IFCONFIG_SLIP + #define USAGE_SIOCSKEEPALIVE(a) a +#else + #define USAGE_SIOCSKEEPALIVE(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + #define USAGE_IFCONFIG_MII(a) a +#else + #define USAGE_IFCONFIG_MII(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_HW + #define USAGE_IFCONFIG_HW(a) a +#else + #define USAGE_IFCONFIG_HW(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_STATUS + #define USAGE_IFCONFIG_OPT_A(a) a +#else + #define USAGE_IFCONFIG_OPT_A(a) +#endif + +#define ifconfig_trivial_usage \ + USAGE_IFCONFIG_OPT_A("[-a]") " [
]" +#define ifconfig_full_usage \ + "configure a network interface\n\n" \ + "Options:\n" \ + "\t[[-]broadcast [
]] [[-]pointopoint [
]]\n" \ + "\t[netmask
] [dstaddr
]\n" \ + USAGE_SIOCSKEEPALIVE("\t[outfill ] [keepalive ]\n") \ + "\t" USAGE_IFCONFIG_HW("[hw ether
] ") \ + "[metric ] [mtu ]\n" \ + "\t[[-]trailers] [[-]arp] [[-]allmulti]\n" \ + "\t[multicast] [[-]promisc] [txqueuelen ] [[-]dynamic]\n" \ + USAGE_IFCONFIG_MII("\t[mem_start ] [io_addr ] [irq ]\n") \ + "\t[up|down] ..." + +#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" \ +"\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" \ +" :::\n" \ +"\n" \ +" : \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" \ +" : \n" \ +"\n" \ +" The runlevels field is completely ignored.\n" \ +"\n" \ +" : \n" \ +"\n" \ +" Valid actions include: sysinit, respawn, askfirst, wait, \n" \ +" once, 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. 'ctrlaltdel'\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 reverse order from how they appear in /etc/inittab.\n" \ +"\n" \ +" : \n" \ +"\n" \ +" Specifies the process to be executed and it's 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 tty5\n" \ +" tty5::respawn:/sbin/getty 38400 tty6\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 before rebooting\n" \ +" ::ctrlaltdel:/sbin/reboot\n" \ +" ::shutdown:/bin/umount -a -r\n" \ +" ::shutdown:/sbin/swapoff -a\n" + +#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-L\tLock to prevent simultaneous loads of a module\n" \ + "\t-x\tdo not export externs" + +#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 \ + "[-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." +#define killall_example_usage \ + "$ killall apache\n" + +#define klogd_trivial_usage \ + "-n" +#define klogd_full_usage \ + "Kernel logger.\n"\ + "Options:\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 loadacm_trivial_usage \ + "< mapfile" +#define loadacm_full_usage \ + "Loads an acm from standard input." +#define loadacm_example_usage \ + "$ loadacm < /etc/i18n/acmname\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\tLog using the specified tag (defaults to user name).\n" \ + "\t-p\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 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 \ + "" + +#define logread_full_usage \ + "Shows the messages from syslogd (using circular buffer)." + +#ifdef BB_FEATURE_LS_TIMESTAMPS + #define USAGE_LS_TIMESTAMPS(a) a +#else + #define USAGE_LS_TIMESTAMPS(a) +#endif +#ifdef BB_FEATURE_LS_FILETYPES + #define USAGE_LS_FILETYPES(a) a +#else + #define USAGE_LS_FILETYPES(a) +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS + #define USAGE_LS_FOLLOWLINKS(a) a +#else + #define USAGE_LS_FOLLOWLINKS(a) +#endif +#ifdef BB_FEATURE_LS_RECURSIVE + #define USAGE_LS_RECURSIVE(a) a +#else + #define USAGE_LS_RECURSIVE(a) +#endif +#ifdef BB_FEATURE_LS_SORTFILES + #define USAGE_LS_SORTFILES(a) a +#else + #define USAGE_LS_SORTFILES(a) +#endif +#ifdef BB_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] [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" \ + "\t-k\tprint sizes in kilobytes(default)") USAGE_NOT_HUMAN_READABLE( \ + "\t-k\tprint sizes in kilobytes(compatibility)") + +#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" + +#define md5sum_trivial_usage \ + "[OPTION] [FILE]...\n" \ + "or: md5sum [OPTION] -c [FILE]" +#define md5sum_full_usage \ + "Print or check MD5 checksums.\n\n" \ + "Options:\n" \ + "With no FILE, or when FILE is -, read standard input.\n\n" \ + "\t-b\tread files in binary mode\n" \ + "\t-c\tcheck MD5 sums against given list\n" \ + "\t-t\tread files in text mode (default)\n" \ + "\t-g\tread a string\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 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 \ + "[-q] 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)." +#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 \ + "[FILE ...]" +#define modprobe_full_usage \ + "Used for hight level module loading and unloading." +#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 BB_FEATURE_MOUNT_LOOP + #define USAGE_MOUNT_LOOP(a) a +#else + #define USAGE_MOUNT_LOOP(a) +#endif +#ifdef BB_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\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 rew rewoffline seek setblk setdensity\n" \ + "setpart tell unload unlock weof wset" + +#define mv_trivial_usage \ + "SOURCE DEST\n" \ + "or: mv SOURCE... DIRECTORY" +#define mv_full_usage \ + "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY." +#define mv_example_usage \ + "$ mv /tmp/foo /bin/bar\n" + +#define nc_trivial_usage \ + "[IP] [port]" +#define nc_full_usage \ + "Netcat opens a pipe to IP:port" +#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 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 pidof_trivial_usage \ + "process-name [process-name ...]" +#define pidof_full_usage \ + "Lists the PIDs of all processes with names that match the names on the command line" +#define pidof_example_usage \ + "$ pidof init\n" \ + "1\n" + +#ifndef BB_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" + +#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 \ + "" +#define poweroff_full_usage \ + "Halt the system and request that the kernel shut off the power." + +#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" + +#define ps_trivial_usage \ + "" +#define ps_full_usage \ + "Report process status\n" \ + "\nThis version of ps accepts no options." +#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 \ + "[OPTION] 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 \ + "Read a symbolic link." + +#define reboot_trivial_usage \ + "" +#define reboot_full_usage \ + "Reboot the system." + +#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" \ + "\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\tTry to remove all unused kernel modules." +#define rmmod_example_usage \ + "$ rmmod tulip\n" + +#define route_trivial_usage \ + "[{add|del|flush}]" +#define route_full_usage \ + "Edit the kernel's routing tables" + +#define rpm2cpio_trivial_usage \ + "package.rpm" +#define rpm2cpio_full_usage \ + "Outputs a cpio archive of the rpm file." + +#define rpmunpack_trivial_usage \ + "< package.rpm | gunzip | cpio -idmuv" +#define rpmunpack_full_usage \ + "Extracts an rpm archive." + +#define sed_trivial_usage \ + "[-nef] pattern [files...]" +#define sed_full_usage \ + "Options:\n" \ + "\t-n\t\tsuppress automatic printing of pattern space\n" \ + "\t-e script\tadd the script to the commands to be executed\n" \ + "\t-f scriptfile\tadd the contents of script-file to the commands to be executed\n" \ + "\n" \ + "If no -e or -f is given, the first non-option argument is taken as the\n" \ + "sed script to interpret. All remaining arguments are names of input\n" \ + "files; if no input files are specified, then the standard input is read." +#define sed_example_usage \ + "$ echo "foo" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \ + "bar\n" + +#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 \ + "lash: 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 sleep_trivial_usage \ + "N" +#define sleep_full_usage \ + "Pause for N seconds." +#define sleep_example_usage \ + "$ sleep 2\n" \ + "[2 second delay results]\n" + + +#ifdef BB_FEATURE_SORT_UNIQUE + #define USAGE_SORT_UNIQUE(a) a +#else + #define USAGE_SORT_UNIQUE(a) +#endif +#ifdef BB_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 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 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 BB_FEATURE_REMOTE_LOG + #define USAGE_REMOTE_LOG(a) a +#else + #define USAGE_REMOTE_LOG(a) +#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 NUM\t\tInterval between MARK lines (default=20min, 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)" \ + 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)") +#define syslogd_example_usage \ + "$ syslogd -R masterlog:514\n" \ + "$ syslogd -R 192.168.1.1:601\n" + + +#ifndef BB_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 BB_FEATURE_TAR_CREATE + #define USAGE_TAR_CREATE(a) a +#else + #define USAGE_TAR_CREATE(a) +#endif +#ifdef BB_FEATURE_TAR_EXCLUDE + #define USAGE_TAR_EXCLUDE(a) a +#else + #define USAGE_TAR_EXCLUDE(a) +#endif +#define tar_trivial_usage \ + "-[" USAGE_TAR_CREATE("c") "xtvO] " \ + USAGE_TAR_EXCLUDE("[--exclude FILE] [-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" \ + "\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" +#define tee_example_usage \ + "$ echo "Hello" | tee /tmp/foo\n" \ + "$ cat /tmp/foo\n" \ + "Hello\n" + +#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." + +#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 BB_FEATURE_TFTP_GET + #define USAGE_TFTP_GET(a) a +#else + #define USAGE_TFTP_GET(a) +#endif +#ifdef BB_FEATURE_TFTP_PUT + #define USAGE_TFTP_PUT(a) a +#else + #define USAGE_TFTP_PUT(a) +#endif + +#define tftp_trivial_usage \ + "command SOURCE DEST" +#define tftp_full_usage \ + "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ + "Commands:\n" \ + USAGE_TFTP_GET( \ + "\tget\tGet file from server SOURCE and store to local DEST.\n" \ + ) \ + USAGE_TFTP_PUT( \ + "\tput\tPut local file SOURCE to server DEST.\n" \ + ) \ + "\nWhen naming a server, use the syntax \"server:file\"." + +#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\ + [-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" + +#ifdef BB_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.2.15pre13 #5 Tue Mar 14 16:03:50 MST 2000 i686 unknown\n" + +#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" +#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 update_trivial_usage \ + "[options]" +#define update_full_usage \ + "Periodically flushes filesystem buffers.\n\n" \ + "Options:\n" \ + "\t-S\tforce use of sync(2) instead of flushing\n" \ + "\t-s SECS\tcall sync this often (default 30)\n" \ + "\t-f SECS\tflush some buffers this often (default 5)" + +#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" \ + "\n" \ + "$ uudecode busybox busybox > busybox.uu\n" \ + "$\n" + +#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 watchdog_trivial_usage \ + "DEV" +#define watchdog_full_usage \ + "Periodically write to watchdog device DEV" + +#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[--header 'header: value'] [-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)" + +#define which_trivial_usage \ + "[COMMAND ...]" +#define which_full_usage \ + "Locates a COMMAND." +#define which_example_usage \ + "$ which login\n" \ + "/bin/login\n" + +#define whoami_trivial_usage \ + "" +#define whoami_full_usage \ + "Prints the user name associated with the current effective user id." + +#define xargs_trivial_usage \ + "[COMMAND] [ARGS...]" +#define xargs_full_usage \ + "Executes COMMAND on every item given by standard input." +#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." diff --git a/busybox/ar.c b/busybox/ar.c new file mode 100644 index 000000000..7f3396c8c --- /dev/null +++ b/busybox/ar.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ar implementation for busybox + * + * Copyright (C) 2000 by Glenn McGrath + * Written by Glenn McGrath 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 + * + */ +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int ar_main(int argc, char **argv) +{ + FILE *src_stream = NULL; + char **extract_names = NULL; + char ar_magic[8]; + int extract_function = extract_unconditional; + int opt; + int num_of_entries = 0; + extern off_t archive_offset; + + while ((opt = getopt(argc, argv, "ovtpx")) != -1) { + switch (opt) { + case 'o': + extract_function |= extract_preserve_date; + break; + case 'v': + extract_function |= extract_verbose_list; + break; + case 't': + extract_function |= extract_list; + break; + case 'p': + extract_function |= extract_to_stdout; + break; + case 'x': + extract_function |= extract_all_to_fs; + break; + default: + show_usage(); + } + } + + /* check the src filename was specified */ + if (optind == argc) { + show_usage(); + } + + src_stream = xfopen(argv[optind++], "r"); + + /* check ar magic */ + fread(ar_magic, 1, 8, src_stream); + archive_offset = 8; + if (strncmp(ar_magic,"!",7) != 0) { + error_msg_and_die("invalid magic"); + } + + /* Create a list of files to extract */ + while (optind < argc) { + extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); + extract_names[num_of_entries] = xstrdup(argv[optind]); + num_of_entries++; + extract_names[num_of_entries] = NULL; + optind++; + } + + unarchive(src_stream, stdout, &get_header_ar, extract_function, "./", extract_names); + return EXIT_SUCCESS; +} diff --git a/busybox/archival/ar.c b/busybox/archival/ar.c new file mode 100644 index 000000000..7f3396c8c --- /dev/null +++ b/busybox/archival/ar.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ar implementation for busybox + * + * Copyright (C) 2000 by Glenn McGrath + * Written by Glenn McGrath 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 + * + */ +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int ar_main(int argc, char **argv) +{ + FILE *src_stream = NULL; + char **extract_names = NULL; + char ar_magic[8]; + int extract_function = extract_unconditional; + int opt; + int num_of_entries = 0; + extern off_t archive_offset; + + while ((opt = getopt(argc, argv, "ovtpx")) != -1) { + switch (opt) { + case 'o': + extract_function |= extract_preserve_date; + break; + case 'v': + extract_function |= extract_verbose_list; + break; + case 't': + extract_function |= extract_list; + break; + case 'p': + extract_function |= extract_to_stdout; + break; + case 'x': + extract_function |= extract_all_to_fs; + break; + default: + show_usage(); + } + } + + /* check the src filename was specified */ + if (optind == argc) { + show_usage(); + } + + src_stream = xfopen(argv[optind++], "r"); + + /* check ar magic */ + fread(ar_magic, 1, 8, src_stream); + archive_offset = 8; + if (strncmp(ar_magic,"!",7) != 0) { + error_msg_and_die("invalid magic"); + } + + /* Create a list of files to extract */ + while (optind < argc) { + extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); + extract_names[num_of_entries] = xstrdup(argv[optind]); + num_of_entries++; + extract_names[num_of_entries] = NULL; + optind++; + } + + unarchive(src_stream, stdout, &get_header_ar, extract_function, "./", extract_names); + return EXIT_SUCCESS; +} diff --git a/busybox/archival/cpio.c b/busybox/archival/cpio.c new file mode 100644 index 000000000..7f68715eb --- /dev/null +++ b/busybox/archival/cpio.c @@ -0,0 +1,95 @@ +/* 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 +#include +#include +#include +#include +#include "busybox.h" + +extern int cpio_main(int argc, char **argv) +{ + FILE *src_stream = stdin; + char **extract_names = NULL; + int extract_function = 0; + int num_of_entries = 0; + int opt = 0; + mode_t oldmask = 0; + + while ((opt = getopt(argc, argv, "idmuvtF:")) != -1) { + switch (opt) { + case 'i': // extract + extract_function |= extract_all_to_fs; + break; + case 'd': // create _leading_ directories + extract_function |= extract_create_leading_dirs; + oldmask = umask(077); /* Make make_directory act like GNU cpio */ + break; + case 'm': // preserve modification time + extract_function |= extract_preserve_date; + break; + case 'v': // verbosly list files + extract_function |= extract_verbose_list; + break; + case 'u': // unconditional + extract_function |= extract_unconditional; + break; + case 't': // list files + extract_function |= extract_list; + break; + case 'F': + src_stream = xfopen(optarg, "r"); + break; + default: + show_usage(); + } + } + + if ((extract_function & extract_all_to_fs) && (extract_function & extract_list)) { + extract_function ^= extract_all_to_fs; /* If specify t, don't extract*/ + } + + if ((extract_function & extract_all_to_fs) && (extract_function & extract_verbose_list)) { + /* The meaning of v changes on extract */ + extract_function ^= extract_verbose_list; + extract_function |= extract_list; + } + + while (optind < argc) { + extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); + extract_names[num_of_entries] = xstrdup(argv[optind]); + num_of_entries++; + extract_names[num_of_entries] = NULL; + optind++; + } + + unarchive(src_stream, stdout, &get_header_cpio, extract_function, "./", extract_names); + if (oldmask) { + umask(oldmask); /* Restore umask if we changed it */ + } + return EXIT_SUCCESS; +} + diff --git a/busybox/archival/dpkg.c b/busybox/archival/dpkg.c new file mode 100644 index 000000000..48c392894 --- /dev/null +++ b/busybox/archival/dpkg.c @@ -0,0 +1,1445 @@ +/* + * Mini dpkg implementation for busybox. + * This is not meant as a replacemnt for dpkg + * + * 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 dont + * 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. + * - The .control file is extracted and kept in the info dir. + * - When installing a package the Status: field is placed at the end of the + * section, rather than just after the Package: field. + * - Packages with previously unknown status are inserted at the begining of + * the status file + * + * Bugs that need to be fixed + * - (unknown, please let me know when you find any) + * + */ + +#include +#include +#include +#include +#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 leaway 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 dependecies 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, dont 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] = 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 = xstrdup(""); + } + if (version2 != NULL) { + version2 = 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 = xstrndup(&version1[len1], tmp_int); + len1 += tmp_int; + tmp_int = strcspn(&version2[len2], "0123456789"); + name2_char = 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 = xstrndup(&version1[len1], tmp_int); + len1 += tmp_int; + tmp_int = strspn(&version2[len2], "0123456789"); + name2_char = 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 = xstrdup(ver1_ptr); + upstream_ver2 = 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); +} + +/* + * Create one new node and one new edge for every dependency. + */ +void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned int edge_type) +{ + char *line = xstrdup(whole_line); + char *line2; + char *line_ptr1 = NULL; + char *line_ptr2 = NULL; + char *field; + char *field2; + char *version; + edge_t *edge; + int offset_ch; + int type; + + field = strtok_r(line, ",", &line_ptr1); + do { + line2 = xstrdup(field); + field2 = strtok_r(line2, "|", &line_ptr2); + if ((edge_type == EDGE_DEPENDS) && (strcmp(field, field2) != 0)) { + type = EDGE_OR_DEPENDS; + } + else if ((edge_type == EDGE_PRE_DEPENDS) && (strcmp(field, field2) != 0)) { + type = EDGE_OR_PRE_DEPENDS; + } else { + type = edge_type; + } + + do { + edge = (edge_t *) xmalloc(sizeof(edge_t)); + edge->type = 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 charactors */ + 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 { + 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); + + /* link the new edge to the current node */ + parent_node->num_of_edges++; + parent_node->edge = xrealloc(parent_node->edge, sizeof(edge_t) * (parent_node->num_of_edges + 1)); + parent_node->edge[parent_node->num_of_edges - 1] = 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) +{ + int i; + if (node != NULL) { + for (i = 0; i < node->num_of_edges; i++) { + if (node->edge[i] != NULL) { + free(node->edge[i]); + } + } + if (node->edge != NULL) { + free(node->edge); + } + if (node != NULL) { + free(node); + } + } +} + +unsigned int fill_package_struct(char *control_buffer) +{ + common_node_t *new_node = (common_node_t *) xcalloc(1, sizeof(common_node_t)); + + char **field_name = xmalloc(sizeof(char *)); + char **field_value = xmalloc(sizeof(char *)); + 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) { + 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 !! + } + + if (strcmp(*field_name, "Package") == 0) { + new_node->name = search_name_hashtable(*field_value); + } + else if (strcmp(*field_name, "Version") == 0) { + new_node->version = search_name_hashtable(*field_value); + } + else if (strcmp(*field_name, "Pre-Depends") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_PRE_DEPENDS); + } + else if (strcmp(*field_name, "Depends") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_DEPENDS); + } + else if (strcmp(*field_name, "Replaces") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_REPLACES); + } + else if (strcmp(*field_name, "Provides") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_PROVIDES); + } + else if (strcmp(*field_name, "Conflicts") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_CONFLICTS); + } + else if (strcmp(*field_name, "Suggests") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_SUGGESTS); + } + else if (strcmp(*field_name, "Recommends") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_RECOMMENDS); + } + else if (strcmp(*field_name, "Enhances") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_ENHANCES); + } +fill_package_struct_cleanup: + if (*field_name) { + free(*field_name); + } + if (*field_value) { + free(*field_value); + } + } + 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 seperating spaces */ + status_string += strspn(status_string, " "); + } + len = strcspn(status_string, " \n\0"); + state_sub_string = 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: + 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; +} + +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 = 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 = 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; +} + + +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); +} + +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 = xfopen("/var/lib/dpkg/status", "r"); + FILE *new_status_file = 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) { + tmp_string = strstr(control_buffer, "Package:") + 8; + tmp_string += strspn(tmp_string, " \n\t"); + package_name = 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 = 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) { + 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 == FALSE) { + fprintf(new_status_file, "%s\n\n", control_buffer); + } + + if (status_from_file != NULL) { + 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 seperate 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) { + error_msg_and_die("Couldnt create backup status file"); + } + /* Its ok if renaming the status file fails becasue status + * file doesnt exist, maybe we are starting from scratch */ + error_msg("No status file found, creating new one"); + } + + if (rename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status") == -1) { + error_msg_and_die("DANGER: Couldnt create status file, you need to manually repair your status file"); + } +} + +int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count) +{ + int *conflicts = NULL; + int conflicts_num = 0; + int state_status; + int state_flag; + int state_want; + unsigned int status_package_num; + int i = deb_start; + int j, k; + + /* 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 */ + for (i = 0; i < conflicts_num; i++) { + /* Check for conflicts */ + for (j = 0; j < STATUS_HASH_PRIME; j++) { + if (status_hashtable[j] == NULL) { + continue; + } + state_flag = get_status(j, 2); + state_status = get_status(j, 3); + if ((state_status != search_name_hashtable("installed")) + && (state_flag != search_name_hashtable("want-install"))) { + continue; + } + status_package_num = status_hashtable[j]->package; + for (k = 0; k < package_hashtable[status_package_num]->num_of_edges; k++) { + const edge_t *package_edge = package_hashtable[status_package_num]->edge[k]; + if (package_edge->type != EDGE_CONFLICTS) { + continue; + } + if (package_edge->name != package_hashtable[conflicts[i]]->name) { + continue; + } + /* There is a conflict against the package name + * check if version conflict as well */ + if (test_version(package_hashtable[deb_file[i]->package]->version, + package_edge->version, package_edge->operator)) { + error_msg_and_die("Package %s conflict with %s", + name_hashtable[package_hashtable[deb_file[i]->package]->name], + name_hashtable[package_hashtable[status_package_num]->name]); + } + } + } + } + + /* Check dependendcies */ + i = 0; + while (deb_file[i] != NULL) { + const common_node_t *package_node = package_hashtable[deb_file[i]->package]; + int status_num = 0; + + for (j = 0; j < package_hashtable[deb_file[i]->package]->num_of_edges; j++) { + const edge_t *package_edge = package_node->edge[j]; + const unsigned int package_num = search_package_hashtable(package_edge->name, + package_edge->version, package_edge->operator); + + status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]); + state_status = get_status(status_num, 3); + state_want = get_status(status_num, 1); + switch (package_edge->type) { + case(EDGE_PRE_DEPENDS): + case(EDGE_OR_PRE_DEPENDS): + /* It must be already installed */ + /* NOTE: This is untested, nothing apropriate in my status file */ + if ((package_hashtable[package_num] == NULL) || (state_status != search_name_hashtable("installed"))) { + error_msg_and_die("Package %s pre-depends on %s, but it is not installed", + name_hashtable[package_node->name], + name_hashtable[package_edge->name]); + } + break; + case(EDGE_DEPENDS): + case(EDGE_OR_DEPENDS): + /* It must be already installed, or to be installed */ + if ((package_hashtable[package_num] == NULL) || + ((state_status != search_name_hashtable("installed")) && + (state_want != search_name_hashtable("want_install")))) { + error_msg_and_die("Package %s depends on %s, but it is not installed, or flaged to be installed", + name_hashtable[package_node->name], + name_hashtable[package_edge->name]); + } + break; + } + } + i++; + } + free(conflicts); + return(TRUE); +} + +char **create_list(const char *filename) +{ + FILE *list_stream; + char **file_list = xmalloc(sizeof(char *)); + char *line = NULL; + char *last_char; + int length = 0; + int count = 0; + + /* dont use [xw]fopen here, handle error ourself */ + list_stream = fopen(filename, "r"); + if (list_stream == NULL) { + *file_list = NULL; + return(file_list); + } + while (getline(&line, &length, list_stream) != -1) { + file_list = xrealloc(file_list, sizeof(char *) * (length + 1)); + last_char = last_char_is(line, '\n'); + if (last_char) { + *last_char = '\0'; + } + file_list[count] = xstrdup(line); + free(line); + count++; + length = 0; + } + 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; + + 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) { + free(script_path); + return(EXIT_SUCCESS); + } else { + free(script_path); + return(system(script_path)); + } +} + +void all_control_list(char **remove_files, const char *package_name) +{ + const char *all_extensions[11] = {"preinst", "postinst", "prerm", "postrm", + "list", "md5sums", "shlibs", "conffiles", "config", "templates", NULL }; + int i; + + /* Create a list of all /var/lib/dpkg/info/ files */ + for(i = 0; i < 10; i++) { + remove_files[i] = xmalloc(strlen(package_name) + strlen(all_extensions[i]) + 21); + sprintf(remove_files[i], "/var/lib/dpkg/info/%s.%s", package_name, all_extensions[i]); + } + remove_files[10] = NULL; +} + +void remove_package(const unsigned int package_num) +{ + const char *package_name = name_hashtable[package_hashtable[package_num]->name]; + 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; + + printf("Removing %s ...\n", package_name); + + /* run prerm script */ + return_value = run_package_script(package_name, "prem"); + if (return_value == -1) { + error_msg_and_die("script failed, prerm failure"); + } + + /* Create a list of files to remove, and a seperate 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) == TRUE); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = xmalloc(11); + all_control_list(remove_files, package_name); + + /* Create a list of files in /var/lib/dpkg/info/.* to keep */ + exclude_files = xmalloc(sizeof(char*) * 3); + exclude_files[0] = 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; + + remove_file_array(remove_files, exclude_files); + + /* rename .conffile to .list */ + rename(conffile_name, list_name); + + /* Change package status */ + set_status(status_num, "deinstall", 1); + 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 unsigned int status_num = search_status_hashtable(package_name); + char **remove_files; + char **exclude_files; + char list_name[strlen(package_name) + 25]; + + /* run prerm script */ + if (run_package_script(package_name, "prerm") == -1) { + 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(1); + exclude_files[0] = NULL; + + /* Some directories cant be removed straight away, so do multiple passes */ + while (remove_file_array(remove_files, exclude_files) == TRUE); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = xmalloc(11); + all_control_list(remove_files, package_name); + remove_file_array(remove_files, exclude_files); + + /* run postrm script */ + if (run_package_script(package_name, "postrm") == -1) { + error_msg_and_die("postrm fialure.. set status to what?"); + } + + /* Change package status */ + set_status(status_num, "purge", 1); + set_status(status_num, "not-installed", 3); +} + +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]->status; + + FILE *out_stream; + char *info_prefix; + + /* 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); + } else { + printf("Unpacking %s (from %s) ...\n", package_name, deb_file->filename); + } + + /* Extract control.tar.gz to /var/lib/dpkg/info/.filename */ + info_prefix = (char *) xmalloc(sizeof(package_name) + 20 + 4 + 1); + sprintf(info_prefix, "/var/lib/dpkg/info/%s.", package_name); + deb_extract(deb_file->filename, stdout, (extract_quiet | extract_control_tar_gz | extract_all_to_fs), info_prefix, NULL); + + /* Extract data.tar.gz to the root directory */ + deb_extract(deb_file->filename, stdout, (extract_quiet | extract_data_tar_gz | extract_all_to_fs), "/", NULL); + + /* Create the list file */ + strcat(info_prefix, "list"); + out_stream = xfopen(info_prefix, "w"); + deb_extract(deb_file->filename, out_stream, (extract_quiet | extract_data_tar_gz | extract_list), NULL, NULL); + 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); + int return_value; + + printf("Setting up %s (%s)\n", package_name, package_version); + + /* Run the preinst prior to extracting */ + return_value = run_package_script(package_name, "postinst"); + if (return_value == -1) { + /* TODO: handle failure gracefully */ + 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); +} + +extern int dpkg_main(int argc, char **argv) +{ + deb_file_t **deb_file = NULL; + status_node_t *status_node; + char opt = 0; + 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; + } + case 'i': + dpkg_opt |= dpkg_opt_install; + dpkg_opt |= dpkg_opt_filename; + break; + case 'l': + dpkg_opt |= dpkg_opt_list_installed; + 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: + show_usage(); + } + } + + if ((argc == optind) || (dpkg_opt == 0)) { + show_usage(); + } + + puts("(Reading database ... xxxxx files and directories installed.)"); + index_status_file("/var/lib/dpkg/status"); + + /* Read arguments and store relevant info in structs */ + deb_file = xmalloc(sizeof(deb_file_t)); + while (optind < argc) { + deb_file[deb_count] = (deb_file_t *) xmalloc(sizeof(deb_file_t)); + if (dpkg_opt & dpkg_opt_filename) { + deb_file[deb_count]->filename = xstrdup(argv[optind]); + deb_file[deb_count]->control_file = deb_extract(argv[optind], stdout, (extract_control_tar_gz | extract_one_to_buffer), NULL, "./control"); + if (deb_file[deb_count]->control_file == NULL) { + error_msg_and_die("Couldnt extract control file"); + } + package_num = fill_package_struct(deb_file[deb_count]->control_file); + + if (package_num == -1) { + 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)) { + status_node = (status_node_t *) xmalloc(sizeof(status_node_t)); + status_node->package = deb_file[deb_count]->package; + /* use 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_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + status_hashtable[status_num] = status_node; + } + } + 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) { + error_msg_and_die("Package %s is uninstalled or unknown\n", argv[optind]); + } + state_status = get_status(search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]), 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)) { + error_msg_and_die("%s is already removed.", name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + } + } + 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) { + error_msg_and_die("%s is already purged.", name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + } + } + } + deb_count++; + optind++; + } + deb_file[deb_count] = NULL; + + /* Check that the deb file arguments are installable */ + /* TODO: check dependencies before removing */ + if ((dpkg_opt & dpkg_opt_force_ignore_depends) != dpkg_opt_force_ignore_depends) { + if (!check_deps(deb_file, 0, deb_count)) { + error_msg_and_die("Dependency check failed"); + } + } + + for (i = 0; i < deb_count; i++) { + /* Remove or purge packages */ + if (dpkg_opt & dpkg_opt_remove) { + remove_package(deb_file[i]->package); + } + 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]); + configure_package(deb_file[i]); + } + else if (dpkg_opt & dpkg_opt_configure) { + 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++) { + if (name_hashtable[i] != NULL) { + free(name_hashtable[i]); + } + } + + for (i = 0; i < PACKAGE_HASH_PRIME; i++) { + free_package(package_hashtable[i]); + } + + for (i = 0; i < STATUS_HASH_PRIME; i++) { + if (status_hashtable[i] != NULL) { + free(status_hashtable[i]); + } + } + + return(EXIT_FAILURE); +} + diff --git a/busybox/archival/dpkg_deb.c b/busybox/archival/dpkg_deb.c new file mode 100644 index 000000000..a933c6948 --- /dev/null +++ b/busybox/archival/dpkg_deb.c @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 +#include +#include +#include "busybox.h" + +extern int dpkg_deb_main(int argc, char **argv) +{ + char *prefix = NULL; + char *filename = NULL; + char *output_buffer = NULL; + int opt = 0; + int arg_type = 0; + int deb_extract_funct = extract_create_leading_dirs | extract_unconditional; + + const int arg_type_prefix = 1; + const int arg_type_field = 2; + const int arg_type_filename = 4; +// const int arg_type_un_ar_gz = 8; + + while ((opt = getopt(argc, argv, "ceftXxI")) != -1) { + switch (opt) { + case 'c': + deb_extract_funct |= extract_data_tar_gz; + deb_extract_funct |= extract_verbose_list; + break; + case 'e': + arg_type = arg_type_prefix; + deb_extract_funct |= extract_control_tar_gz; + deb_extract_funct |= extract_all_to_fs; + break; + case 'f': + arg_type = arg_type_field; + deb_extract_funct |= extract_control_tar_gz; + deb_extract_funct |= extract_one_to_buffer; + filename = xstrdup("./control"); + break; + case 't': /* --fsys-tarfile, i just made up this short name */ + /* Integrate the functionality needed with some code from ar.c */ + error_msg_and_die("Option disabled"); +// arg_type = arg_type_un_ar_gz; + break; + case 'X': + arg_type = arg_type_prefix; + deb_extract_funct |= extract_data_tar_gz; + deb_extract_funct |= extract_all_to_fs; + deb_extract_funct |= extract_list; + case 'x': + arg_type = arg_type_prefix; + deb_extract_funct |= extract_data_tar_gz; + deb_extract_funct |= extract_all_to_fs; + break; + case 'I': + arg_type = arg_type_filename; + deb_extract_funct |= extract_control_tar_gz; + deb_extract_funct |= extract_one_to_buffer; + break; + default: + show_usage(); + } + } + + if (optind == argc) { + show_usage(); + } + + /* Workout where to extract the files */ + if (arg_type == arg_type_prefix) { + /* argument is a dir name */ + if ((optind + 1) == argc ) { + prefix = xstrdup("./DEBIAN/"); + } else { + prefix = (char *) xmalloc(strlen(argv[optind + 1]) + 2); + strcpy(prefix, argv[optind + 1]); + /* Make sure the directory has a trailing '/' */ + if (last_char_is(prefix, '/') == NULL) { + strcat(prefix, "/"); + } + } + mkdir(prefix, 0777); + } + + if (arg_type == arg_type_filename) { + if ((optind + 1) != argc) { + filename = xstrdup(argv[optind + 1]); + } else { + error_msg_and_die("-I currently requires a filename to be specified"); + } + } + + output_buffer = deb_extract(argv[optind], stdout, deb_extract_funct, prefix, filename); + + if ((arg_type == arg_type_filename) && (output_buffer != NULL)) { + puts(output_buffer); + } + else if (arg_type == arg_type_field) { + char *field = NULL; + char *name; + char *value; + int field_start = 0; + + while (1) { + field_start += read_package_field(&output_buffer[field_start], &name, &value); + if (name == NULL) { + break; + } + if (strcmp(name, argv[optind + 1]) == 0) { + puts(value); + } + free(field); + } + } + + return(EXIT_SUCCESS); +} diff --git a/busybox/archival/gunzip.c b/busybox/archival/gunzip.c new file mode 100644 index 000000000..430bc630e --- /dev/null +++ b/busybox/archival/gunzip.c @@ -0,0 +1,183 @@ +/* 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 + * based on gzip sources + * + * Adjusted further by Erik Andersen , + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +extern int gunzip_main(int argc, char **argv) +{ + FILE *in_file = stdin; + FILE *out_file = NULL; + struct stat stat_buf; + + char *if_name = NULL; + char *of_name = NULL; + char *delete_file_name = NULL; + + const int gunzip_to_stdout = 1; + const int gunzip_force = 2; + const int gunzip_test = 4; + + int flags = 0; + int opt = 0; + int delete_old_file = FALSE; + + /* if called as zcat */ + if (strcmp(applet_name, "zcat") == 0) + flags |= gunzip_to_stdout; + + while ((opt = getopt(argc, argv, "ctfhdq")) != -1) { + switch (opt) { + case 'c': + flags |= gunzip_to_stdout; + break; + case 'f': + flags |= gunzip_force; + break; + case 't': + flags |= gunzip_test; + break; + case 'd': /* Used to convert gzip to gunzip. */ + break; + case 'q': + error_msg("-q option not supported, ignored"); + break; + case 'h': + default: + show_usage(); /* exit's inside usage */ + } + } + + /* Set input filename and number */ + if (argv[optind] == NULL || strcmp(argv[optind], "-") == 0) { + flags |= gunzip_to_stdout; + } else { + if_name = strdup(argv[optind]); + /* Open input file */ + in_file = xfopen(if_name, "r"); + + /* set the buffer size */ + setvbuf(in_file, NULL, _IOFBF, 0x8000); + + /* Get the time stamp on the input file. */ + if (stat(if_name, &stat_buf) < 0) { + error_msg_and_die("Couldn't stat file %s", if_name); + } + } + + /* Check that the input is sane. */ + if (isatty(fileno(in_file)) && (flags & gunzip_force) == 0) + error_msg_and_die("compressed data not read from terminal. Use -f to force it."); + + /* Set output filename and number */ + if (flags & gunzip_test) { + out_file = xfopen("/dev/null", "w"); /* why does test use filenum 2 ? */ + } else if (flags & gunzip_to_stdout) { + out_file = stdout; + } else { + char *extension; + int length = strlen(if_name); + + delete_old_file = TRUE; + extension = strrchr(if_name, '.'); + if (extension && strcmp(extension, ".gz") == 0) { + length -= 3; + } else if (extension && strcmp(extension, ".tgz") == 0) { + length -= 4; + } else { + error_msg_and_die("Invalid extension"); + } + of_name = (char *) xcalloc(sizeof(char), length + 1); + strncpy(of_name, if_name, length); + + /* Open output file */ + out_file = xfopen(of_name, "w"); + + /* Set permissions on the file */ + chmod(of_name, stat_buf.st_mode); + } + + /* do the decompression, and cleanup */ + if (unzip(in_file, out_file) == 0) { + /* Success, remove .gz file */ + delete_file_name = if_name; + } else { + /* remove failed attempt */ + delete_file_name = of_name; + } + + fclose(out_file); + fclose(in_file); + + if (delete_old_file == TRUE) { + if (unlink(delete_file_name) < 0) { + error_msg_and_die("Couldnt remove %s", delete_file_name); + } + } + + free(of_name); + + return(EXIT_SUCCESS); +} diff --git a/busybox/archival/gzip.c b/busybox/archival/gzip.c new file mode 100644 index 000000000..54bb72745 --- /dev/null +++ b/busybox/archival/gzip.c @@ -0,0 +1,2550 @@ +/* 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 + * "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 , + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +static int method; /* compression method */ + +/* 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*)calloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \ + if (array == NULL) error_msg(memory_exhausted); \ + } +# define FREE(array) {if (array != NULL) 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 16 bit value, lsb first */ +#define put_short(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)); \ + } \ +} + +/* Output a 32 bit value to the bit stream, lsb first */ +#define put_long(n) { \ + put_short((n) & 0xffff); \ + put_short(((ulg)(n)) >> 16); \ +} + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) 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 crc_table_empty = 1; + +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 char ifname[MAX_PATH_LEN]; /* input file name */ +static char ofname[MAX_PATH_LEN]; /* output file name */ +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 */ + +/* ======================================================================== + * 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_error_msg() +{ + fprintf(stderr, "\n"); + perror(""); + 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_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_table_empty) { + unsigned long csr; /* crc shift register */ + unsigned long e=0; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static const int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* Make exclusive-or pattern from polynomial (0xedb88320) */ + for (i = 0; i < sizeof(p)/sizeof(int); i++) + e |= 1L << (31 - p[i]); + + /* Compute and print table of CRC's, five per line */ + crc_32_tab[0] = 0x00000000L; + 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. + * + * ACKNOWLEDGEMENTS + * + * 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-1 +# error cannot overlay head with tab_prefix1 +#endif +#define HASH_SIZE (unsigned)(1<= 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)<= 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) { + fprintf(stderr, + " start %d, match %d, length %d\n", start, match, length); + error_msg("invalid match"); + } + if (verbose > 1) { + fprintf(stderr, "\\[%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 main (argc, argv) +// int argc; +// char **argv; +int gzip_main(int argc, char **argv) +{ + int result; + int inFileNum; + int outFileNum; + struct stat statBuf; + char *delFileName; + int tostdout = 0; + int fromstdin = 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 BB_GUNZIP + case 'd': + optind = 1; + return gunzip_main(argc, argv); +#endif + default: + show_usage(); + } + } + if ((optind == argc) || (strcmp(argv[optind], "-") == 0)) { + fromstdin = 1; + tostdout = 1; + } + + if (isatty(fileno(stdout)) && tostdout==1 && force==0) + error_msg_and_die( "compressed data not written to terminal. Use -f to force it."); + + 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); + + if (fromstdin == 1) { + strcpy(ofname, "stdin"); + + inFileNum = fileno(stdin); + time_stamp = 0; /* time unknown by default */ + ifile_size = -1L; /* convention for unknown size */ + } else { + /* Open up the input file */ + strncpy(ifname, argv[optind], MAX_PATH_LEN); + + /* Open input file */ + inFileNum = open(ifname, O_RDONLY); + if (inFileNum < 0) + perror_msg_and_die("%s", ifname); + /* Get the time stamp on the input file. */ + if (stat(ifname, &statBuf) < 0) + perror_msg_and_die("%s", ifname); + time_stamp = statBuf.st_ctime; + ifile_size = statBuf.st_size; + } + + + if (tostdout == 1) { + /* And get to work */ + strcpy(ofname, "stdout"); + outFileNum = fileno(stdout); + + clear_bufs(); /* clear input and output buffers */ + part_nb = 0; + + /* Actually do the compression/decompression. */ + zip(inFileNum, outFileNum); + + } else { + + /* And get to work */ + strncpy(ofname, ifname, MAX_PATH_LEN - 4); + strcat(ofname, ".gz"); + + + /* Open output fille */ +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) + outFileNum = open(ofname, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW); +#else + outFileNum = open(ofname, O_RDWR | O_CREAT | O_EXCL); +#endif + if (outFileNum < 0) + perror_msg_and_die("%s", ofname); + /* Set permissions on the file */ + fchmod(outFileNum, statBuf.st_mode); + + clear_bufs(); /* clear input and output buffers */ + part_nb = 0; + + /* Actually do the compression/decompression. */ + result = zip(inFileNum, outFileNum); + close(outFileNum); + close(inFileNum); + /* Delete the original file */ + if (result == OK) + delFileName = ifname; + else + delFileName = ofname; + + if (unlink(delFileName) < 0) + perror_msg_and_die("%s", delFileName); + } + + 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 */ + + +static const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = { 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 }; + +static const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = { 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 }; + +static const int extra_blbits[BL_CODES] /* extra bits for each bit length code */ += { 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 int *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) fprintf(stderr,"\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 int *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) { + fprintf(stderr, "\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) + 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) { + 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 */ + +/* =========================================================================== + * 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_byte(GZIP_MAGIC[0]); /* magic header */ + put_byte(GZIP_MAGIC[1]); + put_byte(DEFLATED); /* compression method */ + + put_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/decompress_unzip.c b/busybox/archival/libunarchive/decompress_unzip.c new file mode 100644 index 000000000..ee746216d --- /dev/null +++ b/busybox/archival/libunarchive/decompress_unzip.c @@ -0,0 +1,1026 @@ +/* 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 + * based on gzip sources + * + * Adjusted further by Erik Andersen , + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "libbb.h" + +static FILE *in_file, *out_file; + +/* these are freed by gz_close */ +static unsigned char *window; +static unsigned long *crc_table; + +static unsigned long crc = 0xffffffffL; /* shift register contents */ + +/* Return codes from gzip */ +static const int ERROR = 1; + +/* + * window size--must be a power of two, and + * at least 32K for zip's deflate method + */ +static const int WSIZE = 0x8000; + +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ +static const int BMAX = 16; /* maximum bit length of any code (16 for explode) */ +static const int N_MAX = 288; /* maximum number of codes in any set */ + +static long bytes_out; /* number of output bytes */ +static unsigned long outcnt; /* bytes in output buffer */ + +static unsigned hufts; /* track memory usage */ +static unsigned long bb; /* bit buffer */ +static unsigned bk; /* bits in bit buffer */ + +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 const unsigned short mask_bits[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +//static int error_number = 0; +/* ======================================================================== + * Signal and error handler. + */ + +static void abort_gzip() +{ + error_msg("gzip aborted\n"); + exit(ERROR); +} + +static void make_crc_table() +{ + unsigned long table_entry; /* crc shift register */ + unsigned long poly = 0; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + crc_table = (unsigned long *) malloc(256 * sizeof(unsigned long)); + + /* Make exclusive-or pattern from polynomial (0xedb88320) */ + for (i = 0; i < sizeof(p)/sizeof(int); i++) + poly |= 1L << (31 - p[i]); + + /* Compute and print table of CRC's, five per line */ + for (i = 0; i < 256; i++) { + 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--) { + table_entry = table_entry & 1 ? (table_entry >> 1) ^ poly : table_entry >> 1; + } + crc_table[i]=table_entry; + } +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void flush_window(void) +{ + int n; + + if (outcnt == 0) + return; + + for (n = 0; n < outcnt; n++) { + crc = crc_table[((int) crc ^ (window[n])) & 0xff] ^ (crc >> 8); + } + + if (fwrite(window, 1, outcnt, out_file) != outcnt) { + error_msg_and_die("Couldnt write"); + } + bytes_out += (unsigned long) outcnt; + outcnt = 0; +} + +/* + * 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, *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 short *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 */ + if ((q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t))) == NULL) { + if (h) { + huft_free(u[0]); + } + return 3; /* not enough memory */ + } + hufts += z + 1; /* track memory usage */ + *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 *tl, huft_t *td, int bl, int bd) +{ + register unsigned long e; /* table entry flag/number of extra bits */ + unsigned long n, d; /* length and index for copy */ + unsigned long w; /* current window position */ + huft_t *t; /* pointer to table entry */ + unsigned ml, md; /* masks for bl and bd bits */ + register unsigned long b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = outcnt; /* initialize window position */ + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + for (;;) { /* do until end of block */ + while (k < (unsigned) bl) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + if ((e = (t = tl + ((unsigned) b & ml))->e) > 16) + do { + if (e == 99) { + return 1; + } + b >>= t->b; + k -= t->b; + e -= 16; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + } 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 */ + window[w++] = (unsigned char) t->v.n; + if (w == WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + } else { /* it's an EOB or a length */ + + /* exit if end of block */ + if (e == 15) { + break; + } + + /* get length of block to copy */ + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + n = t->v.n + ((unsigned) b & mask_bits[e]); + b >>= e; + k -= e; + + /* decode distance of block to copy */ + while (k < (unsigned) bd) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + + if ((e = (t = td + ((unsigned) b & md))->e) > 16) + do { + if (e == 99) + return 1; + b >>= t->b; + k -= t->b; + e -= 16; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); + b >>= t->b; + k -= t->b; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + d = w - t->v.n - ((unsigned) b & mask_bits[e]); + b >>= e; + k -= e; + + /* do the copy */ + do { + n -= (e = (e = WSIZE - ((d &= WSIZE - 1) > w ? d : w)) > n ? n : e); +#if !defined(NOMEMCPY) && !defined(DEBUG) + if (w - d >= e) { /* (this test assumes unsigned comparison) */ + memcpy(window + w, window + d, e); + w += e; + d += e; + } else /* do it slow to avoid memcpy() overlap */ +#endif /* !NOMEMCPY */ + do { + window[w++] = window[d++]; + } while (--e); + if (w == WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + } while (n); + } + } + + /* restore the globals from the locals */ + outcnt = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + + /* done */ + return 0; +} + +/* + * decompress an inflated block + * e: last block flag + * + * GLOBAL VARIABLES: bb, kk, + */ +static int inflate_block(int *e) +{ + unsigned t; /* block type */ + register unsigned long b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + static unsigned short cplens[] = { /* Copy lengths for literal codes 257..285 */ + 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. */ + static unsigned short cplext[] = { /* Extra bits for literal codes 257..285 */ + 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 */ + static unsigned short cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 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 + }; + static unsigned short cpdext[] = { /* Extra bits for distance 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 + }; + + /* make local bit buffer */ + b = bb; + k = bk; + + /* read in last block bit */ + while (k < 1) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + *e = (int) b & 1; + b >>= 1; + k -= 1; + + /* read in block type */ + while (k < 2) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + t = (unsigned) b & 3; + b >>= 2; + k -= 2; + + /* restore the global bit buffer */ + bb = b; + bk = k; + + /* inflate that block type */ + switch (t) { + case 0: /* Inflate stored */ + { + unsigned long n; /* number of bytes in block */ + unsigned long w; /* current window position */ + register unsigned long b_stored; /* bit buffer */ + register unsigned long k_stored; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b_stored = bb; /* initialize bit buffer */ + k_stored = bk; + w = outcnt; /* initialize window position */ + + /* go to byte boundary */ + n = k_stored & 7; + b_stored >>= n; + k_stored -= n; + + /* get the length and its complement */ + while (k_stored < 16) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + n = ((unsigned) b_stored & 0xffff); + b_stored >>= 16; + k_stored -= 16; + while (k_stored < 16) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + if (n != (unsigned) ((~b_stored) & 0xffff)) { + return 1; /* error in compressed data */ + } + b_stored >>= 16; + k_stored -= 16; + + /* read and output the compressed data */ + while (n--) { + while (k_stored < 8) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + window[w++] = (unsigned char) b_stored; + if (w == (unsigned long)WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + b_stored >>= 8; + k_stored -= 8; + } + + /* restore the globals from the locals */ + outcnt = w; /* restore global window pointer */ + bb = b_stored; /* restore global bit buffer */ + bk = k_stored; + return 0; + } + 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 */ + int bl; /* lookup bits for tl */ + 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 */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; + } + case 2: /* Inflate dynamic */ + { + /* Tables for deflate from PKZIP's appnote.txt. */ + static unsigned border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + int dbits = 6; /* bits in base distance lookup table */ + int lbits = 9; /* bits in base literal/length lookup table */ + + int i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + huft_t *tl; /* literal/length code table */ + huft_t *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ + + unsigned ll[286 + 30]; /* literal/length and distance code lengths */ + register unsigned long b_dynamic; /* bit buffer */ + register unsigned k_dynamic; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b_dynamic = bb; + k_dynamic = bk; + + /* read in table lengths */ + while (k_dynamic < 5) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nl = 257 + ((unsigned) b_dynamic & 0x1f); /* number of literal/length codes */ + b_dynamic >>= 5; + k_dynamic -= 5; + while (k_dynamic < 5) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nd = 1 + ((unsigned) b_dynamic & 0x1f); /* number of distance codes */ + b_dynamic >>= 5; + k_dynamic -= 5; + while (k_dynamic < 4) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nb = 4 + ((unsigned) 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++) { + while (k_dynamic < 3) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + ll[border[j]] = (unsigned) b_dynamic & 7; + b_dynamic >>= 3; + k_dynamic -= 3; + } + for (; j < 19; j++) { + ll[border[j]] = 0; + } + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 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) i < n) { + while (k_dynamic < (unsigned) bl) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = (td = tl + ((unsigned) 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 */ + while (k_dynamic < 2) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 3 + ((unsigned) b_dynamic & 3); + b_dynamic >>= 2; + k_dynamic -= 2; + if ((unsigned) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = l; + } + } else if (j == 17) { /* 3 to 10 zero length codes */ + while (k_dynamic < 3) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 3 + ((unsigned) b_dynamic & 7); + b_dynamic >>= 3; + k_dynamic -= 3; + if ((unsigned) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } else { /* j == 18: 11 to 138 zero length codes */ + while (k_dynamic < 7) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 11 + ((unsigned) b_dynamic & 0x7f); + b_dynamic >>= 7; + k_dynamic -= 7; + if ((unsigned) 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 */ + bb = b_dynamic; + 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) { + error_msg("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) { + error_msg("incomplete distance tree"); + huft_free(td); + } + huft_free(tl); + return i; /* incomplete code set */ + } + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; + } + default: + /* bad block type */ + return 2; + } +} + +/* + * decompress an inflated entry + * + * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr + */ +static int inflate() +{ + int e; /* last block flag */ + int r; /* result code */ + unsigned h = 0; /* maximum struct huft's malloc'ed */ + + /* initialize window, bit buffer */ + outcnt = 0; + bk = 0; + bb = 0; + + /* decompress until the last block */ + do { + hufts = 0; + if ((r = inflate_block(&e)) != 0) { + return r; + } + if (hufts > h) { + h = hufts; + } + } while (!e); + + /* flush out window */ + flush_window(); + + /* return success */ + return 0; +} + +/* =========================================================================== + * Unzip in to out. This routine works on both gzip and pkzip files. + * + * IN assertions: the buffer inbuf contains already the beginning of + * the compressed data, from offsets inptr to insize-1 included. + * The magic header has already been checked. The output buffer is cleared. + * in, out: input and output file descriptors + */ +extern int unzip(FILE *l_in_file, FILE *l_out_file) +{ + const int extra_field = 0x04; /* bit 2 set: extra field present */ + const int orig_name = 0x08; /* bit 3 set: original file name present */ + const int comment = 0x10; /* bit 4 set: file comment present */ + unsigned char buf[8]; /* extended local header */ + unsigned char flags; /* compression flags */ + char magic[2]; /* magic header */ + int method; + typedef void (*sig_type) (int); + int exit_code=0; /* program exit code */ + int i; + + in_file = l_in_file; + out_file = l_out_file; + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { + (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 + + /* Allocate all global buffers (for DYN_ALLOC option) */ + window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); + outcnt = 0; + bytes_out = 0L; + + magic[0] = fgetc(in_file); + magic[1] = fgetc(in_file); + + /* Magic header for gzip files, 1F 8B = \037\213 */ + if (memcmp(magic, "\037\213", 2) != 0) { + error_msg("Invalid gzip magic"); + return EXIT_FAILURE; + } + + method = (int) fgetc(in_file); + if (method != 8) { + error_msg("unknown method %d -- get newer version of gzip", method); + exit_code = 1; + return -1; + } + + flags = (unsigned char) fgetc(in_file); + + /* Ignore time stamp(4), extra flags(1), OS type(1) */ + for (i = 0; i < 6; i++) + fgetc(in_file); + + if ((flags & extra_field) != 0) { + size_t extra; + extra = fgetc(in_file); + extra += fgetc(in_file) << 8; + + for (i = 0; i < extra; i++) + fgetc(in_file); + } + + /* Discard original name if any */ + if ((flags & orig_name) != 0) { + while (fgetc(in_file) != 0); /* null */ + } + + /* Discard file comment if any */ + if ((flags & comment) != 0) { + while (fgetc(in_file) != 0); /* null */ + } + + if (method < 0) { + printf("it failed\n"); + return(exit_code); /* error message already emitted */ + } + + make_crc_table(); + + /* Decompress */ + if (method == 8) { + + int res = inflate(); + + if (res == 3) { + error_msg(memory_exhausted); + } else if (res != 0) { + error_msg("invalid compressed data--format violated"); + } + + } else { + error_msg("internal error, invalid method"); + } + + /* Get the crc and original length + * crc32 (see algorithm.doc) + * uncompressed input size modulo 2^32 + */ + fread(buf, 1, 8, in_file); + + /* Validate decompression - crc */ + if ((unsigned int)((buf[0] | (buf[1] << 8)) |((buf[2] | (buf[3] << 8)) << 16)) != (crc ^ 0xffffffffL)) { + error_msg("invalid compressed data--crc error"); + } + /* Validate decompression - size */ + if (((buf[4] | (buf[5] << 8)) |((buf[6] | (buf[7] << 8)) << 16)) != (unsigned long) bytes_out) { + error_msg("invalid compressed data--length error"); + } + + free(window); + free(crc_table); + + return 0; +} + +/* + * This needs access to global variables wondow and crc_table, so its not in its own file. + */ +extern void gz_close(int gunzip_pid) +{ + if (kill(gunzip_pid, SIGTERM) == -1) { + error_msg_and_die("*** Couldnt kill old gunzip process *** aborting"); + } + + if (waitpid(gunzip_pid, NULL, 0) == -1) { + printf("Couldnt wait ?"); + } + free(window); + free(crc_table); +} diff --git a/busybox/archival/libunarchive/unzip.c b/busybox/archival/libunarchive/unzip.c new file mode 100644 index 000000000..ee746216d --- /dev/null +++ b/busybox/archival/libunarchive/unzip.c @@ -0,0 +1,1026 @@ +/* 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 + * based on gzip sources + * + * Adjusted further by Erik Andersen , + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "libbb.h" + +static FILE *in_file, *out_file; + +/* these are freed by gz_close */ +static unsigned char *window; +static unsigned long *crc_table; + +static unsigned long crc = 0xffffffffL; /* shift register contents */ + +/* Return codes from gzip */ +static const int ERROR = 1; + +/* + * window size--must be a power of two, and + * at least 32K for zip's deflate method + */ +static const int WSIZE = 0x8000; + +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ +static const int BMAX = 16; /* maximum bit length of any code (16 for explode) */ +static const int N_MAX = 288; /* maximum number of codes in any set */ + +static long bytes_out; /* number of output bytes */ +static unsigned long outcnt; /* bytes in output buffer */ + +static unsigned hufts; /* track memory usage */ +static unsigned long bb; /* bit buffer */ +static unsigned bk; /* bits in bit buffer */ + +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 const unsigned short mask_bits[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +//static int error_number = 0; +/* ======================================================================== + * Signal and error handler. + */ + +static void abort_gzip() +{ + error_msg("gzip aborted\n"); + exit(ERROR); +} + +static void make_crc_table() +{ + unsigned long table_entry; /* crc shift register */ + unsigned long poly = 0; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + crc_table = (unsigned long *) malloc(256 * sizeof(unsigned long)); + + /* Make exclusive-or pattern from polynomial (0xedb88320) */ + for (i = 0; i < sizeof(p)/sizeof(int); i++) + poly |= 1L << (31 - p[i]); + + /* Compute and print table of CRC's, five per line */ + for (i = 0; i < 256; i++) { + 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--) { + table_entry = table_entry & 1 ? (table_entry >> 1) ^ poly : table_entry >> 1; + } + crc_table[i]=table_entry; + } +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void flush_window(void) +{ + int n; + + if (outcnt == 0) + return; + + for (n = 0; n < outcnt; n++) { + crc = crc_table[((int) crc ^ (window[n])) & 0xff] ^ (crc >> 8); + } + + if (fwrite(window, 1, outcnt, out_file) != outcnt) { + error_msg_and_die("Couldnt write"); + } + bytes_out += (unsigned long) outcnt; + outcnt = 0; +} + +/* + * 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, *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 short *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 */ + if ((q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t))) == NULL) { + if (h) { + huft_free(u[0]); + } + return 3; /* not enough memory */ + } + hufts += z + 1; /* track memory usage */ + *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 *tl, huft_t *td, int bl, int bd) +{ + register unsigned long e; /* table entry flag/number of extra bits */ + unsigned long n, d; /* length and index for copy */ + unsigned long w; /* current window position */ + huft_t *t; /* pointer to table entry */ + unsigned ml, md; /* masks for bl and bd bits */ + register unsigned long b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = outcnt; /* initialize window position */ + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + for (;;) { /* do until end of block */ + while (k < (unsigned) bl) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + if ((e = (t = tl + ((unsigned) b & ml))->e) > 16) + do { + if (e == 99) { + return 1; + } + b >>= t->b; + k -= t->b; + e -= 16; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + } 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 */ + window[w++] = (unsigned char) t->v.n; + if (w == WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + } else { /* it's an EOB or a length */ + + /* exit if end of block */ + if (e == 15) { + break; + } + + /* get length of block to copy */ + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + n = t->v.n + ((unsigned) b & mask_bits[e]); + b >>= e; + k -= e; + + /* decode distance of block to copy */ + while (k < (unsigned) bd) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + + if ((e = (t = td + ((unsigned) b & md))->e) > 16) + do { + if (e == 99) + return 1; + b >>= t->b; + k -= t->b; + e -= 16; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); + b >>= t->b; + k -= t->b; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + d = w - t->v.n - ((unsigned) b & mask_bits[e]); + b >>= e; + k -= e; + + /* do the copy */ + do { + n -= (e = (e = WSIZE - ((d &= WSIZE - 1) > w ? d : w)) > n ? n : e); +#if !defined(NOMEMCPY) && !defined(DEBUG) + if (w - d >= e) { /* (this test assumes unsigned comparison) */ + memcpy(window + w, window + d, e); + w += e; + d += e; + } else /* do it slow to avoid memcpy() overlap */ +#endif /* !NOMEMCPY */ + do { + window[w++] = window[d++]; + } while (--e); + if (w == WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + } while (n); + } + } + + /* restore the globals from the locals */ + outcnt = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + + /* done */ + return 0; +} + +/* + * decompress an inflated block + * e: last block flag + * + * GLOBAL VARIABLES: bb, kk, + */ +static int inflate_block(int *e) +{ + unsigned t; /* block type */ + register unsigned long b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + static unsigned short cplens[] = { /* Copy lengths for literal codes 257..285 */ + 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. */ + static unsigned short cplext[] = { /* Extra bits for literal codes 257..285 */ + 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 */ + static unsigned short cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 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 + }; + static unsigned short cpdext[] = { /* Extra bits for distance 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 + }; + + /* make local bit buffer */ + b = bb; + k = bk; + + /* read in last block bit */ + while (k < 1) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + *e = (int) b & 1; + b >>= 1; + k -= 1; + + /* read in block type */ + while (k < 2) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + t = (unsigned) b & 3; + b >>= 2; + k -= 2; + + /* restore the global bit buffer */ + bb = b; + bk = k; + + /* inflate that block type */ + switch (t) { + case 0: /* Inflate stored */ + { + unsigned long n; /* number of bytes in block */ + unsigned long w; /* current window position */ + register unsigned long b_stored; /* bit buffer */ + register unsigned long k_stored; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b_stored = bb; /* initialize bit buffer */ + k_stored = bk; + w = outcnt; /* initialize window position */ + + /* go to byte boundary */ + n = k_stored & 7; + b_stored >>= n; + k_stored -= n; + + /* get the length and its complement */ + while (k_stored < 16) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + n = ((unsigned) b_stored & 0xffff); + b_stored >>= 16; + k_stored -= 16; + while (k_stored < 16) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + if (n != (unsigned) ((~b_stored) & 0xffff)) { + return 1; /* error in compressed data */ + } + b_stored >>= 16; + k_stored -= 16; + + /* read and output the compressed data */ + while (n--) { + while (k_stored < 8) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + window[w++] = (unsigned char) b_stored; + if (w == (unsigned long)WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + b_stored >>= 8; + k_stored -= 8; + } + + /* restore the globals from the locals */ + outcnt = w; /* restore global window pointer */ + bb = b_stored; /* restore global bit buffer */ + bk = k_stored; + return 0; + } + 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 */ + int bl; /* lookup bits for tl */ + 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 */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; + } + case 2: /* Inflate dynamic */ + { + /* Tables for deflate from PKZIP's appnote.txt. */ + static unsigned border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + int dbits = 6; /* bits in base distance lookup table */ + int lbits = 9; /* bits in base literal/length lookup table */ + + int i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + huft_t *tl; /* literal/length code table */ + huft_t *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ + + unsigned ll[286 + 30]; /* literal/length and distance code lengths */ + register unsigned long b_dynamic; /* bit buffer */ + register unsigned k_dynamic; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b_dynamic = bb; + k_dynamic = bk; + + /* read in table lengths */ + while (k_dynamic < 5) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nl = 257 + ((unsigned) b_dynamic & 0x1f); /* number of literal/length codes */ + b_dynamic >>= 5; + k_dynamic -= 5; + while (k_dynamic < 5) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nd = 1 + ((unsigned) b_dynamic & 0x1f); /* number of distance codes */ + b_dynamic >>= 5; + k_dynamic -= 5; + while (k_dynamic < 4) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nb = 4 + ((unsigned) 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++) { + while (k_dynamic < 3) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + ll[border[j]] = (unsigned) b_dynamic & 7; + b_dynamic >>= 3; + k_dynamic -= 3; + } + for (; j < 19; j++) { + ll[border[j]] = 0; + } + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 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) i < n) { + while (k_dynamic < (unsigned) bl) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = (td = tl + ((unsigned) 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 */ + while (k_dynamic < 2) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 3 + ((unsigned) b_dynamic & 3); + b_dynamic >>= 2; + k_dynamic -= 2; + if ((unsigned) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = l; + } + } else if (j == 17) { /* 3 to 10 zero length codes */ + while (k_dynamic < 3) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 3 + ((unsigned) b_dynamic & 7); + b_dynamic >>= 3; + k_dynamic -= 3; + if ((unsigned) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } else { /* j == 18: 11 to 138 zero length codes */ + while (k_dynamic < 7) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 11 + ((unsigned) b_dynamic & 0x7f); + b_dynamic >>= 7; + k_dynamic -= 7; + if ((unsigned) 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 */ + bb = b_dynamic; + 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) { + error_msg("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) { + error_msg("incomplete distance tree"); + huft_free(td); + } + huft_free(tl); + return i; /* incomplete code set */ + } + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; + } + default: + /* bad block type */ + return 2; + } +} + +/* + * decompress an inflated entry + * + * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr + */ +static int inflate() +{ + int e; /* last block flag */ + int r; /* result code */ + unsigned h = 0; /* maximum struct huft's malloc'ed */ + + /* initialize window, bit buffer */ + outcnt = 0; + bk = 0; + bb = 0; + + /* decompress until the last block */ + do { + hufts = 0; + if ((r = inflate_block(&e)) != 0) { + return r; + } + if (hufts > h) { + h = hufts; + } + } while (!e); + + /* flush out window */ + flush_window(); + + /* return success */ + return 0; +} + +/* =========================================================================== + * Unzip in to out. This routine works on both gzip and pkzip files. + * + * IN assertions: the buffer inbuf contains already the beginning of + * the compressed data, from offsets inptr to insize-1 included. + * The magic header has already been checked. The output buffer is cleared. + * in, out: input and output file descriptors + */ +extern int unzip(FILE *l_in_file, FILE *l_out_file) +{ + const int extra_field = 0x04; /* bit 2 set: extra field present */ + const int orig_name = 0x08; /* bit 3 set: original file name present */ + const int comment = 0x10; /* bit 4 set: file comment present */ + unsigned char buf[8]; /* extended local header */ + unsigned char flags; /* compression flags */ + char magic[2]; /* magic header */ + int method; + typedef void (*sig_type) (int); + int exit_code=0; /* program exit code */ + int i; + + in_file = l_in_file; + out_file = l_out_file; + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { + (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 + + /* Allocate all global buffers (for DYN_ALLOC option) */ + window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); + outcnt = 0; + bytes_out = 0L; + + magic[0] = fgetc(in_file); + magic[1] = fgetc(in_file); + + /* Magic header for gzip files, 1F 8B = \037\213 */ + if (memcmp(magic, "\037\213", 2) != 0) { + error_msg("Invalid gzip magic"); + return EXIT_FAILURE; + } + + method = (int) fgetc(in_file); + if (method != 8) { + error_msg("unknown method %d -- get newer version of gzip", method); + exit_code = 1; + return -1; + } + + flags = (unsigned char) fgetc(in_file); + + /* Ignore time stamp(4), extra flags(1), OS type(1) */ + for (i = 0; i < 6; i++) + fgetc(in_file); + + if ((flags & extra_field) != 0) { + size_t extra; + extra = fgetc(in_file); + extra += fgetc(in_file) << 8; + + for (i = 0; i < extra; i++) + fgetc(in_file); + } + + /* Discard original name if any */ + if ((flags & orig_name) != 0) { + while (fgetc(in_file) != 0); /* null */ + } + + /* Discard file comment if any */ + if ((flags & comment) != 0) { + while (fgetc(in_file) != 0); /* null */ + } + + if (method < 0) { + printf("it failed\n"); + return(exit_code); /* error message already emitted */ + } + + make_crc_table(); + + /* Decompress */ + if (method == 8) { + + int res = inflate(); + + if (res == 3) { + error_msg(memory_exhausted); + } else if (res != 0) { + error_msg("invalid compressed data--format violated"); + } + + } else { + error_msg("internal error, invalid method"); + } + + /* Get the crc and original length + * crc32 (see algorithm.doc) + * uncompressed input size modulo 2^32 + */ + fread(buf, 1, 8, in_file); + + /* Validate decompression - crc */ + if ((unsigned int)((buf[0] | (buf[1] << 8)) |((buf[2] | (buf[3] << 8)) << 16)) != (crc ^ 0xffffffffL)) { + error_msg("invalid compressed data--crc error"); + } + /* Validate decompression - size */ + if (((buf[4] | (buf[5] << 8)) |((buf[6] | (buf[7] << 8)) << 16)) != (unsigned long) bytes_out) { + error_msg("invalid compressed data--length error"); + } + + free(window); + free(crc_table); + + return 0; +} + +/* + * This needs access to global variables wondow and crc_table, so its not in its own file. + */ +extern void gz_close(int gunzip_pid) +{ + if (kill(gunzip_pid, SIGTERM) == -1) { + error_msg_and_die("*** Couldnt kill old gunzip process *** aborting"); + } + + if (waitpid(gunzip_pid, NULL, 0) == -1) { + printf("Couldnt wait ?"); + } + free(window); + free(crc_table); +} diff --git a/busybox/archival/rpm2cpio.c b/busybox/archival/rpm2cpio.c new file mode 100644 index 000000000..45c3ffb17 --- /dev/null +++ b/busybox/archival/rpm2cpio.c @@ -0,0 +1,95 @@ +/* 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 "busybox.h" +#include /* For ntohl & htonl function */ +#include + +#define RPM_MAGIC "\355\253\356\333" +#define RPM_HEADER_MAGIC "\216\255\350" + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +struct rpm_lead { + unsigned char magic[4]; + u8 major, minor; + u16 type; + u16 archnum; + char name[66]; + u16 osnum; + u16 signature_type; + char reserved[16]; +}; + +struct rpm_header { + char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */ + u8 version; /* 1 byte version number */ + u32 reserved; /* 4 bytes reserved */ + u32 entries; /* Number of entries in header (4 bytes) */ + u32 size; /* Size of store (4 bytes) */ +}; + +void skip_header(FILE *rpmfile) +{ + struct rpm_header header; + + fread(&header, sizeof(struct rpm_header), 1, rpmfile); + if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */ + if (header.version != 1) error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */ + header.entries = ntohl(header.entries); + header.size = ntohl(header.size); + fseek (rpmfile, 16 * header.entries, SEEK_CUR); /* Seek past index entries */ + fseek (rpmfile, header.size, SEEK_CUR); /* Seek past store */ +} + +/* No getopt required */ +extern int rpm2cpio_main(int argc, char **argv) +{ + struct rpm_lead lead; + int gunzip_pid; + FILE *rpmfile, *cpiofile; + + if (argc == 1) { + rpmfile = stdin; + } else { + rpmfile = fopen(argv[1], "r"); + if (!rpmfile) perror_msg_and_die("Can't open rpm file"); + /* set the buffer size */ + setvbuf(rpmfile, NULL, _IOFBF, 0x8000); + } + + fread (&lead, sizeof(struct rpm_lead), 1, rpmfile); + if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */ + /* Skip the signature header */ + skip_header(rpmfile); + fseek(rpmfile, (8 - (ftell(rpmfile) % 8)) % 8, SEEK_CUR); /* Pad to 8 byte boundary */ + /* Skip the main header */ + skip_header(rpmfile); + + cpiofile = gz_open(rpmfile, &gunzip_pid); + + copyfd(fileno(cpiofile), fileno(stdout)); + gz_close(gunzip_pid); + fclose(rpmfile); + return 0; +} diff --git a/busybox/archival/tar.c b/busybox/archival/tar.c new file mode 100644 index 000000000..cf65798ff --- /dev/null +++ b/busybox/archival/tar.c @@ -0,0 +1,1148 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tar implementation for busybox + * + * Note, that as of BusyBox-0.43, tar has been completely rewritten from the + * ground up. It still has remnents of the old code lying about, but it is + * very different now (i.e., cleaner, less global variables, etc.) + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* Tar file constants */ +#ifndef MAJOR +#define MAJOR(dev) (((dev)>>8)&0xff) +#define MINOR(dev) ((dev)&0xff) +#endif + +enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */ + +/* POSIX tar Header Block, from POSIX 1003.1-1990 */ +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; + + +/* A few useful constants */ +#define TAR_MAGIC "ustar" /* ustar and a null */ +#define TAR_VERSION " " /* Be compatable with GNU tar format */ +static const int TAR_MAGIC_LEN = 6; +static const int TAR_VERSION_LEN = 2; +static const int TAR_BLOCK_SIZE = 512; + +/* 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; + +/* This struct ignores magic, non-numeric user name, + * non-numeric group name, and the checksum, since + * these are all ignored by BusyBox tar. */ +struct TarInfo +{ + int tarFd; /* An open file descriptor for reading from the tarball */ + char * name; /* File name */ + mode_t mode; /* Unix mode, including device bits. */ + uid_t uid; /* Numeric UID */ + gid_t gid; /* Numeric GID */ + size_t size; /* Size of file */ + time_t mtime; /* Last-modified time */ + enum TarFileType type; /* Regular, directory, link, etc. */ + char * linkname; /* Name for symbolic and hard links */ + long devmajor; /* Major number for special device */ + long devminor; /* Minor number for special device */ +}; +typedef struct TarInfo TarInfo; + +/* Local procedures to restore files from a tar file. */ +static int readTarFile(int tarFd, int extractFlag, int listFlag, + int tostdoutFlag, int verboseFlag, char** extractList, + char** excludeList); + +#ifdef BB_FEATURE_TAR_CREATE +/* Local procedures to save files into a tar file. */ +static int writeTarFile(const char* tarName, int verboseFlag, char **argv, + char** excludeList); +#endif + +#if defined BB_FEATURE_TAR_EXCLUDE +static struct option longopts[] = { + { "exclude", 1, NULL, 'e' }, + { NULL, 0, NULL, 0 } +}; +#endif + +extern int tar_main(int argc, char **argv) +{ + char** excludeList=NULL; + char** extractList=NULL; + const char *tarName="-"; + const char *cwd=NULL; +#if defined BB_FEATURE_TAR_EXCLUDE + int excludeListSize=0; + FILE *fileList; + char file[256]; +#endif +#if defined BB_FEATURE_TAR_GZIP + FILE *comp_file = NULL; + int unzipFlag = FALSE; +#endif + int listFlag = FALSE; + int extractFlag = FALSE; + int createFlag = FALSE; + int verboseFlag = FALSE; + int tostdoutFlag = FALSE; + int status = FALSE; + int opt; + pid_t pid; + + if (argc <= 1) + show_usage(); + + if (argv[1][0] != '-') { + char *tmp = xmalloc(strlen(argv[1]) + 2); + tmp[0] = '-'; + strcpy(tmp + 1, argv[1]); + argv[1] = tmp; + } + + while ( +#ifndef BB_FEATURE_TAR_EXCLUDE + (opt = getopt(argc, argv, "cxtzvOf:pC:")) +#else + (opt = getopt_long(argc, argv, "cxtzvOf:X:pC:", longopts, NULL)) +#endif + > 0) { + switch (opt) { + case 'c': + if (extractFlag == TRUE || listFlag == TRUE) + goto flagError; + createFlag = TRUE; + break; + case 'x': + if (listFlag == TRUE || createFlag == TRUE) + goto flagError; + extractFlag = TRUE; + break; + case 't': + if (extractFlag == TRUE || createFlag == TRUE) + goto flagError; + listFlag = TRUE; + break; +#ifdef BB_FEATURE_TAR_GZIP + case 'z': + unzipFlag = TRUE; + break; +#endif + case 'v': + verboseFlag = TRUE; + break; + case 'O': + tostdoutFlag = TRUE; + break; + case 'f': + if (*tarName != '-') + error_msg_and_die( "Only one 'f' option allowed"); + tarName = optarg; + break; +#if defined BB_FEATURE_TAR_EXCLUDE + case 'e': + excludeList=xrealloc( excludeList, + sizeof(char *) * (excludeListSize+2)); + excludeList[excludeListSize] = optarg; + /* Tack a NULL onto the end of the list */ + excludeList[++excludeListSize] = NULL; + case 'X': + fileList = xfopen(optarg, "r"); + while (fgets(file, sizeof(file), fileList) != NULL) { + excludeList = xrealloc(excludeList, + sizeof(char *) * (excludeListSize+2)); + chomp(file); + excludeList[excludeListSize] = xstrdup(file); + /* Tack a NULL onto the end of the list */ + excludeList[++excludeListSize] = NULL; + } + fclose(fileList); + break; +#endif + case 'p': + break; + case 'C': + cwd = xgetcwd((char *)cwd); + if (chdir(optarg)) { + printf("cd: %s: %s\n", optarg, strerror(errno)); + return EXIT_FAILURE; + } + break; + default: + show_usage(); + } + } + + /* + * Do the correct type of action supplying the rest of the + * command line arguments as the list of files to process. + */ + if (createFlag == TRUE) { +#ifndef BB_FEATURE_TAR_CREATE + error_msg_and_die( "This version of tar was not compiled with tar creation support."); +#else +#ifdef BB_FEATURE_TAR_GZIP + if (unzipFlag==TRUE) + error_msg_and_die("Creation of compressed not internally support by tar, pipe to busybox gunzip"); +#endif + status = writeTarFile(tarName, verboseFlag, argv + optind, excludeList); +#endif + } + if (listFlag == TRUE || extractFlag == TRUE) { + int tarFd; + if (argv[optind]) + extractList = argv + optind; + /* Open the tar file for reading. */ + if (!strcmp(tarName, "-")) + tarFd = fileno(stdin); + else + tarFd = open(tarName, O_RDONLY); + if (tarFd < 0) + perror_msg_and_die("Error opening '%s'", tarName); + +#ifdef BB_FEATURE_TAR_GZIP + /* unzip tarFd in a seperate process */ + if (unzipFlag == TRUE) { + comp_file = fdopen(tarFd, "r"); + + /* set the buffer size */ + setvbuf(comp_file, NULL, _IOFBF, 0x8000); + + if ((tarFd = fileno(gz_open(comp_file, &pid))) == EXIT_FAILURE) { + error_msg_and_die("Couldnt unzip file"); + } + } +#endif + status = readTarFile(tarFd, extractFlag, listFlag, tostdoutFlag, + verboseFlag, extractList, excludeList); + close(tarFd); +#ifdef BB_FEATURE_TAR_GZIP + if (unzipFlag == TRUE) { + gz_close(pid); + fclose(comp_file); + } +#endif + } + + if (cwd) + chdir(cwd); + if (status == TRUE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + + flagError: + error_msg_and_die( "Exactly one of 'c', 'x' or 't' must be specified"); +} + +static void +fixUpPermissions(TarInfo *header) +{ + struct utimbuf t; + /* Now set permissions etc. for the new file */ + chown(header->name, header->uid, header->gid); + chmod(header->name, header->mode); + /* Reset the time */ + t.actime = time(0); + t.modtime = header->mtime; + utime(header->name, &t); +} + +static int +tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + size_t writeSize; + size_t readSize; + size_t actualWriteSz; + char buffer[20 * TAR_BLOCK_SIZE]; + size_t size = header->size; + int outFd=fileno(stdout); + + /* Open the file to be written, if a file is supposed to be written */ + if (extractFlag==TRUE && tostdoutFlag==FALSE) { + /* Create the path to the file, just in case it isn't there... + * This should not screw up path permissions or anything. */ + char *dir = dirname (header->name); + make_directory (dir, -1, FILEUTILS_RECUR); + free (dir); + if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, + header->mode & ~S_IFMT)) < 0) { + error_msg(io_error, header->name, strerror(errno)); + return( FALSE); + } + } + + /* Write out the file, if we are supposed to be doing that */ + while ( size > 0 ) { + actualWriteSz=0; + if ( size > sizeof(buffer) ) + writeSize = readSize = sizeof(buffer); + else { + int mod = size % TAR_BLOCK_SIZE; + if ( mod != 0 ) + readSize = size + (TAR_BLOCK_SIZE - mod); + else + readSize = size; + writeSize = size; + } + if ( (readSize = full_read(header->tarFd, buffer, readSize)) <= 0 ) { + /* Tarball seems to have a problem */ + error_msg("Unexpected EOF in archive"); + return( FALSE); + } + if ( readSize < writeSize ) + writeSize = readSize; + + /* Write out the file, if we are supposed to be doing that */ + if (extractFlag==TRUE) { + + if ((actualWriteSz=full_write(outFd, buffer, writeSize)) != writeSize ) { + /* Output file seems to have a problem */ + error_msg(io_error, header->name, strerror(errno)); + return( FALSE); + } + } else { + actualWriteSz=writeSize; + } + + size -= actualWriteSz; + } + + /* Now we are done writing the file out, so try + * and fix up the permissions and whatnot */ + if (extractFlag==TRUE && tostdoutFlag==FALSE) { + close(outFd); + fixUpPermissions(header); + } + return( TRUE); +} + +static int +tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + if (extractFlag==FALSE || tostdoutFlag==TRUE) + return( TRUE); + + if (make_directory(header->name, header->mode, FILEUTILS_RECUR) < 0) + return( FALSE); + + fixUpPermissions(header); + return( TRUE); +} + +static int +tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + if (extractFlag==FALSE || tostdoutFlag==TRUE) + return( TRUE); + + if (link(header->linkname, header->name) < 0) { + perror_msg("%s: Cannot create hard link to '%s'", header->name, + header->linkname); + return( FALSE); + } + + /* Now set permissions etc. for the new directory */ + fixUpPermissions(header); + return( TRUE); +} + +static int +tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + if (extractFlag==FALSE || tostdoutFlag==TRUE) + return( TRUE); + +#ifdef S_ISLNK + if (symlink(header->linkname, header->name) < 0) { + perror_msg("%s: Cannot create symlink to '%s'", header->name, + header->linkname); + return( FALSE); + } + /* Try to change ownership of the symlink. + * If libs doesn't support that, don't bother. + * Changing the pointed-to-file is the Wrong Thing(tm). + */ +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) + lchown(header->name, header->uid, header->gid); +#endif + + /* Do not change permissions or date on symlink, + * since it changes the pointed to file instead. duh. */ +#else + error_msg("%s: Cannot create symlink to '%s': %s", + header->name, header->linkname, + "symlinks not supported"); +#endif + return( TRUE); +} + +static int +tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + if (extractFlag==FALSE || tostdoutFlag==TRUE) + return( TRUE); + + if (S_ISCHR(header->mode) || S_ISBLK(header->mode) || S_ISSOCK(header->mode)) { + if (mknod(header->name, header->mode, makedev(header->devmajor, header->devminor)) < 0) { + perror_msg("%s: Cannot mknod", header->name); + return( FALSE); + } + } else if (S_ISFIFO(header->mode)) { + if (mkfifo(header->name, header->mode) < 0) { + perror_msg("%s: Cannot mkfifo", header->name); + return( FALSE); + } + } + + /* Now set permissions etc. for the new directory */ + fixUpPermissions(header); + return( TRUE); +} + +/* Parse the tar header and fill in the nice struct with the details */ +static int +readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header) +{ + int i; + long chksum, sum=0; + unsigned char *s = (unsigned char *)rawHeader; + + header->name = rawHeader->name; + /* Check for and relativify any absolute paths */ + if ( *(header->name) == '/' ) { + static int alreadyWarned=FALSE; + + while (*(header->name) == '/') + header->name++; + + if (alreadyWarned == FALSE) { + error_msg("Removing leading '/' from member names"); + alreadyWarned = TRUE; + } + } + + header->mode = strtol(rawHeader->mode, NULL, 8); + header->uid = strtol(rawHeader->uid, NULL, 8); + header->gid = strtol(rawHeader->gid, NULL, 8); + header->size = strtol(rawHeader->size, NULL, 8); + header->mtime = strtol(rawHeader->mtime, NULL, 8); + chksum = strtol(rawHeader->chksum, NULL, 8); + header->type = rawHeader->typeflag; + header->linkname = rawHeader->linkname; + header->devmajor = strtol(rawHeader->devmajor, NULL, 8); + header->devminor = strtol(rawHeader->devminor, NULL, 8); + + /* Check the checksum */ + for (i = sizeof(*rawHeader); i-- != 0;) { + sum += *s++; + } + /* Remove the effects of the checksum field (replace + * with blanks for the purposes of the checksum) */ + s = rawHeader->chksum; + for (i = sizeof(rawHeader->chksum) ; i-- != 0;) { + sum -= *s++; + } + sum += ' ' * sizeof(rawHeader->chksum); + if (sum == chksum ) + return ( TRUE); + return( FALSE); +} + +static int exclude_file(char **excluded_files, const char *file) +{ + int i; + + if (excluded_files == NULL) + return 0; + + for (i = 0; excluded_files[i] != NULL; i++) { + if (excluded_files[i][0] == '/') { + if (fnmatch(excluded_files[i], 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[i], p, + FNM_PATHNAME | FNM_LEADING_DIR) == 0) + return 1; + } + } + } + + return 0; +} + +static int extract_file(char **extract_files, const char *file) +{ + int i; + + if (extract_files == NULL) + return 1; + + for (i = 0; extract_files[i] != NULL; i++) { + if (fnmatch(extract_files[i], file, FNM_LEADING_DIR) == 0) + return 1; + } + + return 0; +} + +/* + * Read a tar file and extract or list the specified files within it. + * If the list is empty than all files are extracted or listed. + */ +static int readTarFile(int tarFd, int extractFlag, int listFlag, + int tostdoutFlag, int verboseFlag, char** extractList, + char** excludeList) +{ + int status; + int errorFlag=FALSE; + int skipNextHeaderFlag=FALSE; + TarHeader rawHeader; + TarInfo header; + + /* Read the tar file, and iterate over it one file at a time */ + while ( (status = full_read(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) { + + /* Try to read the header */ + if ( readTarHeader(&rawHeader, &header) == FALSE ) { + if ( *(header.name) == '\0' ) { + goto endgame; + } else { + errorFlag=TRUE; + error_msg("Bad tar header, skipping"); + continue; + } + } + if ( *(header.name) == '\0' ) + continue; + header.tarFd = tarFd; + + /* Skip funky extra GNU headers that precede long files */ + if ( (header.type == GNULONGNAME) || (header.type == GNULONGLINK) ) { + skipNextHeaderFlag=TRUE; + if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) + errorFlag = TRUE; + continue; + } + if ( skipNextHeaderFlag == TRUE ) { + skipNextHeaderFlag=FALSE; + error_msg(name_longer_than_foo, NAME_SIZE); + if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) + errorFlag = TRUE; + continue; + } + +#if defined BB_FEATURE_TAR_EXCLUDE + if (exclude_file(excludeList, header.name)) { + /* There are not the droids you're looking for, move along */ + /* If it is a regular file, pretend to extract it with + * the extractFlag set to FALSE, so the junk in the tarball + * is properly skipped over */ + if ( header.type==REGTYPE || header.type==REGTYPE0 ) { + if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) + errorFlag = TRUE; + } + continue; + } +#endif + + if (!extract_file(extractList, header.name)) { + /* There are not the droids you're looking for, move along */ + /* If it is a regular file, pretend to extract it with + * the extractFlag set to FALSE, so the junk in the tarball + * is properly skipped over */ + if ( header.type==REGTYPE || header.type==REGTYPE0 ) { + if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) + errorFlag = TRUE; + } + continue; + } + + if (listFlag == TRUE) { + /* Special treatment if the list (-t) flag is on */ + if (verboseFlag == TRUE) { + int len, len1; + char buf[35]; + struct tm *tm = localtime (&(header.mtime)); + + len=printf("%s ", mode_string(header.mode)); + my_getpwuid(buf, header.uid); + if (! *buf) + len+=printf("%d", header.uid); + else + len+=printf("%s", buf); + my_getgrgid(buf, header.gid); + if (! *buf) + len+=printf("/%-d ", header.gid); + else + len+=printf("/%-s ", buf); + + if (header.type==CHRTYPE || header.type==BLKTYPE) { + len1=snprintf(buf, sizeof(buf), "%ld,%-ld ", + header.devmajor, header.devminor); + } else { + len1=snprintf(buf, sizeof(buf), "%lu ", (long)header.size); + } + /* Jump through some hoops to make the columns match up */ + for(;(len+len1)<31;len++) + printf(" "); + printf(buf); + + /* Use ISO 8610 time format */ + if (tm) { + printf ("%04d-%02d-%02d %02d:%02d:%02d ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + } + printf("%s", header.name); + if (verboseFlag == TRUE) { + if (header.type==LNKTYPE) /* If this is a link, say so */ + printf(" link to %s", header.linkname); + else if (header.type==SYMTYPE) + printf(" -> %s", header.linkname); + } + printf("\n"); + } + + /* List contents if we are supposed to do that */ + if (verboseFlag == TRUE && extractFlag == TRUE) { + /* Now the normal listing */ + FILE *vbFd = stdout; + if (tostdoutFlag == TRUE) // If the archive goes to stdout, verbose to stderr + vbFd = stderr; + fprintf(vbFd, "%s\n", header.name); + } + + /* Remove files if we would overwrite them */ + if (extractFlag == TRUE && tostdoutFlag == FALSE) + unlink(header.name); + + /* If we got here, we can be certain we have a legitimate + * header to work with. So work with it. */ + switch ( header.type ) { + case REGTYPE: + case REGTYPE0: + /* If the name ends in a '/' then assume it is + * supposed to be a directory, and fall through */ + if (!last_char_is(header.name,'/')) { + if (tarExtractRegularFile(&header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; + } + case DIRTYPE: + if (tarExtractDirectory( &header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; + case LNKTYPE: + if (tarExtractHardLink( &header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; + case SYMTYPE: + if (tarExtractSymLink( &header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; + case CHRTYPE: + case BLKTYPE: + case FIFOTYPE: + if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; +#if 0 + /* Handled earlier */ + case GNULONGNAME: + case GNULONGLINK: + skipNextHeaderFlag=TRUE; + break; +#endif + default: + error_msg("Unknown file type '%c' in tar file", header.type); + close( tarFd); + return( FALSE); + } + } + close(tarFd); + if (status > 0) { + /* Bummer - we read a partial header */ + perror_msg("Error reading tar file"); + return ( FALSE); + } + else if (errorFlag==TRUE) { + error_msg( "Error exit delayed from previous errors"); + return( FALSE); + } else + return( status); + + /* Stuff to do when we are done */ +endgame: + close( tarFd); + if ( *(header.name) == '\0' ) { + if (errorFlag==TRUE) + error_msg( "Error exit delayed from previous errors"); + else + return( TRUE); + } + return( FALSE); +} + + +#ifdef BB_FEATURE_TAR_CREATE + +/* +** writeTarFile(), writeFileToTarball(), and writeTarHeader() are +** the only functions that deal with the HardLinkInfo structure. +** Even these functions use the xxxHardLinkInfo() functions. +*/ +typedef struct HardLinkInfo 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 */ + char** 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; + + +/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ +static void +addHardLinkInfo (HardLinkInfo **hlInfoHeadPtr, dev_t dev, ino_t ino, + short linkCount, const char *name) +{ + /* Note: hlInfoHeadPtr can never be NULL! */ + HardLinkInfo *hlInfo; + + hlInfo = (HardLinkInfo *)xmalloc(sizeof(HardLinkInfo)+strlen(name)+1); + if (hlInfo) { + hlInfo->next = *hlInfoHeadPtr; + *hlInfoHeadPtr = hlInfo; + hlInfo->dev = dev; + hlInfo->ino = ino; + hlInfo->linkCount = linkCount; + strcpy(hlInfo->name, name); + } + return; +} + +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 HardLinkInfo * +findHardLinkInfo (HardLinkInfo *hlInfo, dev_t dev, ino_t ino) +{ + while(hlInfo) { + if ((ino == hlInfo->ino) && (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 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) */ + my_getpwuid(header.uname, statbuf->st_uid); + if (! *header.uname) + strcpy(header.uname, "root"); + my_getgrgid(header.gname, statbuf->st_gid); + if (! *header.uname) + strcpy(header.uname, "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 { + 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=full_write(tbInfo->tarFd, (char*)&header, sizeof(struct TarHeader))) < 0) { + error_msg(io_error, real_name, strerror(errno)); + return ( FALSE); + } + /* Pad the header up to the tar block size */ + for (; sizetarFd, "\0", 1); + } + /* Now do the verbose thing (or not) */ + if (tbInfo->verboseFlag==TRUE) { + FILE *vbFd = stdout; + if (tbInfo->tarFd == fileno(stdout)) // If the archive goes to stdout, verbose to stderr + vbFd = stderr; + fprintf(vbFd, "%s\n", header.name); + } + + return ( TRUE); +} + + +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->st_dev, + statbuf->st_ino); + if (tbInfo->hlInfo == NULL) + addHardLinkInfo (&tbInfo->hlInfoHead, statbuf->st_dev, + statbuf->st_ino, statbuf->st_nlink, fileName); + } + + /* It is against the rules to archive a socket */ + if (S_ISSOCK(statbuf->st_mode)) { + 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) { + 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) { + error_msg("Removing leading '/' from member names"); + alreadyWarned=TRUE; + } + header_name++; + } + + if (strlen(fileName) >= NAME_SIZE) { + error_msg(name_longer_than_foo, NAME_SIZE); + return ( TRUE); + } + + if (header_name[0] == '\0') + return TRUE; + +#if defined BB_FEATURE_TAR_EXCLUDE + if (exclude_file(tbInfo->excludeList, header_name)) { + return SKIP; + } +#endif + + 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; + char buffer[BUFSIZ]; + ssize_t size=0, readSize=0; + + /* open the file we want to archive, and make sure all is well */ + if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { + error_msg("%s: Cannot open: %s", fileName, strerror(errno)); + return( FALSE); + } + + /* write the file to the archive */ + while ( (size = full_read(inputFileFd, buffer, sizeof(buffer))) > 0 ) { + if (full_write(tbInfo->tarFd, buffer, size) != size ) { + /* Output file seems to have a problem */ + error_msg(io_error, fileName, strerror(errno)); + return( FALSE); + } + readSize+=size; + } + if (size == -1) { + error_msg(io_error, fileName, strerror(errno)); + return( FALSE); + } + /* 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 int writeTarFile(const char* tarName, int verboseFlag, char **argv, + char** excludeList) +{ + int tarFd=-1; + int errorFlag=FALSE; + ssize_t size; + struct TarBallInfo tbInfo; + tbInfo.verboseFlag = verboseFlag; + tbInfo.hlInfoHead = NULL; + + /* Make sure there is at least one file to tar up. */ + if (*argv == NULL) + error_msg_and_die("Cowardly refusing to create an empty archive"); + + /* Open the tar file for writing. */ + if (!strcmp(tarName, "-")) + tbInfo.tarFd = fileno(stdout); + else + tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (tbInfo.tarFd < 0) { + perror_msg( "Error opening '%s'", tarName); + freeHardLinkInfo(&tbInfo.hlInfoHead); + return ( FALSE); + } + tbInfo.excludeList=excludeList; + /* Store the stat info for the tarball's file, so + * can avoid including the tarball into itself.... */ + if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) + error_msg_and_die(io_error, tarName, strerror(errno)); + + /* Read the directory/files and iterate over them one at a time */ + while (*argv != NULL) { + if (recursive_action(*argv++, TRUE, FALSE, FALSE, + writeFileToTarball, writeFileToTarball, + (void*) &tbInfo) == FALSE) { + errorFlag = TRUE; + } + } + /* 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(tarFd); + if (errorFlag == TRUE) { + error_msg("Error exit delayed from previous errors"); + freeHardLinkInfo(&tbInfo.hlInfoHead); + return(FALSE); + } + freeHardLinkInfo(&tbInfo.hlInfoHead); + return( TRUE); +} + + +#endif + diff --git a/busybox/ash.c b/busybox/ash.c new file mode 100644 index 000000000..bcd12f106 --- /dev/null +++ b/busybox/ash.c @@ -0,0 +1,12880 @@ +/* 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. + * + * 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 + * + * This version of ash is adapted from the source in Debian's ash 0.3.8-5 + * package. + * + * Modified by Erik Andersen and + * Vladimir Oleynik to be used in busybox + * + * + * Original copyright notice is retained at the end of this file. + */ + + +/* These defines allow you to adjust the feature set to be compiled + * into the ash shell. As a rule, enabling these options will make + * ash get bigger... With all of these options off, ash adds about + * 60k to busybox on an x86 system.*/ + + +/* Enable job control. This allows you to run jobs in the background, + * which is great when ash is being used as an interactive shell, but + * it completely useless for is all you are doing is running scripts. + * This adds about 2.5k on an x86 system. */ +#undef JOBS + +/* This enables alias support in ash. If you want to support things + * like "alias ls='ls -l'" with ash, enable this. This is only useful + * when ash is used as an intractive shell. This adds about 1.5k */ +#define ASH_ALIAS + +/* If you need ash to act as a full Posix shell, with full math + * support, enable this. This adds a bit over 2k an x86 system. */ +//#undef ASH_MATH_SUPPORT +#define ASH_MATH_SUPPORT + +/* Getopts is used by shell procedures to parse positional parameters. + * You probably want to leave this disabled, and use the busybox getopt + * applet if you want to do this sort of thing. There are some scripts + * out there that use it, so it you need it, enable. Most people will + * leave this disabled. This adds 1k on an x86 system. */ +#undef ASH_GETOPTS + +/* This allows you to override shell builtins and use whatever is on + * the filesystem. This is most useful when ash is acting as a + * standalone shell. Adds about 272 bytes. */ +#undef ASH_CMDCMD + + +/* Optimize size vs speed as size */ +#define ASH_OPTIMIZE_FOR_SIZE + +/* Enable this to compile in extra debugging noise. When debugging is + * on, debugging info will be written to $HOME/trace and a quit signal + * will generate a core dump. */ +#undef DEBUG + +/* These are here to work with glibc -- Don't change these... */ +#undef FNMATCH_BROKEN +#undef GLOB_BROKEN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if !defined(FNMATCH_BROKEN) +#include +#endif +#if !defined(GLOB_BROKEN) +#include +#endif + +#ifdef JOBS +#include +#endif + +#include "busybox.h" +#include "cmdedit.h" + +/* + * 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 */ + +/* Syntax classes for is_ functions */ +#define ISDIGIT 01 /* a digit */ +#define ISUPPER 02 /* an upper case letter */ +#define ISLOWER 04 /* a lower case letter */ +#define ISUNDER 010 /* an underscore */ +#define ISSPECL 020 /* the name of a special parameter */ + +#define SYNBASE 130 +#define PEOF -130 + +#define PEOA -129 + +#define TEOF 0 +#define TNL 1 +#define TSEMI 2 +#define TBACKGND 3 +#define TAND 4 +#define TOR 5 +#define TPIPE 6 +#define TLP 7 +#define TRP 8 +#define TENDCASE 9 +#define TENDBQUOTE 10 +#define TREDIR 11 +#define TWORD 12 +#define TASSIGN 13 +#define TNOT 14 +#define TCASE 15 +#define TDO 16 +#define TDONE 17 +#define TELIF 18 +#define TELSE 19 +#define TESAC 20 +#define TFI 21 +#define TFOR 22 +#define TIF 23 +#define TIN 24 +#define TTHEN 25 +#define TUNTIL 26 +#define TWHILE 27 +#define TBEGIN 28 +#define TEND 29 + + +#define BASESYNTAX (basesyntax + SYNBASE) +#define DQSYNTAX (dqsyntax + SYNBASE) +#define SQSYNTAX (sqsyntax + SYNBASE) +#define ARISYNTAX (arisyntax + SYNBASE) + +/* control characters in argument strings */ +#define CTLESC '\201' +#define CTLVAR '\202' +#define CTLENDVAR '\203' +#define CTLBACKQ '\204' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ +/* CTLBACKQ | CTLQUOTE == '\205' */ +#define CTLARI '\206' +#define CTLENDARI '\207' +#define CTLQUOTEMARK '\210' + +#define is_digit(c) ((c)>='0' && (c)<='9') +#define is_alpha(c) (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c))) +#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c)))) +#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c)))) +#define is_special(c) ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT)) +#define digit_val(c) ((c) - '0') + + +#define _DIAGASSERT(x) + + + +#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 */ + + +/* 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 VSTRIMLEFT 0x6 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */ +#define VSTRIMRIGHT 0x8 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ +#define VSLENGTH 0xa /* ${#var} */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output to pipe */ + +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ + +#if defined(BSD) +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) +#endif + +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ + +#ifndef ALIGN +union align { + int i; + char *cp; +}; + +#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1)) +#endif + +#ifdef BB_LOCALE_SUPPORT +#include +static void change_lc_all(const char *value); +static void change_lc_ctype(const char *value); +#endif + +/* + * 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. :-)) + */ + +static void onint (void); +static volatile int suppressint; +static volatile int intpending; + +#define INTOFF suppressint++ +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define INTON { if (--suppressint == 0 && intpending) onint(); } +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#else +static void __inton (void); +static void forceinton (void); +#define INTON __inton() +#define FORCEINTON forceinton() +#endif + +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + + +typedef void *pointer; +#ifndef NULL +#define NULL (void *)0 +#endif + +static inline pointer ckmalloc (int sz) { return xmalloc(sz); } +static inline pointer ckrealloc(void *p, int sz) { return xrealloc(p, sz); } +static inline char * savestr (const char *s) { return xstrdup(s); } + +static pointer stalloc (int); +static void stunalloc (pointer); +static void ungrabstackstr (char *, char *); +static char * growstackstr(void); +static char * makestrspace(size_t newlen); +static char *sstrdup (const char *); + +/* + * 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. + */ + +#define MINSIZE 504 /* minimum size of a block */ + + +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 int stacknleft = MINSIZE; + + +#define equal(s1, s2) (strcmp(s1, s2) == 0) + +#define stackblock() stacknxt +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() + +#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) +#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(n); } +#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0')) + + +#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) +#define STUNPUTC(p) (++sstrnleft, --p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) +#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) + +#define ckfree(p) free((pointer)(p)) + + +#ifdef DEBUG +#define TRACE(param) trace param +static void trace (const char *, ...); +static void trargs (char **); +static void showtree (union node *); +static void trputc (int); +static void trputs (const char *); +static void opentrace (void); +#else +#define TRACE(param) +#endif + +#define NSEMI 0 +#define NCMD 1 +#define NPIPE 2 +#define NREDIR 3 +#define NBACKGND 4 +#define NSUBSHELL 5 +#define NAND 6 +#define NOR 7 +#define NIF 8 +#define NWHILE 9 +#define NUNTIL 10 +#define NFOR 11 +#define NCASE 12 +#define NCLIST 13 +#define NDEFUN 14 +#define NARG 15 +#define NTO 16 +#define NFROM 17 +#define NFROMTO 18 +#define NAPPEND 19 +#define NTOOV 20 +#define NTOFD 21 +#define NFROMFD 22 +#define NHERE 23 +#define NXHERE 24 +#define NNOT 25 + +/* + * 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 NOPTS 16 + +static char optet_vals[NOPTS]; + +static const char * const optlist[NOPTS] = { + "e" "errexit", + "f" "noglob", + "I" "ignoreeof", + "i" "interactive", + "m" "monitor", + "n" "noexec", + "s" "stdin", + "x" "xtrace", + "v" "verbose", + "V" "vi", + "E" "emacs", + "C" "noclobber", + "a" "allexport", + "b" "notify", + "u" "nounset", + "q" "quietprofile" +}; + +#define optent_name(optent) (optent+1) +#define optent_letter(optent) optent[0] +#define optent_val(optent) optet_vals[optent] + +#define eflag optent_val(0) +#define fflag optent_val(1) +#define Iflag optent_val(2) +#define iflag optent_val(3) +#define mflag optent_val(4) +#define nflag optent_val(5) +#define sflag optent_val(6) +#define xflag optent_val(7) +#define vflag optent_val(8) +#define Vflag optent_val(9) +#define Eflag optent_val(10) +#define Cflag optent_val(11) +#define aflag optent_val(12) +#define bflag optent_val(13) +#define uflag optent_val(14) +#define qflag optent_val(15) + + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + + +struct nbinary { + int type; + union node *ch1; + union node *ch2; +}; + + +struct ncmd { + int type; + int backgnd; + 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 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 nbinary nbinary; + struct ncmd ncmd; + struct npipe npipe; + struct nredir nredir; + struct nif nif; + struct nfor nfor; + struct ncase ncase; + struct nclist nclist; + struct narg narg; + struct nfile nfile; + struct ndup ndup; + struct nhere nhere; + struct nnot nnot; +}; + + +struct nodelist { + struct nodelist *next; + union node *n; +}; + +struct 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 */ +}; + +struct cmdentry { + int cmdtype; + union param { + int index; + union node *func; + const struct builtincmd *cmd; + } u; +}; + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; +#ifdef 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 */ +}; + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; + struct stackmark *marknext; +}; + +struct shparam { + int nparam; /* # of positional parameters (without $0) */ + unsigned char malloc; /* if parameter list dynamically allocated */ + char **p; /* parameter list */ + int optind; /* next parameter to be processed by getopts */ + int optoff; /* used by getopts */ +}; + +/* + * 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 int exerrno = 0; /* Last exec error */ + + +static void tryexec (char *, char **, char **); +static void printentry (struct tblentry *, int); +static void clearcmdentry (int); +static struct tblentry *cmdlookup (const char *, int); +static void delete_cmd_entry (void); +static int path_change (const char *, int *); + + +static void flushall (void); +static void out2fmt (const char *, ...) + __attribute__((__format__(__printf__,1,2))); +static int xwrite (int, const char *, int); + +static void outstr (const char *p, FILE *file) { fputs(p, file); } +static void out1str(const char *p) { outstr(p, stdout); } +static void out2str(const char *p) { outstr(p, stderr); } + +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define out2c(c) putc((c), stderr) +#else +static void out2c(int c) { putc(c, stderr); } +#endif + +/* syntax table used when not in quotes */ +static const char basesyntax[257] = { + CENDFILE, CSPCL, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CSPCL, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CSPCL, CWORD, + CDQUOTE, CWORD, CVAR, CWORD, + CSPCL, CSQUOTE, CSPCL, CSPCL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CSPCL, CSPCL, CWORD, + CSPCL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CBACK, CWORD, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CSPCL, CENDVAR, + CWORD +}; + +/* syntax table used when in double quotes */ +static const char dqsyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CCTL, + CENDQUOTE,CWORD, CVAR, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CCTL, CBACK, CCTL, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CENDVAR, + CCTL +}; + +/* syntax table used when in single quotes */ +static const char sqsyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CCTL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CENDQUOTE,CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CCTL, CCTL, CCTL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL +}; + +/* syntax table used when in arithmetic */ +static const char arisyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CDQUOTE, CWORD, CVAR, CWORD, + CWORD, CSQUOTE, CLP, CRP, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CBACK, CWORD, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CENDVAR, + CWORD +}; + +/* character classification table */ +static const char is_type[257] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, ISSPECL, + 0, ISSPECL, ISSPECL, 0, + 0, 0, 0, 0, + ISSPECL, 0, 0, ISSPECL, + 0, 0, ISDIGIT, ISDIGIT, + ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, + ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, + 0, 0, 0, 0, + 0, ISSPECL, ISSPECL, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, 0, 0, 0, + 0, ISUNDER, 0, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, 0, 0, 0, + 0 +}; + +/* Array indicating which tokens mark the end of a list */ +static const char tokendlist[] = { + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, +}; + +static const char *const tokname[] = { + "end of file", + "newline", + "\";\"", + "\"&\"", + "\"&&\"", + "\"||\"", + "\"|\"", + "\"(\"", + "\")\"", + "\";;\"", + "\"`\"", + "redirection", + "word", + "assignment", + "\"!\"", + "\"case\"", + "\"do\"", + "\"done\"", + "\"elif\"", + "\"else\"", + "\"esac\"", + "\"fi\"", + "\"for\"", + "\"if\"", + "\"in\"", + "\"then\"", + "\"until\"", + "\"while\"", + "\"{\"", + "\"}\"", +}; + +#define KWDOFFSET 14 + +static const char *const parsekwd[] = { + "!", + "case", + "do", + "done", + "elif", + "else", + "esac", + "fi", + "for", + "if", + "in", + "then", + "until", + "while", + "{", + "}" +}; + + +static int plinno = 1; /* input line number */ + +static int parselleft; /* copy of parsefile->lleft */ + +static struct parsefile basepf; /* top level input file */ +static char basebuf[BUFSIZ]; /* buffer for top level input file */ +static struct parsefile *parsefile = &basepf; /* current input file */ + +/* + * 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 tokpushback; /* last token pushed back */ +#define NEOF ((union node *)&tokpushback) +static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ + + +static void error (const char *, ...) __attribute__((__noreturn__)); +static void exerror (int, const char *, ...) __attribute__((__noreturn__)); +static void shellexec (char **, char **, const char *, int) + __attribute__((noreturn)); +static void exitshell (int) __attribute__((noreturn)); + +static int goodname(const char *); +static void ignoresig (int); +static void onsig (int); +static void dotrap (void); +static int decode_signal (const char *, int); + +static void shprocvar(void); +static void deletefuncs(void); +static void setparam (char **); +static void freeparam (volatile struct shparam *); + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 +#define SKIPFILE 4 + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDBUILTIN 1 /* command is a shell builtin */ +#define CMDFUNCTION 2 /* command is a shell function */ + +#define DO_ERR 1 /* find_command prints errors */ +#define DO_ABS 2 /* find_command checks absolute paths */ +#define DO_NOFUN 4 /* find_command ignores functions */ +#define DO_BRUTE 8 /* find_command ignores hash table */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is staticly allocated */ +#define VTEXTFIXED 0x08 /* text is staticly 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 */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + 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 */ + char *text; /* saved text */ +}; + + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +#define rmescapes(p) _rmescapes((p), 0) +static char *_rmescapes (char *, int); +#else +static void rmescapes (char *); +#endif + +static int casematch (union node *, const char *); +static void clearredir(void); +static void popstring(void); +static void readcmdfile (const char *); + +static int number (const char *); +static int is_number (const char *, int *num); +static char *single_quote (const char *); +static int nextopt (const char *); + +static void redirect (union node *, int); +static void popredir (void); +static int dup_as_newfd (int, int); + +static void changepath(const char *newval); +static void getoptsreset(const char *value); + + +static int parsenleft; /* copy of parsefile->nleft */ +static char *parsenextc; /* copy of parsefile->nextc */ +static int rootpid; /* pid of main shell */ +static int rootshell; /* true if we aren't a child of the main shell */ + +static const char spcstr[] = " "; +static const char snlfmt[] = "%s\n"; + +static int sstrnleft; +static int herefd = -1; + +static struct localvar *localvars; + +static struct var vifs; +static struct var vmail; +static struct var vmpath; +static struct var vpath; +static struct var vps1; +static struct var vps2; +static struct var voptind; +#ifdef BB_LOCALE_SUPPORT +static struct var vlc_all; +static struct var vlc_ctype; +#endif + +struct varinit { + struct var *var; + int flags; + const char *text; + void (*func) (const char *); +}; + +static const char defpathvar[] = + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; +#define defpath (defpathvar + 5) + +#ifdef IFS_BROKEN +static const char defifsvar[] = "IFS= \t\n"; +#define defifs (defifsvar + 4) +#else +static const char defifs[] = " \t\n"; +#endif + +static const struct varinit varinit[] = { +#ifdef IFS_BROKEN + { &vifs, VSTRFIXED|VTEXTFIXED, defifsvar, +#else + { &vifs, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=", +#endif + NULL }, + { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", + NULL }, + { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", + NULL }, + { &vpath, VSTRFIXED|VTEXTFIXED, defpathvar, + changepath }, + /* + * vps1 depends on uid + */ + { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", + NULL }, + { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", + getoptsreset }, +#ifdef BB_LOCALE_SUPPORT + { &vlc_all, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL=", + change_lc_all }, + { &vlc_ctype, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE=", + change_lc_ctype }, +#endif + { NULL, 0, NULL, + NULL } +}; + +#define VTABSIZE 39 + +static struct var *vartab[VTABSIZE]; + +/* + * 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 optindval() (voptind.text + 7) + +#define mpathset() ((vmpath.flags & VUNSET) == 0) + +static void initvar (void); +static void setvar (const char *, const char *, int); +static void setvareq (char *, int); +static void listsetvar (struct strlist *); +static const char *lookupvar (const char *); +static const char *bltinlookup (const char *); +static char **environment (void); +static int showvarscmd (int, char **); +static void mklocal (char *); +static void poplocalvars (void); +static int unsetvar (const char *); +static int varequal (const char *, const char *); + + +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 */ + + +#ifdef ASH_ALIAS + +#define ALIASINUSE 1 +#define ALIASDEAD 2 + +#define ATABSIZE 39 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +static struct alias *atab[ATABSIZE]; + +static void setalias (char *, char *); +static struct alias **hashalias (const char *); +static struct alias *freealias (struct alias *); +static struct alias **__lookupalias (const char *); + +static void +setalias(name, val) + char *name, *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(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); +} + +static void +printalias(const struct alias *ap) { + char *p; + + p = single_quote(ap->val); + printf("alias %s=%s\n", ap->name, p); + stunalloc(p); +} + + +/* + * 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) { + out2fmt("%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)) { + out2fmt("%s: %s not found\n", "unalias", *argptr); + i = 1; + } + } + + return (i); +} + +static struct alias ** +hashalias(p) + const char *p; + { + unsigned int hashval; + + hashval = *p << 4; + while (*p) + hashval+= *p++; + return &atab[hashval % ATABSIZE]; +} + +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 struct alias ** +__lookupalias(const char *name) { + struct alias **app = hashalias(name); + + for (; *app; app = &(*app)->next) { + if (equal(name, (*app)->name)) { + break; + } + } + + return app; +} +#endif + +#ifdef ASH_MATH_SUPPORT +/* The generated file arith.c has been replaced with a custom hand + * written implementation written by Aaron Lehmann . + * This is now part of libbb, so that it can be used by all the shells + * in busybox. */ +#define ARITH_NUM 257 +#define ARITH_LPAREN 258 +#define ARITH_RPAREN 259 +#define ARITH_OR 260 +#define ARITH_AND 261 +#define ARITH_BOR 262 +#define ARITH_BXOR 263 +#define ARITH_BAND 264 +#define ARITH_EQ 265 +#define ARITH_NE 266 +#define ARITH_LT 267 +#define ARITH_GT 268 +#define ARITH_GE 269 +#define ARITH_LE 270 +#define ARITH_LSHIFT 271 +#define ARITH_RSHIFT 272 +#define ARITH_ADD 273 +#define ARITH_SUB 274 +#define ARITH_MUL 275 +#define ARITH_DIV 276 +#define ARITH_REM 277 +#define ARITH_UNARYMINUS 278 +#define ARITH_UNARYPLUS 279 +#define ARITH_NOT 280 +#define ARITH_BNOT 281 + +static void expari (int); +#endif + +static char *trap[NSIG]; /* trap handler commands */ +static char sigmode[NSIG - 1]; /* current value of signal */ +static char gotsig[NSIG - 1]; /* indicates specified signal received */ +static int pendingsigs; /* indicates some signal received */ + +/* + * This file was generated by the mkbuiltins program. + */ + +#ifdef JOBS +static int bgcmd (int, char **); +static int fgcmd (int, char **); +static int killcmd (int, char **); +#endif +static int bltincmd (int, char **); +static int cdcmd (int, char **); +static int breakcmd (int, char **); +#ifdef 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 histcmd (int, char **); +static int hashcmd (int, char **); +static int helpcmd (int, char **); +static int jobscmd (int, char **); +static int localcmd (int, char **); +#ifndef BB_PWD +static int pwdcmd (int, char **); +#endif +static int readcmd (int, char **); +static int returncmd (int, char **); +static int setcmd (int, char **); +static int setvarcmd (int, char **); +static int shiftcmd (int, char **); +static int trapcmd (int, char **); +static int umaskcmd (int, char **); +#ifdef ASH_ALIAS +static int aliascmd (int, char **); +static int unaliascmd (int, char **); +#endif +static int unsetcmd (int, char **); +static int waitcmd (int, char **); +static int ulimitcmd (int, char **); +static int timescmd (int, char **); +#ifdef ASH_MATH_SUPPORT +static int letcmd (int, char **); +#endif +static int typecmd (int, char **); +#ifdef ASH_GETOPTS +static int getoptscmd (int, char **); +#endif + +#ifndef BB_TRUE_FALSE +static int true_main (int, char **); +static int false_main (int, char **); +#endif + +static void setpwd (const char *, int); + + +#define BUILTIN_NOSPEC "0" +#define BUILTIN_SPECIAL "1" +#define BUILTIN_REGULAR "2" +#define BUILTIN_ASSIGN "4" +#define BUILTIN_SPEC_ASSG "5" +#define BUILTIN_REG_ASSG "6" + +#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1) +#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2) +#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4) + +struct builtincmd { + const char *name; + int (*const builtinfunc) (int, char **); + //unsigned flags; +}; + + +/* It is CRUCIAL that this listing be kept in ascii order, otherwise + * the binary search in find_builtin() will stop working. If you value + * your kneecaps, you'll be sure to *make sure* that any changes made + * to this array result in the listing remaining in ascii order. You + * have been warned. + */ +static const struct builtincmd builtincmds[] = { + { BUILTIN_SPECIAL ".", dotcmd }, /* first, see declare DOTCMD */ + { BUILTIN_SPECIAL ":", true_main }, +#ifdef ASH_ALIAS + { BUILTIN_REG_ASSG "alias", aliascmd }, +#endif +#ifdef JOBS + { BUILTIN_REGULAR "bg", bgcmd }, +#endif + { BUILTIN_SPECIAL "break", breakcmd }, + { BUILTIN_SPECIAL "builtin", bltincmd }, + { BUILTIN_REGULAR "cd", cdcmd }, + { BUILTIN_NOSPEC "chdir", cdcmd }, +#ifdef ASH_CMDCMD + { BUILTIN_REGULAR "command", commandcmd }, +#endif + { BUILTIN_SPECIAL "continue", breakcmd }, + { BUILTIN_SPECIAL "eval", evalcmd }, + { BUILTIN_SPECIAL "exec", execcmd }, + { BUILTIN_SPECIAL "exit", exitcmd }, + { BUILTIN_SPEC_ASSG "export", exportcmd }, + { BUILTIN_REGULAR "false", false_main }, + { BUILTIN_REGULAR "fc", histcmd }, +#ifdef JOBS + { BUILTIN_REGULAR "fg", fgcmd }, +#endif +#ifdef ASH_GETOPTS + { BUILTIN_REGULAR "getopts", getoptscmd }, +#endif + { BUILTIN_NOSPEC "hash", hashcmd }, + { BUILTIN_NOSPEC "help", helpcmd }, + { BUILTIN_REGULAR "jobs", jobscmd }, +#ifdef JOBS + { BUILTIN_REGULAR "kill", killcmd }, +#endif +#ifdef ASH_MATH_SUPPORT + { BUILTIN_REGULAR "let", letcmd }, +#endif + { BUILTIN_ASSIGN "local", localcmd }, +#ifndef BB_PWD + { BUILTIN_NOSPEC "pwd", pwdcmd }, +#endif + { BUILTIN_REGULAR "read", readcmd }, + { BUILTIN_SPEC_ASSG "readonly", exportcmd }, + { BUILTIN_SPECIAL "return", returncmd }, + { BUILTIN_SPECIAL "set", setcmd }, + { BUILTIN_NOSPEC "setvar", setvarcmd }, + { BUILTIN_SPECIAL "shift", shiftcmd }, + { BUILTIN_SPECIAL "times", timescmd }, + { BUILTIN_SPECIAL "trap", trapcmd }, + { BUILTIN_REGULAR "true", true_main }, + { BUILTIN_NOSPEC "type", typecmd }, + { BUILTIN_NOSPEC "ulimit", ulimitcmd }, + { BUILTIN_REGULAR "umask", umaskcmd }, +#ifdef ASH_ALIAS + { BUILTIN_REGULAR "unalias", unaliascmd }, +#endif + { BUILTIN_SPECIAL "unset", unsetcmd }, + { BUILTIN_REGULAR "wait", waitcmd }, +}; +#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) ) + +static const struct builtincmd *DOTCMD = &builtincmds[0]; +static struct builtincmd *BLTINCMD; +static struct builtincmd *EXECCMD; +static struct builtincmd *EVALCMD; + +/* states */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ + +/* + * 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; /* status flags (defined above) */ + char *cmd; /* text of command being run */ +}; + + +static int job_warning; /* user was warned about stopped jobs */ + +#ifdef JOBS +static void setjobctl(int enable); +#else +#define setjobctl(on) /* do nothing */ +#endif + + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ + short nprocs; /* number of processes */ + short pgrp; /* process group of this job */ + char state; /* true if job is finished */ + char used; /* true if this entry is in used */ + char changed; /* true if status has changed */ +#ifdef JOBS + char jobctl; /* job running under job control */ +#endif +}; + +static struct job *jobtab; /* array of jobs */ +static int njobs; /* size of array */ +static int backgndpid = -1; /* pid of last background process */ +#ifdef JOBS +static int initialpgrp; /* pgrp of shell on invocation */ +static int curjob; /* current job */ +static int jobctl; +#endif +static int intreceived; + +static struct job *makejob (const union node *, int); +static int forkshell (struct job *, const union node *, int); +static int waitforjob (struct job *); + +static int docd (char *, int); +static char *getcomponent (void); +static void updatepwd (const char *); +static void getpwd (void); + +static char *padvance (const char **, const char *); + +static char nullstr[1]; /* zero length string */ +static char *curdir = nullstr; /* current working directory */ +static char *cdcomppath; + +static int +cdcmd(argc, argv) + int argc; + char **argv; +{ + const char *dest; + const char *path; + char *p; + struct stat statb; + int print = 0; + + nextopt(nullstr); + if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL) + error("HOME not set"); + if (*dest == '\0') + dest = "."; + if (dest[0] == '-' && dest[1] == '\0') { + dest = bltinlookup("OLDPWD"); + if (!dest || !*dest) { + dest = curdir; + } + print = 1; + if (dest) + print = 1; + else + dest = "."; + } + if (*dest == '/' || (path = bltinlookup("CDPATH")) == NULL) + path = nullstr; + while ((p = padvance(&path, dest)) != NULL) { + if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { + if (!print) { + /* + * XXX - rethink + */ + if (p[0] == '.' && p[1] == '/' && p[2] != '\0') + p += 2; + print = strcmp(p, dest); + } + if (docd(p, print) >= 0) + return 0; + + } + } + error("can't cd to %s", dest); + /* NOTREACHED */ +} + + +/* + * Actually do the chdir. In an interactive shell, print the + * directory name if "print" is nonzero. + */ + +static int +docd(dest, print) + char *dest; + int print; +{ + char *p; + char *q; + char *component; + struct stat statb; + int first; + int badstat; + + TRACE(("docd(\"%s\", %d) called\n", dest, print)); + + /* + * Check each component of the path. If we find a symlink or + * something we can't stat, clear curdir to force a getcwd() + * next time we get the value of the current directory. + */ + badstat = 0; + cdcomppath = sstrdup(dest); + STARTSTACKSTR(p); + if (*dest == '/') { + STPUTC('/', p); + cdcomppath++; + } + first = 1; + while ((q = getcomponent()) != NULL) { + if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) + continue; + if (! first) + STPUTC('/', p); + first = 0; + component = q; + while (*q) + STPUTC(*q++, p); + if (equal(component, "..")) + continue; + STACKSTRNUL(p); + if ((lstat(stackblock(), &statb) < 0) + || (S_ISLNK(statb.st_mode))) { + /* print = 1; */ + badstat = 1; + break; + } + } + + INTOFF; + if (chdir(dest) < 0) { + INTON; + return -1; + } + updatepwd(badstat ? NULL : dest); + INTON; + if (print && iflag) + printf(snlfmt, curdir); + return 0; +} + + +/* + * Get the next component of the path name pointed to by cdcomppath. + * This routine overwrites the string pointed to by cdcomppath. + */ + +static char * +getcomponent() { + char *p; + char *start; + + if ((p = cdcomppath) == NULL) + return NULL; + start = cdcomppath; + while (*p != '/' && *p != '\0') + p++; + if (*p == '\0') { + cdcomppath = NULL; + } else { + *p++ = '\0'; + cdcomppath = p; + } + return start; +} + + + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. We also call hashcd to let the routines in exec.c know + * that the current directory has changed. + */ + +static void hashcd (void); + +static void +updatepwd(const char *dir) +{ + char *new; + char *p; + size_t len; + + hashcd(); /* update command hash table */ + + /* + * If our argument is NULL, we don't know the current directory + * any more because we traversed a symbolic link or something + * we couldn't stat(). + */ + if (dir == NULL || curdir == nullstr) { + setpwd(0, 1); + return; + } + len = strlen(dir); + cdcomppath = sstrdup(dir); + STARTSTACKSTR(new); + if (*dir != '/') { + p = curdir; + while (*p) + STPUTC(*p++, new); + if (p[-1] == '/') + STUNPUTC(new); + } + while ((p = getcomponent()) != NULL) { + if (equal(p, "..")) { + while (new > stackblock() && (STUNPUTC(new), *new) != '/'); + } else if (*p != '\0' && ! equal(p, ".")) { + STPUTC('/', new); + while (*p) + STPUTC(*p++, new); + } + } + if (new == stackblock()) + STPUTC('/', new); + STACKSTRNUL(new); + setpwd(stackblock(), 1); +} + + +#ifndef BB_PWD +static int +pwdcmd(argc, argv) + int argc; + char **argv; +{ + printf(snlfmt, curdir); + return 0; +} +#endif + +/* + * Find out what the current directory is. If we already know the current + * directory, this routine returns immediately. + */ +static void +getpwd(void) +{ + curdir = xgetcwd(0); + if(curdir==0) + curdir = nullstr; +} + +static void +setpwd(const char *val, int setold) +{ + if (setold) { + setvar("OLDPWD", curdir, VEXPORT); + } + INTOFF; + if (curdir != nullstr) { + free(curdir); + curdir = nullstr; + } + if (!val) { + getpwd(); + } else { + curdir = savestr(val); + } + INTON; + setvar("PWD", curdir, VEXPORT); +} + +/* + * Errors and exceptions. + */ + +/* + * Code to handle exceptions in C. + */ + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exeception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +struct jmploc { + jmp_buf loc; +}; + +/* 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 */ + +static struct jmploc *handler; +static int exception; + +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) __attribute__((__noreturn__)); + +static void +exraise(int e) +{ +#ifdef DEBUG + if (handler == NULL) + abort(); +#endif + flushall(); + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. The call to _exit is necessary because + * there is a short period after a fork before the signal handlers are + * set to the appropriate value for the child. (The test for iflag is + * just defensive programming.) + */ + +static void +onint(void) { + sigset_t mysigset; + + if (suppressint) { + intpending++; + return; + } + intpending = 0; + sigemptyset(&mysigset); + sigprocmask(SIG_SETMASK, &mysigset, NULL); + if (rootshell && iflag) + exraise(EXINT); + else { + signal(SIGINT, SIG_DFL); + raise(SIGINT); + } + /* NOTREACHED */ +} + + +static char *commandname; /* currently executing command */ + +/* + * Exverror is called to raise the error exception. If the first argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ +static void +exverror(int cond, const char *msg, va_list ap) +{ + CLEAR_PENDING_INT; + INTOFF; + +#ifdef DEBUG + if (msg) + TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid())); + else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); +#endif + if (msg) { + if (commandname) + out2fmt("%s: ", commandname); + vfprintf(stderr, msg, ap); + out2c('\n'); + } + 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); +} + + + +/* + * Table of error messages. + */ + +struct errname { + short errcode; /* error number */ + char action; /* operation which encountered the error */ +}; + +/* + * Types of operations (passed to the errmsg routine). + */ + +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + +static const struct errname errormsg[] = { + { EINTR, ALL }, + { EACCES, ALL }, + { EIO, ALL }, + { ENOENT, E_OPEN }, + { ENOENT, E_CREAT }, + { ENOENT, E_EXEC }, + { ENOTDIR, E_OPEN }, + { ENOTDIR, E_CREAT }, + { ENOTDIR, E_EXEC }, + { EISDIR, ALL }, + { EEXIST, E_CREAT }, +#ifdef EMFILE + { EMFILE, ALL }, +#endif + { ENFILE, ALL }, + { ENOSPC, ALL }, +#ifdef EDQUOT + { EDQUOT, ALL }, +#endif +#ifdef ENOSR + { ENOSR, ALL }, +#endif + { ENXIO, ALL }, + { EROFS, ALL }, + { ETXTBSY, ALL }, +#ifdef EAGAIN + { EAGAIN, E_EXEC }, +#endif + { ENOMEM, ALL }, +#ifdef ENOLINK + { ENOLINK, ALL }, +#endif +#ifdef EMULTIHOP + { EMULTIHOP, ALL }, +#endif +#ifdef ECOMM + { ECOMM, ALL }, +#endif +#ifdef ESTALE + { ESTALE, ALL }, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, ALL }, +#endif +#ifdef ELOOP + { ELOOP, ALL }, +#endif + { E2BIG, E_EXEC }, +#ifdef ELIBACC + { ELIBACC, E_EXEC }, +#endif +}; + +#define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname)) + +/* + * 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, int action) +{ + struct errname const *ep; + static char buf[12]; + + for (ep = errormsg ; ep < errormsg+ERRNAME_SIZE; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return strerror(e); + } + + snprintf(buf, sizeof buf, "error %d", e); + return buf; +} + + +#ifdef ASH_OPTIMIZE_FOR_SIZE +static void +__inton() { + if (--suppressint == 0 && intpending) { + onint(); + } +} +static void forceinton (void) { + suppressint = 0; + if (intpending) + onint(); +} +#endif + +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_BACKCMD 04 /* command executing within back quotes */ + +static int evalskip; /* set if we are skipping commands */ +static int skipcount; /* number of levels to skip */ +static int loopnest; /* current loop nesting level */ +static int funcnest; /* depth of function calls */ + + +static struct strlist *cmdenviron; /* environment for builtin command */ +static int exitstatus; /* exit status of last command */ +static int oexitstatus; /* saved exit status */ + +static void evalsubshell (const union node *, int); +static void expredir (union node *); +static void prehash (union node *); +static void eprintlist (struct strlist *); + +static union node *parsecmd(int); +/* + * Called to reset things after an exception. + */ + +/* + * The eval commmand. + */ +static void evalstring (char *, int); + +static int +evalcmd(argc, argv) + int argc; + char **argv; +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + evalstring(p, EV_TESTED); + } + return exitstatus; +} + +/* + * Execute a command or commands contained in a string. + */ + +static void evaltree (union node *, int); +static void setinputstring (char *); +static void popfile (void); +static void setstackmark(struct stackmark *mark); +static void popstackmark(struct stackmark *mark); + + +static void +evalstring(char *s, int flag) +{ + union node *n; + struct stackmark smark; + + setstackmark(&smark); + setinputstring(s); + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, flag); + popstackmark(&smark); + } + popfile(); + popstackmark(&smark); +} + +static struct builtincmd *find_builtin (const char *); +static void expandarg (union node *, struct arglist *, int); +static void calcsize (const union node *); +static union node *copynode (const union node *); + +/* + * Make a copy of a parse tree. + */ + +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 inline union node * +copyfunc(union node *n) +{ + if (n == NULL) + return NULL; + funcblocksize = 0; + funcstringsize = 0; + calcsize(n); + funcblock = ckmalloc(funcblocksize + funcstringsize); + funcstring = (char *) funcblock + funcblocksize; + return copynode(n); +} + +/* + * Free a parse tree. + */ + +static void +freefunc(union node *n) +{ + if (n) + ckfree(n); +} + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name. + */ + +static inline void +addcmdentry(char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp; + + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + INTON; +} + +static inline void +evalloop(const union node *n, int flags) +{ + int status; + + loopnest++; + status = 0; + for (;;) { + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { +skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + if (n->type == NWHILE) { + if (exitstatus != 0) + break; + } else { + if (exitstatus == 0) + break; + } + evaltree(n->nbinary.ch2, flags & EV_TESTED); + status = exitstatus; + if (evalskip) + goto skipping; + } + loopnest--; + exitstatus = status; +} + +static void +evalfor(const 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) { + oexitstatus = exitstatus; + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); + if (evalskip) + goto out; + } + *arglist.lastp = NULL; + + exitstatus = 0; + loopnest++; + for (sp = arglist.list ; sp ; sp = sp->next) { + setvar(n->nfor.var, sp->text, 0); + evaltree(n->nfor.body, flags & EV_TESTED); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; +out: + popstackmark(&smark); +} + +static inline void +evalcase(const union node *n, int flags) +{ + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + oexitstatus = exitstatus; + expandarg(n->ncase.expr, &arglist, EXP_TILDE); + for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.list->text)) { + if (evalskip == 0) { + evaltree(cp->nclist.body, flags); + } + goto out; + } + } + } +out: + popstackmark(&smark); +} + +/* + * 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 inline void +evalpipe(n) + union node *n; +{ + 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++; + 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 (prevfd > 0) { + close(0); + dup_as_newfd(prevfd, 0); + close(prevfd); + if (pip[0] == 0) { + pip[0] = -1; + } + } + if (pip[1] >= 0) { + if (pip[0] >= 0) { + close(pip[0]); + } + if (pip[1] != 1) { + close(1); + dup_as_newfd(pip[1], 1); + close(pip[1]); + } + } + evaltree(lp->n, EV_EXIT); + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + INTON; + if (n->npipe.backgnd == 0) { + INTOFF; + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + INTON; + } +} + +static void find_command (const char *, struct cmdentry *, int, const char *); + +static int +isassignment(const char *word) { + if (!is_name(*word)) { + return 0; + } + do { + word++; + } while (is_in_name(*word)); + return *word == '='; +} + + +static void +evalcommand(union node *cmd, int flags) +{ + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + char **envp; + struct strlist *sp; + int mode; + struct cmdentry cmdentry; + struct job *jp; + char *volatile savecmdname; + volatile struct shparam saveparam; + struct localvar *volatile savelocalvars; + volatile int e; + char *lastarg; + const char *path; + const struct builtincmd *firstbltin; + struct jmploc *volatile savehandler; + struct jmploc jmploc; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &argv; + (void) &argc; + (void) &lastarg; + (void) &flags; +#endif + + /* First expand the arguments. */ + TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); + setstackmark(&smark); + arglist.lastp = &arglist.list; + varlist.lastp = &varlist.list; + arglist.list = 0; + oexitstatus = exitstatus; + exitstatus = 0; + path = pathval(); + for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { + expandarg(argp, &varlist, EXP_VARTILDE); + } + for ( + argp = cmd->ncmd.args; argp && !arglist.list; + argp = argp->narg.next + ) { + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + } + if (argp) { + struct builtincmd *bcmd; + int pseudovarflag; + bcmd = find_builtin(arglist.list->text); + pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd); + for (; argp; argp = argp->narg.next) { + if (pseudovarflag && isassignment(argp->narg.text)) { + expandarg(argp, &arglist, EXP_VARTILDE); + continue; + } + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + } + } + *arglist.lastp = NULL; + *varlist.lastp = NULL; + expredir(cmd->ncmd.redirect); + argc = 0; + for (sp = arglist.list ; sp ; sp = sp->next) + argc++; + argv = stalloc(sizeof (char *) * (argc + 1)); + + for (sp = arglist.list ; sp ; sp = sp->next) { + TRACE(("evalcommand arg: %s\n", sp->text)); + *argv++ = sp->text; + } + *argv = NULL; + lastarg = NULL; + if (iflag && funcnest == 0 && argc > 0) + lastarg = argv[-1]; + argv -= argc; + + /* Print the command if xflag is set. */ + if (xflag) { + out2c('+'); + eprintlist(varlist.list); + eprintlist(arglist.list); + out2c('\n'); + } + + /* Now locate the command. */ + if (argc == 0) { + cmdentry.cmdtype = CMDBUILTIN; + firstbltin = cmdentry.u.cmd = BLTINCMD; + } else { + const char *oldpath; + int findflag = DO_ERR; + int oldfindflag; + + /* + * Modify the command lookup path, if a PATH= assignment + * is present + */ + for (sp = varlist.list ; sp ; sp = sp->next) + if (varequal(sp->text, defpathvar)) { + path = sp->text + 5; + findflag |= DO_BRUTE; + } + oldpath = path; + oldfindflag = findflag; + firstbltin = 0; + for(;;) { + find_command(argv[0], &cmdentry, findflag, path); + if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ + exitstatus = 127; + goto out; + } + /* implement bltin and command here */ + if (cmdentry.cmdtype != CMDBUILTIN) { + break; + } + if (!firstbltin) { + firstbltin = cmdentry.u.cmd; + } + if (cmdentry.u.cmd == BLTINCMD) { + for(;;) { + struct builtincmd *bcmd; + + argv++; + if (--argc == 0) + goto found; + if (!(bcmd = find_builtin(*argv))) { + out2fmt("%s: not found\n", *argv); + exitstatus = 127; + goto out; + } + cmdentry.u.cmd = bcmd; + if (bcmd != BLTINCMD) + break; + } + } + if (cmdentry.u.cmd == find_builtin("command")) { + argv++; + if (--argc == 0) { + goto found; + } + if (*argv[0] == '-') { + if (!equal(argv[0], "-p")) { + argv--; + argc++; + break; + } + argv++; + if (--argc == 0) { + goto found; + } + path = defpath; + findflag |= DO_BRUTE; + } else { + path = oldpath; + findflag = oldfindflag; + } + findflag |= DO_NOFUN; + continue; + } +found: + break; + } + } + + /* Fork off a child process if necessary. */ + if (cmd->ncmd.backgnd + || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) + ) { + jp = makejob(cmd, 1); + mode = cmd->ncmd.backgnd; + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ + flags |= EV_EXIT; + } + + /* This is the child process if a fork occurred. */ + /* Execute the command. */ + if (cmdentry.cmdtype == CMDFUNCTION) { +#ifdef DEBUG + trputs("Shell function: "); trargs(argv); +#endif + exitstatus = oexitstatus; + redirect(cmd->ncmd.redirect, REDIR_PUSH); + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + INTOFF; + savelocalvars = localvars; + localvars = NULL; + INTON; + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) { + freeparam((volatile struct shparam *) + &saveparam); + } else { + saveparam.optind = shellparam.optind; + saveparam.optoff = shellparam.optoff; + freeparam(&shellparam); + shellparam = saveparam; + } + poplocalvars(); + localvars = savelocalvars; + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + for (sp = varlist.list ; sp ; sp = sp->next) + mklocal(sp->text); + funcnest++; + evaltree(cmdentry.u.func, flags & EV_TESTED); + funcnest--; + INTOFF; + poplocalvars(); + localvars = savelocalvars; + saveparam.optind = shellparam.optind; + saveparam.optoff = shellparam.optoff; + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + popredir(); + INTON; + if (evalskip == SKIPFUNC) { + evalskip = 0; + skipcount = 0; + } + if (flags & EV_EXIT) + exitshell(exitstatus); + } else if (cmdentry.cmdtype == CMDBUILTIN) { +#ifdef DEBUG + trputs("builtin command: "); trargs(argv); +#endif + mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH; + redirect(cmd->ncmd.redirect, mode); + savecmdname = commandname; + if (IS_BUILTIN_SPECIAL(firstbltin)) { + listsetvar(varlist.list); + } else { + cmdenviron = varlist.list; + } + e = -1; + if (setjmp(jmploc.loc)) { + e = exception; + exitstatus = (e == EXINT)? SIGINT+128 : 2; + goto cmddone; + } + savehandler = handler; + handler = &jmploc; + commandname = argv[0]; + argptr = argv + 1; + optptr = NULL; /* initialize nextopt */ + exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv); + flushall(); +cmddone: + cmdenviron = NULL; + if (e != EXSHELLPROC) { + commandname = savecmdname; + if (flags & EV_EXIT) + exitshell(exitstatus); + } + handler = savehandler; + if (e != -1) { + if ((e != EXERROR && e != EXEXEC) + || cmdentry.u.cmd == BLTINCMD + || cmdentry.u.cmd == DOTCMD + || cmdentry.u.cmd == EVALCMD + || cmdentry.u.cmd == EXECCMD) + exraise(e); + FORCEINTON; + } + if (cmdentry.u.cmd != EXECCMD) + popredir(); + } else { +#ifdef DEBUG + trputs("normal command: "); trargs(argv); +#endif + redirect(cmd->ncmd.redirect, 0); + clearredir(); + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, path, cmdentry.u.index); + } + goto out; + +parent: /* parent process gets here (if we forked) */ + if (mode == 0) { /* argument to fork */ + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } + +out: + if (lastarg) + setvar("_", lastarg, 0); + popstackmark(&smark); +} + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ +static void +evaltree(n, flags) + union node *n; + int flags; +{ + int checkexit = 0; + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + goto out; + } + TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, flags & EV_TESTED); + if (evalskip) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NAND: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus != 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NOR: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus == 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NREDIR: + expredir(n->nredir.redirect); + redirect(n->nredir.redirect, REDIR_PUSH); + evaltree(n->nredir.n, flags); + popredir(); + break; + case NSUBSHELL: + evalsubshell(n, flags); + break; + case NBACKGND: + evalsubshell(n, flags); + break; + case NIF: { + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) + evaltree(n->nif.ifpart, flags); + else if (n->nif.elsepart) + evaltree(n->nif.elsepart, flags); + else + exitstatus = 0; + break; + } + case NWHILE: + case NUNTIL: + evalloop(n, flags); + break; + case NFOR: + evalfor(n, flags); + break; + case NCASE: + evalcase(n, flags); + break; + case NDEFUN: { + struct builtincmd *bcmd; + struct cmdentry entry; + if ( + (bcmd = find_builtin(n->narg.text)) && + IS_BUILTIN_SPECIAL(bcmd) + ) { + out2fmt("%s is a special built-in\n", n->narg.text); + exitstatus = 1; + break; + } + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(n->narg.next); + addcmdentry(n->narg.text, &entry); + exitstatus = 0; + break; + } + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + exitstatus = !exitstatus; + break; + + case NPIPE: + evalpipe(n); + checkexit = 1; + break; + case NCMD: + evalcommand(n, flags); + checkexit = 1; + break; +#ifdef DEBUG + default: + printf("Node type = %d\n", n->type); + break; +#endif + } +out: + if (pendingsigs) + dotrap(); + if ( + flags & EV_EXIT || + (checkexit && eflag && exitstatus && !(flags & EV_TESTED)) + ) + exitshell(exitstatus); +} + +/* + * Kick off a subshell to evaluate a tree. + */ + +static void +evalsubshell(const union node *n, int flags) +{ + struct job *jp; + int backgnd = (n->type == NBACKGND); + + expredir(n->nredir.redirect); + jp = makejob(n, 1); + if (forkshell(jp, n, backgnd) == 0) { + if (backgnd) + flags &=~ EV_TESTED; + redirect(n->nredir.redirect, 0); + evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ + } + if (! backgnd) { + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } +} + +/* + * Compute the names of the files in a redirection list. + */ + +static void fixredir(union node *n, const char *text, int err); + +static void +expredir(union node *n) +{ + union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + struct arglist fn; + fn.lastp = &fn.list; + oexitstatus = exitstatus; + switch (redir->type) { + case NFROMTO: + case NFROM: + case NTO: + case NAPPEND: + case NTOOV: + 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; + } + } +} + + +/* + * 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 pip[2]; + struct job *jp; + struct stackmark smark; /* unnecessary */ + + setstackmark(&smark); + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + exitstatus = 0; + goto out; + } + exitstatus = 0; + 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); + dup_as_newfd(pip[1], 1); + close(pip[1]); + } + eflag = 0; + evaltree(n, EV_EXIT); + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; +out: + popstackmark(&smark); + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", + result->fd, result->buf, result->nleft, result->jp)); +} + + +/* + * Execute a simple command. + */ + +/* + * 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(n) + 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, or a bltin command with no arguments. Set the + * specified variables. + */ + +int +bltincmd(argc, argv) + int argc; + char **argv; +{ + /* + * Preserve exitstatus of a previous possible redirection + * as POSIX mandates + */ + return exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +static int +breakcmd(argc, argv) + int argc; + char **argv; +{ + int n = argc > 1 ? number(argv[1]) : 1; + + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +static int +returncmd(argc, argv) + int argc; + char **argv; +{ + int ret = argc > 1 ? number(argv[1]) : oexitstatus; + + 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; + } +} + + +#ifndef BB_TRUE_FALSE +static int +false_main(argc, argv) + int argc; + char **argv; +{ + return 1; +} + + +static int +true_main(argc, argv) + int argc; + char **argv; +{ + return 0; +} +#endif + +/* + * Controls whether the shell is interactive or not. + */ + +static void setsignal(int signo); +static void chkmail(int silent); + + +static void +setinteractive(int on) +{ + static int is_interactive; + static int do_banner=0; + + if (on == is_interactive) + return; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + chkmail(1); + is_interactive = on; + if (do_banner==0 && is_interactive) { + /* Looks like they want an interactive shell */ + printf( "\n\n" BB_BANNER " Built-in shell (ash)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); + do_banner=1; + } +} + +static void +optschanged(void) +{ + setinteractive(iflag); + setjobctl(mflag); +} + + +static int +execcmd(argc, argv) + int argc; + char **argv; +{ + if (argc > 1) { + struct strlist *sp; + + iflag = 0; /* exit on error */ + mflag = 0; + optschanged(); + for (sp = cmdenviron; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + shellexec(argv + 1, environment(), pathval(), 0); + } + return 0; +} + +static void +eprintlist(struct strlist *sp) +{ + for (; sp; sp = sp->next) { + out2fmt(" %s",sp->text); + } +} + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + */ + +static const char *pathopt; /* set by padvance */ + +static void +shellexec(argv, envp, path, idx) + char **argv, **envp; + const char *path; + int idx; +{ + char *cmdname; + int e; + + if (strchr(argv[0], '/') != NULL) { + tryexec(argv[0], argv, envp); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--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; + } + exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); + /* NOTREACHED */ +} + +/* + * Clear traps on a fork. + */ +static void +clear_traps(void) { + char **tp; + + for (tp = trap ; tp < &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + INTON; + } + } +} + + +static void +initshellproc(void) { + +#ifdef ASH_ALIAS + /* from alias.c: */ + { + rmaliases(); + } +#endif + /* from eval.c: */ + { + exitstatus = 0; + } + + /* from exec.c: */ + { + deletefuncs(); + } + + /* from jobs.c: */ + { + backgndpid = -1; +#ifdef JOBS + jobctl = 0; +#endif + } + + /* from options.c: */ + { + int i; + + for (i = 0; i < NOPTS; i++) + optent_val(i) = 0; + optschanged(); + + } + + /* from redir.c: */ + { + clearredir(); + } + + /* from trap.c: */ + { + char *sm; + + clear_traps(); + for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } + } + + /* from var.c: */ + { + shprocvar(); + } +} + +static int preadbuffer(void); +static void pushfile (void); + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) +static int +pgetc(void) +{ + return pgetc_macro(); +} +#else +static int +pgetc_macro(void) +{ + return --parsenleft >= 0? *parsenextc++ : preadbuffer(); +} + +static inline int +pgetc(void) +{ + return pgetc_macro(); +} +#endif + + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +static void +pungetc() { + parsenleft++; + parsenextc--; +} + + +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() { + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} + + +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ + +static void +setinputfd(fd, push) + int fd, push; +{ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); + if (push) { + pushfile(); + parsefile->buf = 0; + } else { + closescript(); + while (parsefile->strpush) + popstring(); + } + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(BUFSIZ); + parselleft = parsenleft = 0; + plinno = 1; +} + + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +static void +setinputfile(const char *fname, int push) +{ + int fd; + int myfileno2; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s", fname); + if (fd < 10) { + myfileno2 = dup_as_newfd(fd, 10); + close(fd); + if (myfileno2 < 0) + error("Out of file descriptors"); + fd = myfileno2; + } + setinputfd(fd, push); + INTON; +} + + +static void +tryexec(char *cmd, char **argv, char **envp) +{ + int e; + +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + char *name = cmd; + char** argv_l=argv; + int argc_l; +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + name = get_last_path_component(name); +#endif + argv_l=envp; + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++) + putenv(*argv_l); + argv_l=argv; + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++) + optind = 1; + run_applet_by_name(name, argc_l, argv); +#endif + execve(cmd, argv, envp); + e = errno; + if (e == ENOEXEC) { + INTOFF; + initshellproc(); + setinputfile(cmd, 0); + commandname = arg0 = savestr(argv[0]); + setparam(argv + 1); + exraise(EXSHELLPROC); + } + errno = e; +} + +static char *commandtext (const union node *); + +/* + * 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 const char *pathopt; + +static void growstackblock(void); + + +static char * +padvance(const char **path, const char *name) +{ + const char *p; + char *q; + const char *start; + int len; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start ; *p && *p != ':' && *p != '%' ; p++); + len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ + while (stackblocksize() < len) + growstackblock(); + q = stackblock(); + if (p != start) { + 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); +} + +/* + * Wrapper around strcmp for qsort/bsearch/... + */ +static int +pstrcmp(const void *a, const void *b) +{ + return strcmp((const char *) a, *(const char *const *) b); +} + +/* + * Find a keyword is in a sorted array. + */ + +static const char *const * +findkwd(const char *s) +{ + return bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *), + sizeof(const char *), pstrcmp); +} + + +/*** Command hashing code ***/ + + +static int +hashcmd(argc, argv) + int argc; + char **argv; +{ + struct tblentry **pp; + struct tblentry *cmdp; + int c; + int verbose; + struct cmdentry entry; + char *name; +#ifdef ASH_ALIAS + const struct alias *ap; +#endif + + verbose = 0; + while ((c = nextopt("rvV")) != '\0') { + if (c == 'r') { + clearcmdentry(0); + return 0; + } else if (c == 'v' || c == 'V') { + verbose = c; + } + } + if (*argptr == NULL) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype != CMDBUILTIN) { + printentry(cmdp, verbose); + } + } + } + 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(); +#ifdef ASH_ALIAS + /* Then look at the aliases */ + if ((ap = lookupalias(name, 0)) != NULL) { + if (verbose=='v') + printf("%s is an alias for %s\n", name, ap->val); + else + printalias(ap); + continue; + } +#endif + /* First look at the keywords */ + if (findkwd(name)!=0) { + if (verbose=='v') + printf("%s is a shell keyword\n", name); + else + printf(snlfmt, name); + continue; + } + + find_command(name, &entry, DO_ERR, pathval()); + if (entry.cmdtype == CMDUNKNOWN) c = 1; + else if (verbose) { + cmdp = cmdlookup(name, 0); + if (cmdp) printentry(cmdp, verbose=='v'); + flushall(); + } + } + return c; +} + +static void +printentry(cmdp, verbose) + struct tblentry *cmdp; + int verbose; + { + int idx; + const char *path; + char *name; + + printf("%s%s", cmdp->cmdname, (verbose ? " is " : "")); + if (cmdp->cmdtype == CMDNORMAL) { + idx = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--idx >= 0); + if(verbose) + out1str(name); + } else if (cmdp->cmdtype == CMDBUILTIN) { + if(verbose) + out1str("a shell builtin"); + } else if (cmdp->cmdtype == CMDFUNCTION) { + if (verbose) { + INTOFF; + out1str("a function\n"); + name = commandtext(cmdp->param.func); + printf("%s() {\n %s\n}", cmdp->cmdname, name); + ckfree(name); + INTON; + } +#ifdef DEBUG + } else { + error("internal error: cmdtype %d", cmdp->cmdtype); +#endif + } + printf(snlfmt, cmdp->rehash ? "*" : nullstr); +} + + + +/*** List the available builtins ***/ + + +static int helpcmd(int argc, char** argv) +{ + int col, i; + + printf("\nBuilt-in commands:\n-------------------\n"); + for (col=0, i=0; i < NUMBUILTINS; i++) { + col += printf("%c%s", ((col == 0) ? '\t' : ' '), + builtincmds[i].name+1); + if (col > 60) { + printf("\n"); + col = 0; + } + } +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + { + extern const struct BB_applet applets[]; + extern const size_t NUM_APPLETS; + + for (i=0; i < NUM_APPLETS; i++) { + + col += printf("%c%s", ((col == 0) ? '\t' : ' '), + applets[i].name); + if (col > 60) { + printf("\n"); + col = 0; + } + } + } +#endif + printf("\n\n"); + return EXIT_SUCCESS; +} + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +static int prefix (const char *, const char *); + +static void +find_command(const 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 bltin; + int firstchange; + int updatetbl; + int regular; + struct builtincmd *bcmd; + + /* If name contains a slash, don't use the hash table */ + if (strchr(name, '/') != NULL) { + if (act & DO_ABS) { + while (stat(name, &statb) < 0) { + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + entry->cmdtype = CMDUNKNOWN; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = 0; + return; + } + + updatetbl = 1; + if (act & DO_BRUTE) { + firstchange = path_change(path, &bltin); + } else { + bltin = builtinloc; + firstchange = 9999; + } + + /* If name is in the table, and not invalidated by cd, we're done */ + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) { + if (cmdp->cmdtype == CMDFUNCTION) { + if (act & DO_NOFUN) { + updatetbl = 0; + } else { + goto success; + } + } else if (act & DO_BRUTE) { + if ((cmdp->cmdtype == CMDNORMAL && + cmdp->param.index >= firstchange) || + (cmdp->cmdtype == CMDBUILTIN && + ((builtinloc < 0 && bltin >= 0) ? + bltin : builtinloc) >= firstchange)) { + /* need to recompute the entry */ + } else { + goto success; + } + } else { + goto success; + } + } + + bcmd = find_builtin(name); + regular = bcmd && IS_BUILTIN_REGULAR(bcmd); + + if (regular) { + if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) { + goto success; + } + } else if (act & DO_BRUTE) { + if (firstchange == 0) { + updatetbl = 0; + } + } + + /* If %builtin not in path, check for builtin next */ + if (regular || (bltin < 0 && bcmd)) { +builtin: + if (!updatetbl) { + entry->cmdtype = CMDBUILTIN; + entry->u.cmd = bcmd; + return; + } + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.cmd = bcmd; + INTON; + goto 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 (idx >= firstchange) { + updatetbl = 0; + } + if (pathopt) { + if (prefix("builtin", pathopt)) { + if ((bcmd = find_builtin(name))) { + goto builtin; + } + continue; + } else if (!(act & DO_NOFUN) && + prefix("func", pathopt)) { + /* handled below */ + } else { + continue; /* ignore unimplemented options */ + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && idx <= prev && + idx < firstchange) { + if (idx < prev) + continue; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat(fullname, &statb) < 0) { + 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 we aren't called with DO_BRUTE and cmdp is set, it must + be a function and we're being called with DO_NOFUN */ + 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) + out2fmt("%s: %s\n", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; +} + + + +/* + * Search the table of builtin commands. + */ + +static int +bstrcmp(const void *name, const void *b) +{ + return strcmp((const char *)name, (*(const char *const *) b)+1); +} + +static struct builtincmd * +find_builtin(const char *name) +{ + struct builtincmd *bp; + + bp = bsearch(name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd), + bstrcmp + ); + 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 && builtinloc >= 0)) + cmdp->rehash = 1; + } + } +} + + + +/* + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. Called with + * interrupts off. + */ + +static void +changepath(const char *newval) +{ + int firstchange; + int bltin; + + firstchange = path_change(newval, &bltin); + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + clearcmdentry(firstchange); + builtinloc = bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +static void +clearcmdentry(firstchange) + int firstchange; +{ + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if ((cmdp->cmdtype == CMDNORMAL && + cmdp->param.index >= firstchange) + || (cmdp->cmdtype == CMDBUILTIN && + builtinloc >= firstchange)) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + +/* + * Delete all functions. + */ + +static void +deletefuncs(void) { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDFUNCTION) { + *pp = cmdp->next; + freefunc(cmdp->param.func); + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + */ + +static struct tblentry **lastcmdentry; + +static struct tblentry * +cmdlookup(const char *name, int add) +{ + int hashval; + const char *p; + struct tblentry *cmdp; + struct tblentry **pp; + + p = name; + hashval = *p << 4; + while (*p) + hashval += *p++; + hashval &= 0x7FFF; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + INTOFF; + cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB + + strlen(name) + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + cmdp->rehash = 0; + strcpy(cmdp->cmdname, name); + INTON; + } + lastcmdentry = pp; + return cmdp; +} + +/* + * Delete the command entry returned on the last lookup. + */ + +static void +delete_cmd_entry() { + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + ckfree(cmdp); + INTON; +} + + + + + +static const short nodesize[26] = { + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct ncmd)), + ALIGN(sizeof (struct npipe)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nif)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nfor)), + ALIGN(sizeof (struct ncase)), + ALIGN(sizeof (struct nclist)), + ALIGN(sizeof (struct narg)), + ALIGN(sizeof (struct narg)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct ndup)), + ALIGN(sizeof (struct ndup)), + ALIGN(sizeof (struct nhere)), + ALIGN(sizeof (struct nhere)), + ALIGN(sizeof (struct nnot)), +}; + + + +/* + * Delete a function if it exists. + */ + +static void +unsetfunc(char *name) +{ + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + delete_cmd_entry(); + } +} + + +/* + * Locate and print what a word is... + */ + +static int +typecmd(int argc, char **argv) +{ + int i; + int err = 0; + char *argv_a[2]; + + argv_a[1] = 0; + + for (i = 1; i < argc; i++) { + argv_a[0] = argv[i]; + argptr = argv_a; + optptr = "v"; + err |= hashcmd(argc, argv); + } + return err; +} + +#ifdef ASH_CMDCMD +static int +commandcmd(argc, argv) + 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) { + 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) { + out2str( + "command [-p] command [arg ...]\n" + "command {-v|-V} command\n"); + return EX_USAGE; + } + + if (verify_only || verbose_verify_only) { + char *argv_a[2]; + + argv_a[1] = 0; + argv_a[0] = *argptr; + argptr = argv_a; + optptr = verbose_verify_only ? "v" : "V"; /* reverse special */ + return hashcmd(argc, argv); + } + + return 0; +} +#endif + +static int +path_change(newval, bltin) + const char *newval; + int *bltin; +{ + const char *old, *new; + int idx; + int firstchange; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + idx = 0; + *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 == '%' && *bltin < 0 && prefix("builtin", new + 1)) + *bltin = idx; + if (*new == ':') { + idx++; + } + new++, old++; + } + if (builtinloc >= 0 && *bltin < 0) + firstchange = 0; + return firstchange; +} +/* + * 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 */ + +/* + * 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 */ +}; + + +static char *expdest; /* output of current string */ +static struct nodelist *argbackq; /* list of back quote expressions */ +static struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +static struct ifsregion *ifslastp; /* last struct in list */ +static struct arglist exparg; /* holds expanded arg list */ + +static void argstr (char *, int); +static char *exptilde (char *, int); +static void expbackq (union node *, int, int); +static int subevalvar (char *, char *, int, int, int, int, int); +static int varisset (char *, int); +static void strtodest (const char *, const char *, int); +static void 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); +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB) +#if !defined(GLOB_BROKEN) +static void addglob (const glob_t *); +#endif +#endif +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +static void expmeta (char *, char *); +#endif +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +static struct strlist *expsort (struct strlist *); +static struct strlist *msort (struct strlist *, int); +#endif +static int patmatch (char *, char *, int); +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +static int patmatch2 (char *, char *, int); +#else +static int pmatch (char *, char *, int); +#define patmatch2 patmatch +#endif +static char *cvtnum (int, char *); + +/* + * Expand shell variables and backquotes inside a here document. + */ + +/* arg: the document, fd: where to write the expanded version */ +static inline void +expandhere(union node *arg, int fd) +{ + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + xwrite(fd, stackblock(), expdest - 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. + */ + +static void +expandarg(arg, arglist, flag) + 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; + } + ifsfree(); + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } +} + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +static inline char * +evalvar(p, flag) + char *p; + int flag; +{ + int subtype; + int varflags; + char *var; + const char *val; + int patloc; + int c; + int set; + int special; + int startloc; + int varlen; + int easy; + int quotes = flag & (EXP_FULL | EXP_CASE); + + varflags = *p++; + subtype = varflags & VSTYPE; + var = p; + special = 0; + if (! is_name(*p)) + special = 1; + p = strchr(p, '=') + 1; +again: /* jump here after setting a variable with ${var=text} */ + if (special) { + set = varisset(var, varflags & VSNUL); + val = NULL; + } else { + val = lookupvar(var); + if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { + val = NULL; + set = 0; + } else + set = 1; + } + varlen = 0; + startloc = expdest - stackblock(); + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + varvalue(var, varflags & VSQUOTE, flag); + if (subtype == VSLENGTH) { + varlen = expdest - stackblock() - startloc; + STADJUST(-varlen, expdest); + } + } else { + if (subtype == VSLENGTH) { + varlen = strlen(val); + } else { + strtodest( + val, + varflags & VSQUOTE ? + DQSYNTAX : BASESYNTAX, + quotes + ); + } + } + } + + if (subtype == VSPLUS) + set = ! set; + + easy = ((varflags & VSQUOTE) == 0 || + (*var == '@' && shellparam.nparam != 1)); + + + switch (subtype) { + case VSLENGTH: + expdest = cvtnum(varlen, expdest); + goto record; + + case VSNORMAL: + if (!easy) + break; +record: + recordregion(startloc, expdest - stackblock(), + varflags & VSQUOTE); + break; + + case VSPLUS: + case VSMINUS: + if (!set) { + argstr(p, flag); + break; + } + if (easy) + goto record; + break; + + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + if (!set) + break; + /* + * Terminate the string and start recording the pattern + * right after it + */ + STPUTC('\0', expdest); + patloc = expdest - stackblock(); + if (subevalvar(p, NULL, patloc, subtype, + startloc, varflags, quotes) == 0) { + int amount = (expdest - stackblock() - patloc) + 1; + STADJUST(-amount, expdest); + } + /* Remove any recorded regions beyond start of variable */ + removerecordregions(startloc); + goto record; + + case VSASSIGN: + case VSQUESTION: + if (!set) { + if (subevalvar(p, var, 0, subtype, startloc, + varflags, quotes)) { + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; + } + break; + } + if (easy) + goto record; + break; + +#ifdef DEBUG + default: + abort(); +#endif + } + + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (set) + argbackq = argbackq->next; + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + +/* + * 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(p, flag) + char *p; + int flag; +{ + char c; + int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ + int firsteq = 1; + + if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) + p = exptilde(p, flag); + for (;;) { + switch (c = *p++) { + case '\0': + case CTLENDVAR: /* ??? */ + goto breakloop; + case CTLQUOTEMARK: + /* "$@" syntax adherence hack */ + if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') + break; + if ((flag & EXP_FULL) != 0) + STPUTC(c, expdest); + break; + case CTLESC: + if (quotes) + STPUTC(c, expdest); + c = *p++; + STPUTC(c, expdest); + break; + case CTLVAR: + p = evalvar(p, flag); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c & CTLQUOTE, flag); + argbackq = argbackq->next; + break; +#ifdef ASH_MATH_SUPPORT + case CTLENDARI: + expari(flag); + break; +#endif + case ':': + case '=': + /* + * sort of a hack - expand tildes in variable + * assignments (after the first '=' and after ':'s). + */ + STPUTC(c, expdest); + if (flag & EXP_VARTILDE && *p == '~') { + if (c == '=') { + if (firsteq) + firsteq = 0; + else + break; + } + p = exptilde(p, flag); + } + break; + default: + STPUTC(c, expdest); + } + } +breakloop:; + return; +} + +static char * +exptilde(p, flag) + char *p; + int flag; +{ + char c, *startp = p; + struct passwd *pw; + const char *home; + int quotes = flag & (EXP_FULL | EXP_CASE); + + while ((c = *p) != '\0') { + switch(c) { + case CTLESC: + return (startp); + case CTLQUOTEMARK: + return (startp); + case ':': + if (flag & EXP_VARTILDE) + goto done; + break; + case '/': + goto done; + } + p++; + } +done: + *p = '\0'; + if (*(startp+1) == '\0') { + if ((home = lookupvar("HOME")) == NULL) + goto lose; + } else { + if ((pw = getpwnam(startp+1)) == NULL) + goto lose; + home = pw->pw_dir; + } + if (*home == '\0') + goto lose; + *p = c; + strtodest(home, SQSYNTAX, quotes); + 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 ASH_MATH_SUPPORT +/* + * Expand arithmetic expression. Backup to start of expression, + * evaluate, place result in (backed up) result, adjust string position. + */ +static void +expari(int flag) +{ + char *p, *start; + int errcode; + int result; + int begoff; + int quotes = flag & (EXP_FULL | EXP_CASE); + int quoted; + + /* ifsfree(); */ + + /* + * This routine is slightly over-complicated for + * efficiency. First we make sure there is + * enough space for the result, which may be bigger + * than the expression if we add exponentation. Next we + * scan backwards looking for the start of arithmetic. If the + * next previous character is a CTLESC character, then we + * have to rescan starting from the beginning since CTLESC + * characters have to be processed left to right. + */ + CHECKSTRSPACE(10, expdest); + USTPUTC('\0', expdest); + start = stackblock(); + p = expdest - 1; + while (*p != CTLARI && p >= start) + --p; + if (*p != CTLARI) + error("missing CTLARI (shouldn't happen)"); + if (p > start && *(p-1) == CTLESC) + for (p = start; *p != CTLARI; p++) + if (*p == CTLESC) + p++; + + if (p[1] == '"') + quoted=1; + else + quoted=0; + begoff = p - start; + removerecordregions(begoff); + if (quotes) + rmescapes(p+2); + result = arith(p+2, &errcode); + if (errcode < 0) { + if(errcode == -2) + error("divide by zero"); + else + error("syntax error: \"%s\"\n", p+2); + } + snprintf(p, 12, "%d", result); + + while (*p++) + ; + + if (quoted == 0) + recordregion(begoff, p - 1 - start, 0); + result = expdest - p + 1; + STADJUST(-result, expdest); +} +#endif + +/* + * Expand stuff in backwards quotes. + */ + +static void +expbackq(cmd, quoted, flag) + union node *cmd; + int quoted; + int flag; +{ + volatile struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + volatile struct ifsregion saveifs; + struct ifsregion *volatile savelastp; + struct nodelist *volatile saveargbackq; + char lastc; + int startloc = dest - stackblock(); + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + volatile int saveherefd; + int quotes = flag & (EXP_FULL | EXP_CASE); + struct jmploc jmploc; + struct jmploc *volatile savehandler; + int ex; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &dest; + (void) &syntax; +#endif + + in.fd = -1; + in.buf = 0; + in.jp = 0; + + INTOFF; + saveifs = ifsfirst; + savelastp = ifslastp; + saveargbackq = argbackq; + saveherefd = herefd; + herefd = -1; + if ((ex = setjmp(jmploc.loc))) { + goto err1; + } + savehandler = handler; + handler = &jmploc; + INTON; + p = grabstackstr(dest); + evalbackcmd(cmd, (struct backcmd *) &in); + ungrabstackstr(p, dest); +err1: + INTOFF; + ifsfirst = saveifs; + ifslastp = savelastp; + argbackq = saveargbackq; + herefd = saveherefd; + if (ex) { + goto err2; + } + + p = in.buf; + lastc = '\0'; + for (;;) { + if (--in.nleft < 0) { + 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; + in.nleft = i - 1; + } + lastc = *p++; + if (lastc != '\0') { + if (quotes && syntax[(int)lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } + + /* Eat all trailing newlines */ + for (; dest > stackblock() && dest[-1] == '\n';) + STUNPUTC(dest); + +err2: + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + exitstatus = waitforjob(in.jp); + handler = savehandler; + if (ex) { + longjmp(handler->loc, 1); + } + if (quoted == 0) + recordregion(startloc, dest - stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - stackblock()) - startloc, + (dest - stackblock()) - startloc, + stackblock() + startloc)); + expdest = dest; + INTON; +} + +static int +subevalvar(p, str, strloc, subtype, startloc, varflags, quotes) + char *p; + char *str; + int strloc; + int subtype; + int startloc; + int varflags; + int quotes; +{ + char *startp; + char *loc = NULL; + char *q; + int c = 0; + int saveherefd = herefd; + struct nodelist *saveargbackq = argbackq; + int amount; + + herefd = -1; + argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + argbackq = saveargbackq; + startp = stackblock() + startloc; + if (str == NULL) + str = stackblock() + strloc; + + switch (subtype) { + case VSASSIGN: + setvar(str, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + varflags &= ~VSNUL; + if (c != 0) + *loc = c; + return 1; + + case VSQUESTION: + if (*p != CTLENDVAR) { + out2fmt(snlfmt, startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - str - 1, + str, (varflags & VSNUL) ? "null or " + : nullstr); + /* NOTREACHED */ + + case VSTRIMLEFT: + for (loc = startp; loc < str; loc++) { + c = *loc; + *loc = '\0'; + if (patmatch2(str, startp, quotes)) + goto recordleft; + *loc = c; + if (quotes && *loc == CTLESC) + loc++; + } + return 0; + + case VSTRIMLEFTMAX: + for (loc = str - 1; loc >= startp;) { + c = *loc; + *loc = '\0'; + if (patmatch2(str, startp, quotes)) + goto recordleft; + *loc = c; + loc--; + if (quotes && loc > startp && *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; + + case VSTRIMRIGHT: + for (loc = str - 1; loc >= startp;) { + if (patmatch2(str, loc, quotes)) + goto recordright; + loc--; + if (quotes && loc > startp && *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; + + case VSTRIMRIGHTMAX: + for (loc = startp; loc < str - 1; loc++) { + if (patmatch2(str, loc, quotes)) + goto recordright; + if (quotes && *loc == CTLESC) + loc++; + } + return 0; + +#ifdef DEBUG + default: + abort(); +#endif + } + +recordleft: + *loc = c; + amount = ((str - 1) - (loc - startp)) - expdest; + STADJUST(amount, expdest); + while (loc != str - 1) + *startp++ = *loc++; + return 1; + +recordright: + amount = loc - expdest; + STADJUST(amount, expdest); + STPUTC('\0', expdest); + STADJUST(-1, expdest); + return 1; +} + + +/* + * Test whether a specialized variable is set. + */ + +static int +varisset(name, nulok) + char *name; + int nulok; +{ + if (*name == '!') + return backgndpid != -1; + else if (*name == '@' || *name == '*') { + if (*shellparam.p == NULL) + return 0; + + if (nulok) { + char **av; + + for (av = shellparam.p; *av; av++) + if (**av != '\0') + return 1; + return 0; + } + } else if (is_digit(*name)) { + char *ap; + int num = atoi(name); + + if (num > shellparam.nparam) + return 0; + + if (num == 0) + ap = arg0; + else + ap = shellparam.p[num - 1]; + + if (nulok && (ap == NULL || *ap == '\0')) + return 0; + } + return 1; +} + +/* + * Put a string on the stack. + */ + +static void +strtodest(p, syntax, quotes) + const char *p; + const char *syntax; + int quotes; +{ + while (*p) { + if (quotes && syntax[(int) *p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); + } +} + +/* + * Add the value of a specialized variable to the stack string. + */ + +static void +varvalue(name, quoted, flags) + char *name; + int quoted; + int flags; +{ + int num; + char *p; + int i; + int sep; + int sepq = 0; + char **ap; + char const *syntax; + int allow_split = flags & EXP_FULL; + int quotes = flags & (EXP_FULL | EXP_CASE); + + syntax = quoted ? DQSYNTAX : BASESYNTAX; + switch (*name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = oexitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; +numvar: + expdest = cvtnum(num, expdest); + break; + case '-': + for (i = 0 ; i < NOPTS ; i++) { + if (optent_val(i)) + STPUTC(optent_letter(optlist[i]), expdest); + } + break; + case '@': + if (allow_split && quoted) { + sep = 1 << CHAR_BIT; + goto param; + } + /* fall through */ + case '*': + sep = ifsset() ? ifsval()[0] : ' '; + if (quotes) { + sepq = syntax[(int) sep] == CCTL; + } +param: + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + strtodest(p, syntax, quotes); + if (*ap && sep) { + if (sepq) + STPUTC(CTLESC, expdest); + STPUTC(sep, expdest); + } + } + break; + case '0': + strtodest(arg0, syntax, quotes); + break; + default: + num = atoi(name); + if (num > 0 && num <= shellparam.nparam) { + strtodest(shellparam.p[num - 1], syntax, quotes); + } + break; + } +} + + +/* + * Record the fact that we have to scan this region of the + * string for IFS characters. + */ + +static void +recordregion(start, end, nulonly) + 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(string, arglist) + 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; + ifsspc = 0; + nulonly = 0; + realifs = ifsset() ? ifsval() : defifs; + if (ifslastp != NULL) { + 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 (!(*start || (!ifsspc && start > string && nulonly))) { + return; + } + } + + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; +} + +static void +ifsfree() +{ + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + ifslastp = NULL; + ifsfirst.next = NULL; +} + +/* + * Add a file name to the list. + */ + +static void +addfname(const char *name) +{ + char *p; + struct strlist *sp; + + p = sstrdup(name); + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} + +/* + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. + */ + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) +static void +expandmeta(str, flag) + struct strlist *str; + int flag; +{ + const char *p; + glob_t pglob; + /* TODO - EXP_REDIR */ + + while (str) { + if (fflag) + goto nometa; + p = preglob(str->text); + INTOFF; + switch (glob(p, 0, 0, &pglob)) { + case 0: + if(pglob.gl_pathv[1]==0 && !strcmp(p, pglob.gl_pathv[0])) + goto nometa2; + addglob(&pglob); + globfree(&pglob); + INTON; + break; + case GLOB_NOMATCH: +nometa2: + globfree(&pglob); + INTON; +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + break; + default: /* GLOB_NOSPACE */ + error("Out of space"); + } + str = str->next; + } +} + + +/* + * Add the result of glob(3) to the list. + */ + +static void +addglob(pglob) + const glob_t *pglob; +{ + char **p = pglob->gl_pathv; + + do { + addfname(*p); + } while (*++p); +} + + +#else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ +static char *expdir; + + +static void +expandmeta(str, flag) + struct strlist *str; + int flag; +{ + char *p; + struct strlist **savelastp; + struct strlist *sp; + char c; + /* TODO - EXP_REDIR */ + + while (str) { + if (fflag) + goto nometa; + p = str->text; + for (;;) { /* fast check for meta chars */ + if ((c = *p++) == '\0') + goto nometa; + if (c == '*' || c == '?' || c == '[' || c == '!') + break; + } + savelastp = exparg.lastp; + INTOFF; + if (expdir == NULL) { + int i = strlen(str->text); + expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ + } + + expmeta(expdir, str->text); + ckfree(expdir); + expdir = NULL; + 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; + } +} + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +static void +expmeta(enddir, name) + char *enddir; + char *name; + { + char *p; + const char *cp; + char *q; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + + metaflag = 0; + start = name; + for (p = name ; ; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!') + q++; + for (;;) { + while (*q == CTLQUOTEMARK) + q++; + if (*q == CTLESC) + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { + metaflag = 1; + } else if (*p == '\0') + break; + else if (*p == CTLQUOTEMARK) + continue; + else if (*p == CTLESC) + p++; + if (*p == '/') { + if (metaflag) + break; + start = p + 1; + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLQUOTEMARK) + continue; + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + } + if (metaflag == 0 || lstat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (start != name) { + p = name; + while (p < start) { + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + *enddir++ = *p++; + } + } + 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; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if (*p == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name, 0)) { + if (atend) { + strcpy(enddir, dp->d_name); + 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] = '/'; +} +#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ + + + +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +/* + * 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(str) + 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(list, len) + struct strlist *list; + int len; +{ + struct strlist *p, *q = NULL; + struct strlist **lpp; + int half; + int n; + + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { + if (strcmp(p->text, q->text) < 0) { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} +#endif + + + +/* + * Returns true if the pattern matches the string. + */ + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +/* squoted: string might have quote chars */ +static int +patmatch(char *pattern, char *string, int squoted) +{ + const char *p; + char *q; + + p = preglob(pattern); + q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string; + + return !fnmatch(p, q, 0); +} + + +static int +patmatch2(char *pattern, char *string, int squoted) +{ + char *p; + int res; + + sstrnleft--; + p = grabstackstr(expdest); + res = patmatch(pattern, string, squoted); + ungrabstackstr(p, expdest); + return res; +} +#else +static int +patmatch(char *pattern, char *string, int squoted) { + return pmatch(pattern, string, squoted); +} + + +static int +pmatch(char *pattern, char *string, int squoted) +{ + char *p, *q; + char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (squoted && *q == CTLESC) + q++; + if (*q++ != *p++) + return 0; + break; + case CTLQUOTEMARK: + continue; + case '?': + if (squoted && *q == CTLESC) + q++; + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + while (c == CTLQUOTEMARK || c == '*') + c = *++p; + if (c != CTLESC && c != CTLQUOTEMARK && + c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (squoted && *q == CTLESC && + q[1] == c) + break; + if (*q == '\0') + return 0; + if (squoted && *q == CTLESC) + q++; + q++; + } + } + do { + if (pmatch(p, q, squoted)) + return 1; + if (squoted && *q == CTLESC) + q++; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; + + endp = p; + if (*endp == '!') + endp++; + for (;;) { + while (*endp == CTLQUOTEMARK) + endp++; + if (*endp == '\0') + goto dft; /* no matching ] */ + if (*endp == CTLESC) + endp++; + if (*++endp == ']') + break; + } + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q++; + if (squoted && chr == CTLESC) + chr = *q++; + if (chr == '\0') + return 0; + c = *p++; + do { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + break; + } +dft: default: + if (squoted && *q == CTLESC) + q++; + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; +} +#endif + + + +/* + * Remove any CTLESC characters from a string. + */ + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +static char * +_rmescapes(char *str, int flag) +{ + char *p, *q, *r; + static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; + + p = strpbrk(str, qchars); + if (!p) { + return str; + } + q = p; + r = str; + if (flag & RMESCAPE_ALLOC) { + size_t len = p - str; + q = r = stalloc(strlen(p) + len + 1); + if (len > 0) { + memcpy(q, str, len); + q += len; + } + } + while (*p) { + if (*p == CTLQUOTEMARK) { + p++; + continue; + } + if (*p == CTLESC) { + p++; + if (flag & RMESCAPE_GLOB && *p != '/') { + *q++ = '\\'; + } + } + *q++ = *p++; + } + *q = '\0'; + return r; +} +#else +static void +rmescapes(str) + char *str; +{ + char *p, *q; + + p = str; + while (*p != CTLESC && *p != CTLQUOTEMARK) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLQUOTEMARK) { + p++; + continue; + } + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; +} +#endif + + + +/* + * See if a pattern matches in a case statement. + */ + +static int +casematch(union node *pattern, const char *val) +{ + struct stackmark smark; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + ifslastp = NULL; + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + STPUTC('\0', expdest); + p = grabstackstr(expdest); + result = patmatch(p, (char *)val, 0); + popstackmark(&smark); + return result; +} + +/* + * Our own itoa(). + */ + +static char * +cvtnum(num, buf) + int num; + char *buf; + { + int len; + + CHECKSTRSPACE(32, buf); + len = sprintf(buf, "%d", num); + STADJUST(len, buf); + return buf; +} +/* + * Editline and history functions (and glue). + */ +static int histcmd(argc, argv) + int argc; + char **argv; +{ + error("not compiled with history support"); + /* NOTREACHED */ +} + + +struct redirtab { + struct redirtab *next; + short renamed[10]; /* Current ash support only 0-9 descriptors */ + /* char on arm (and others) can't be negative */ +}; + +static struct redirtab *redirlist; + +extern char **environ; + + + +/* + * Initialization code. + */ + +static void +init(void) { + + /* from cd.c: */ + { + setpwd(0, 0); + } + + /* from input.c: */ + { + basepf.nextc = basepf.buf = basebuf; + } + + /* 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); + } +} + + + +/* + * This routine is called when an error or an interrupt occurs in an + * interactive shell and control is returned to the main command loop. + */ + +/* 1 == check for aliases, 2 == also check for assignments */ +static int checkalias; /* also used in no alias mode for check assignments */ + +static void +reset(void) { + + /* from eval.c: */ + { + evalskip = 0; + loopnest = 0; + funcnest = 0; + } + + /* from input.c: */ + { + if (exception != EXSHELLPROC) + parselleft = parsenleft = 0; /* clear input buffer */ + popallfiles(); + } + + /* from parser.c: */ + { + tokpushback = 0; + checkkwd = 0; + checkalias = 0; + } + + /* from redir.c: */ + { + while (redirlist) + popredir(); + } + +} + + + +/* + * This file implements the input routines used by the parser. + */ + +#ifdef BB_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 + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ + + + +/* + * Same as pgetc(), but ignores PEOA. + */ + +#ifdef ASH_ALIAS +static int +pgetc2() +{ + int c; + do { + c = pgetc_macro(); + } while (c == PEOA); + return c; +} +#else +static inline int pgetc2() { 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; +} + +static inline int +preadfd(void) +{ + int nr; + char *buf = parsefile->buf; + parsenextc = buf; + +retry: +#ifdef BB_FEATURE_COMMAND_EDITING + { + if (!iflag || parsefile->fd) + nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); + else { + nr = cmdedit_read_input((char*)cmdedit_prompt, buf); + } + } +#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; +} + +static void +popstring(void) +{ + struct strpush *sp = parsefile->strpush; + + INTOFF; +#ifdef ASH_ALIAS + if (sp->ap) { + if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { + if (!checkalias) { + checkalias = 1; + } + } + 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; +} + + +/* + * 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. + */ + +static int +preadbuffer(void) +{ + char *p, *q; + int more; + char savec; + + while (parsefile->strpush) { +#ifdef 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++; +} + + +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +static void +pushstring(char *s, int len, void *ap) +{ + struct strpush *sp; + + 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 ASH_ALIAS + sp->ap = (struct alias *)ap; + if (ap) { + ((struct alias *)ap)->flag |= ALIASINUSE; + sp->string = s; + } +#endif + parsenextc = s; + parsenleft = len; + INTON; +} + + +/* + * 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; +} + +#ifdef JOBS +static void restartjob (struct job *); +#endif +static void freejob (struct job *); +static struct job *getjob (const char *); +static int dowait (int, struct job *); +static void waitonint(int); + + +/* + * We keep track of whether or not fd0 has been redirected. This is for + * background commands, where we want to redirect fd0 to /dev/null only + * if it hasn't already been redirected. +*/ +static int fd0_redirected = 0; + +/* Return true if fd 0 has already been redirected at least once. */ +static inline int +fd0_redirected_p () { + return fd0_redirected != 0; +} + +static void dupredirect (const union node *, int, int fd1dup); + +#ifdef 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. + */ + + + +static void setjobctl(int enable) +{ +#ifdef OLD_TTY_DRIVER + int ldisc; +#endif + + if (enable == jobctl || rootshell == 0) + return; + if (enable) { + do { /* while we are in the background */ +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { +#else + initialpgrp = tcgetpgrp(2); + if (initialpgrp < 0) { +#endif + out2str("sh: can't access tty; job control turned off\n"); + mflag = 0; + return; + } + if (initialpgrp == -1) + initialpgrp = getpgrp(); + else if (initialpgrp != getpgrp()) { + killpg(initialpgrp, SIGTTIN); + continue; + } + } while (0); +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { + out2str("sh: need new tty driver to run job control; job control turned off\n"); + mflag = 0; + return; + } +#endif + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + setpgid(0, rootpid); +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&rootpid); +#else + tcsetpgrp(2, rootpid); +#endif + } else { /* turning job control off */ + setpgid(0, initialpgrp); +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&initialpgrp); +#else + tcsetpgrp(2, initialpgrp); +#endif + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + } + jobctl = enable; +} +#endif + + +#ifdef JOBS +static int +killcmd(argc, argv) + 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[1] == '-') { + signo = decode_signal(argv[1] + 1, 1); + if (signo < 0) { + int c; + + while ((c = nextopt("ls:")) != '\0') + switch (c) { + case 'l': + list = 1; + break; + case 's': + signo = decode_signal(optionarg, 1); + if (signo < 0) { + error( + "invalid signal number or name: %s", + optionarg + ); + } + break; +#ifdef DEBUG + default: + error( + "nextopt returned character code 0%o", c); +#endif + } + } else + argptr++; + } + + if (!list && signo < 0) + signo = SIGTERM; + + if ((signo < 0 || !*argptr) ^ list) { + goto usage; + } + + if (list) { + const char *name; + + if (!*argptr) { + out1str("0\n"); + for (i = 1; i < NSIG; i++) { + name = u_signal_names(0, &i, 1); + if(name) + printf(snlfmt, name); + } + return 0; + } + name = u_signal_names(*argptr, &signo, -1); + if (name) + printf(snlfmt, name); + else + error("invalid signal number or exit status: %s", + *argptr); + return 0; + } + + do { + if (**argptr == '%') { + jp = getjob(*argptr); + if (jp->jobctl == 0) + error("job %s not created under job control", + *argptr); + pid = -jp->ps[0].pid; + } else + pid = atoi(*argptr); + if (kill(pid, signo) != 0) + error("%s: %m", *argptr); + } while (*++argptr); + + return 0; +} + +static int +fgcmd(argc, argv) + int argc; + char **argv; +{ + struct job *jp; + int pgrp; + int status; + + jp = getjob(argv[1]); + if (jp->jobctl == 0) + error("job not created under job control"); + pgrp = jp->ps[0].pid; +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&pgrp); +#else + tcsetpgrp(2, pgrp); +#endif + restartjob(jp); + INTOFF; + status = waitforjob(jp); + INTON; + return status; +} + + +static int +bgcmd(argc, argv) + int argc; + char **argv; +{ + struct job *jp; + + do { + jp = getjob(*++argv); + if (jp->jobctl == 0) + error("job not created under job control"); + restartjob(jp); + } while (--argc > 1); + return 0; +} + + +static void +restartjob(jp) + struct job *jp; +{ + struct procstat *ps; + int i; + + if (jp->state == JOBDONE) + return; + INTOFF; + killpg(jp->ps[0].pid, SIGCONT); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if (WIFSTOPPED(ps->status)) { + ps->status = -1; + jp->state = 0; + } + } + INTON; +} +#endif + +static void showjobs(int change); + + +static int +jobscmd(argc, argv) + int argc; + char **argv; +{ + showjobs(0); + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + * + * If the shell is interrupted in the process of creating a job, the + * result may be a job structure containing zero processes. Such structures + * will be freed here. + */ + +static void +showjobs(change) + int change; +{ + int jobno; + int procno; + int i; + struct job *jp; + struct procstat *ps; + int col; + char s[64]; + + TRACE(("showjobs(%d) called\n", change)); + while (dowait(0, (struct job *)NULL) > 0); + for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { + if (! jp->used) + continue; + if (jp->nprocs == 0) { + freejob(jp); + continue; + } + if (change && ! jp->changed) + continue; + procno = jp->nprocs; + for (ps = jp->ps ; ; ps++) { /* for each process */ + if (ps == jp->ps) + snprintf(s, 64, "[%d] %ld ", jobno, + (long)ps->pid); + else + snprintf(s, 64, " %ld ", + (long)ps->pid); + out1str(s); + col = strlen(s); + s[0] = '\0'; + if (ps->status == -1) { + /* don't print anything */ + } else if (WIFEXITED(ps->status)) { + snprintf(s, 64, "Exit %d", + WEXITSTATUS(ps->status)); + } else { +#ifdef JOBS + if (WIFSTOPPED(ps->status)) + i = WSTOPSIG(ps->status); + else /* WIFSIGNALED(ps->status) */ +#endif + i = WTERMSIG(ps->status); + if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) + strcpy(s, sys_siglist[i & 0x7F]); + else + snprintf(s, 64, "Signal %d", i & 0x7F); + if (WCOREDUMP(ps->status)) + strcat(s, " (core dumped)"); + } + out1str(s); + col += strlen(s); + printf( + "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ', + ps->cmd + ); + if (--procno <= 0) + break; + } + jp->changed = 0; + if (jp->state == JOBDONE) { + freejob(jp); + } + } +} + + +/* + * Mark a job structure as unused. + */ + +static void +freejob(struct job *jp) +{ + const 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; +#ifdef JOBS + if (curjob == jp - jobtab + 1) + curjob = 0; +#endif + INTON; +} + + + +static int +waitcmd(argc, argv) + int argc; + char **argv; +{ + struct job *job; + int status, retval; + struct job *jp; + + if (--argc > 0) { +start: + job = getjob(*++argv); + } else { + job = NULL; + } + for (;;) { /* loop until process terminated or stopped */ + if (job != NULL) { + if (job->state) { + status = job->ps[job->nprocs - 1].status; + if (! iflag) + freejob(job); + if (--argc) { + goto start; + } + if (WIFEXITED(status)) + retval = WEXITSTATUS(status); +#ifdef JOBS + else if (WIFSTOPPED(status)) + retval = WSTOPSIG(status) + 128; +#endif + else { + /* XXX: limits number of signals */ + retval = WTERMSIG(status) + 128; + } + return retval; + } + } else { + for (jp = jobtab ; ; jp++) { + if (jp >= jobtab + njobs) { /* no running procs */ + return 0; + } + if (jp->used && jp->state == 0) + break; + } + } + if (dowait(2, 0) < 0 && errno == EINTR) { + return 129; + } + } +} + + + +/* + * Convert a job name to a job structure. + */ + +static struct job * +getjob(const char *name) +{ + int jobno; + struct job *jp; + int pid; + int i; + + if (name == NULL) { +#ifdef JOBS +currentjob: + if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0) + error("No current job"); + return &jobtab[jobno - 1]; +#else + error("No current job"); +#endif + } else if (name[0] == '%') { + if (is_digit(name[1])) { + jobno = number(name + 1); + if (jobno > 0 && jobno <= njobs + && jobtab[jobno - 1].used != 0) + return &jobtab[jobno - 1]; +#ifdef JOBS + } else if (name[1] == '%' && name[2] == '\0') { + goto currentjob; +#endif + } else { + struct job *found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && prefix(name + 1, jp->ps[0].cmd)) { + if (found) + error("%s: ambiguous", name); + found = jp; + } + } + if (found) + return found; + } + } else if (is_number(name, &pid)) { + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } + } + error("No such job: %s", name); + /* NOTREACHED */ +} + + + +/* + * Return a new job structure, + */ + +static struct job * +makejob(const union node *node, int nprocs) +{ + int i; + struct job *jp; + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + INTOFF; + if (njobs == 0) { + jobtab = ckmalloc(4 * sizeof jobtab[0]); + } else { + jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); + memcpy(jp, jobtab, njobs * sizeof jp[0]); + /* Relocate `ps' pointers */ + for (i = 0; i < njobs; i++) + if (jp[i].ps == &jobtab[i].ps0) + jp[i].ps = &jp[i].ps0; + ckfree(jobtab); + jobtab = jp; + } + jp = jobtab + njobs; + for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); + INTON; + break; + } + if (jp->used == 0) + break; + } + INTOFF; + jp->state = 0; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; +#ifdef JOBS + jp->jobctl = jobctl; +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; + } + INTON; + TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, + jp - jobtab + 1)); + return jp; +} + + +/* + * Fork of a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + */ + + + +static int +forkshell(struct job *jp, const union node *n, int mode) +{ + int pid; +#ifdef JOBS + int pgrp; +#endif + const char *devnull = _PATH_DEVNULL; + const char *nullerr = "Can't open %s"; + + TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n, + mode)); + INTOFF; + pid = fork(); + if (pid == -1) { + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork"); + } + if (pid == 0) { + struct job *p; + int wasroot; + int i; + + TRACE(("Child shell %d\n", getpid())); + wasroot = rootshell; + rootshell = 0; + closescript(); + INTON; + clear_traps(); +#ifdef JOBS + jobctl = 0; /* do job control only in root shell */ + if (wasroot && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + setpgid(0, pgrp); + if (mode == FORK_FG) { + /*** this causes superfluous TIOCSPGRPS ***/ +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) + error("TIOCSPGRP failed, errno=%d", errno); +#else + if (tcsetpgrp(2, pgrp) < 0) + error("tcsetpgrp failed, errno=%d", errno); +#endif + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } else if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#endif + for (i = njobs, p = jobtab ; --i >= 0 ; p++) + if (p->used) + freejob(p); + if (wasroot && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + return pid; + } +#ifdef JOBS + if (rootshell && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; + setpgid(pid, pgrp); + } +#endif + if (mode == FORK_BG) + backgndpid = pid; /* set $! */ + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; + if (iflag && rootshell && n) + ps->cmd = commandtext(n); + } + INTON; + TRACE(("In parent shell: child = %d\n", pid)); + return pid; +} + + + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * forground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + */ + +static int +waitforjob(struct job *jp) +{ +#ifdef JOBS + int mypgrp = getpgrp(); +#endif + int status; + int st; + struct sigaction act, oact; + + INTOFF; + intreceived = 0; +#ifdef JOBS + if (!jobctl) { +#else + if (!iflag) { +#endif + sigaction(SIGINT, 0, &act); + act.sa_handler = waitonint; + sigaction(SIGINT, &act, &oact); + } + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == 0) { + dowait(1, jp); + } +#ifdef JOBS + if (!jobctl) { +#else + if (!iflag) { +#endif + sigaction(SIGINT, &oact, 0); + if (intreceived && trap[SIGINT]) kill(getpid(), SIGINT); + } +#ifdef JOBS + if (jp->jobctl) { +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); +#else + if (tcsetpgrp(2, mypgrp) < 0) + error("tcsetpgrp failed, errno=%d\n", errno); +#endif + } + if (jp->state == JOBSTOPPED) + curjob = jp - jobtab + 1; +#endif + status = jp->ps[jp->nprocs - 1].status; + /* convert to 8 bits */ + if (WIFEXITED(status)) + st = WEXITSTATUS(status); +#ifdef JOBS + else if (WIFSTOPPED(status)) + st = WSTOPSIG(status) + 128; +#endif + else + st = WTERMSIG(status) + 128; +#ifdef JOBS + if (jp->jobctl) { + /* + * 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 + * occured, and if so interrupt ourselves. Yuck. - mycroft + */ + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) + raise(SIGINT); + } + if (jp->state == JOBDONE) + +#endif + freejob(jp); + INTON; + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +/* + * 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. + * + */ + +static inline int +waitproc(int block, int *status) +{ + int flags; + + flags = 0; +#ifdef JOBS + if (jobctl) + flags |= WUNTRACED; +#endif + if (block == 0) + flags |= WNOHANG; + return wait3(status, flags, (struct rusage *)NULL); +} + +static int +dowait(int block, struct job *job) +{ + int pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + int core; + int sig; + + TRACE(("dowait(%d) called\n", block)); + do { + pid = waitproc(block, &status); + TRACE(("wait returns %d, status=%d\n", pid, status)); + } while (!(block & 2) && pid == -1 && errno == EINTR); + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = jobtab ; jp < jobtab + njobs ; jp++) { + if (jp->used) { + done = 1; + stopped = 1; + for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { + if (sp->pid == -1) + continue; + if (sp->pid == pid) { + TRACE(("Changing status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + stopped = 0; + else if (WIFSTOPPED(sp->status)) + done = 0; + } + if (stopped) { /* stopped or done */ + int state = done? JOBDONE : JOBSTOPPED; + if (jp->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); + jp->state = state; +#ifdef JOBS + if (done && curjob == jp - jobtab + 1) + curjob = 0; /* no current job */ +#endif + } + } + } + } + INTON; + if (! rootshell || ! iflag || (job && thisjob == job)) { + core = WCOREDUMP(status); +#ifdef JOBS + if (WIFSTOPPED(status)) sig = WSTOPSIG(status); + else +#endif + if (WIFEXITED(status)) sig = 0; + else sig = WTERMSIG(status); + + if (sig != 0 && sig != SIGINT && sig != SIGPIPE) { + if (thisjob != job) + out2fmt("%d: ", pid); +#ifdef JOBS + if (sig == SIGTSTP && rootshell && iflag) + out2fmt("%%%ld ", + (long)(job - jobtab + 1)); +#endif + if (sig < NSIG && sys_siglist[sig]) + out2str(sys_siglist[sig]); + else + out2fmt("Signal %d", sig); + if (core) + out2str(" - core dumped"); + out2c('\n'); + } else { + TRACE(("Not printing status: status=%d, sig=%d\n", + status, sig)); + } + } else { + TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); + if (thisjob) + thisjob->changed = 1; + } + return pid; +} + + + + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ +static int +stoppedjobs(void) +{ + int jobno; + struct job *jp; + + if (job_warning) + return (0); + for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { + if (jp->used == 0) + continue; + if (jp->state == JOBSTOPPED) { + out2str("You have stopped jobs.\n"); + job_warning = 2; + return (1); + } + } + + return (0); +} + +/* + * Return a string identifying a command (to be printed by the + * jobs command. + */ + +static char *cmdnextc; +static int cmdnleft; +#define MAXCMDTEXT 200 + +static void +cmdputs(const char *s) +{ + const char *p; + char *q; + char c; + int subtype = 0; + + if (cmdnleft <= 0) + return; + p = s; + q = cmdnextc; + while ((c = *p++) != '\0') { + if (c == CTLESC) + *q++ = *p++; + else if (c == CTLVAR) { + *q++ = '$'; + if (--cmdnleft > 0) + *q++ = '{'; + subtype = *p++; + } else if (c == '=' && subtype != 0) { + *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; + subtype = 0; + } else if (c == CTLENDVAR) { + *q++ = '}'; + } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE) + cmdnleft++; /* ignore it */ + else + *q++ = c; + if (--cmdnleft <= 0) { + *q++ = '.'; + *q++ = '.'; + *q++ = '.'; + break; + } + } + cmdnextc = q; +} + + +static void +cmdtxt(const union node *n) +{ + union node *np; + struct nodelist *lp; + const char *p; + int i; + char s[2]; + + if (n == NULL) + return; + switch (n->type) { + case NSEMI: + cmdtxt(n->nbinary.ch1); + cmdputs("; "); + cmdtxt(n->nbinary.ch2); + break; + case NAND: + cmdtxt(n->nbinary.ch1); + cmdputs(" && "); + cmdtxt(n->nbinary.ch2); + break; + case NOR: + cmdtxt(n->nbinary.ch1); + cmdputs(" || "); + cmdtxt(n->nbinary.ch2); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + cmdtxt(lp->n); + if (lp->next) + cmdputs(" | "); + } + break; + case NSUBSHELL: + cmdputs("("); + cmdtxt(n->nredir.n); + cmdputs(")"); + break; + case NREDIR: + case NBACKGND: + cmdtxt(n->nredir.n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + cmdtxt(n->nif.ifpart); + cmdputs("..."); + break; + case NWHILE: + cmdputs("while "); + goto until; + case NUNTIL: + cmdputs("until "); +until: + cmdtxt(n->nbinary.ch1); + cmdputs("; do "); + cmdtxt(n->nbinary.ch2); + cmdputs("; done"); + break; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in ..."); + break; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in ..."); + break; + case NDEFUN: + cmdputs(n->narg.text); + cmdputs("() ..."); + break; + case NCMD: + for (np = n->ncmd.args ; np ; np = np->narg.next) { + cmdtxt(np); + if (np->narg.next) + cmdputs(spcstr); + } + for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { + cmdputs(spcstr); + cmdtxt(np); + } + break; + case NARG: + cmdputs(n->narg.text); + break; + case NTO: + p = ">"; i = 1; goto redir; + case NAPPEND: + p = ">>"; i = 1; goto redir; + case NTOFD: + p = ">&"; i = 1; goto redir; + case NTOOV: + p = ">|"; i = 1; goto redir; + case NFROM: + p = "<"; i = 0; goto redir; + case NFROMFD: + p = "<&"; i = 0; goto redir; + case NFROMTO: + p = "<>"; i = 0; goto redir; +redir: + if (n->nfile.fd != i) { + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + } + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + s[1] = '\0'; + cmdputs(s); + } else { + cmdtxt(n->nfile.fname); + } + break; + case NHERE: + case NXHERE: + cmdputs("<<..."); + break; + default: + cmdputs("???"); + break; + } +} + + +static char * +commandtext(const union node *n) +{ + char *name; + + cmdnextc = name = ckmalloc(MAXCMDTEXT); + cmdnleft = MAXCMDTEXT - 4; + cmdtxt(n); + *cmdnextc = '\0'; + return name; +} + + +static void waitonint(int sig) { + intreceived = 1; + return; +} +/* + * Routines to check for mail. (Perhaps make part of main.c?) + */ + + +#define MAXMBOXES 10 + + +static int nmboxes; /* number of mailboxes */ +static time_t mailtime[MAXMBOXES]; /* times of mailboxes */ + + + +/* + * Print appropriate message(s) if mail has arrived. If the argument is + * nozero, then the value of MAIL has changed, so we just update the + * values. + */ + +static void +chkmail(int silent) +{ + int i; + const char *mpath; + char *p; + char *q; + struct stackmark smark; + struct stat statb; + + if (silent) + nmboxes = 10; + if (nmboxes == 0) + return; + setstackmark(&smark); + mpath = mpathset()? mpathval() : mailval(); + for (i = 0 ; i < nmboxes ; i++) { + p = padvance(&mpath, nullstr); + if (p == NULL) + break; + if (*p == '\0') + continue; + for (q = p ; *q ; q++); +#ifdef DEBUG + if (q[-1] != '/') + abort(); +#endif + q[-1] = '\0'; /* delete trailing '/' */ + if (stat(p, &statb) < 0) + statb.st_size = 0; + if (statb.st_size > mailtime[i] && ! silent) { + out2fmt(snlfmt, + pathopt? pathopt : "you have mail"); + } + mailtime[i] = statb.st_size; + } + nmboxes = i; + popstackmark(&smark); +} + +#define PROFILE 0 + +#if PROFILE +static short profile_buf[16384]; +extern int etext(); +#endif + +static void read_profile (const char *); +static void cmdloop (int); +static void options (int); +static void setoption (int, int); +static void procargs (int, 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(argc, argv) + int argc; + char **argv; +{ + struct jmploc jmploc; + struct stackmark smark; + volatile int state; + const char *shinit; + + BLTINCMD = find_builtin("builtin"); + EXECCMD = find_builtin("exec"); + EVALCMD = find_builtin("eval"); + +#ifndef BB_FEATURE_SH_FANCY_PROMPT + unsetenv("PS1"); + unsetenv("PS2"); +#endif + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif +#if defined(linux) || defined(__GNU__) + signal(SIGCHLD, SIG_DFL); +#endif + state = 0; + if (setjmp(jmploc.loc)) { + INTOFF; + /* + * When a shell procedure is executed, we raise the + * exception EXSHELLPROC to clean up before executing + * the shell procedure. + */ + switch (exception) { + case EXSHELLPROC: + rootpid = getpid(); + rootshell = 1; + minusc = NULL; + state = 3; + break; + + case EXEXEC: + exitstatus = exerrno; + break; + + case EXERROR: + exitstatus = 2; + break; + + default: + break; + } + + if (exception != EXSHELLPROC) { + if (state == 0 || iflag == 0 || ! rootshell) + exitshell(exitstatus); + } + reset(); + if (exception == EXINT) { + out2c('\n'); + } + 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(); + rootshell = 1; + init(); + setstackmark(&smark); + procargs(argc, argv); + if (argv[0] && argv[0][0] == '-') { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + read_profile(".profile"); + } +state2: + state = 3; +#ifndef linux + if (getuid() == geteuid() && getgid() == getegid()) { +#endif + if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { + state = 3; + read_profile(shinit); + } +#ifndef linux + } +#endif +state3: + state = 4; + if (sflag == 0 || minusc) { + static int sigs[] = { + SIGINT, SIGQUIT, SIGHUP, +#ifdef SIGTSTP + SIGTSTP, +#endif + SIGPIPE + }; +#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) + int i; + + for (i = 0; i < SIGSSIZE; i++) + setsignal(sigs[i]); + } + + if (minusc) + evalstring(minusc, 0); + + if (sflag || minusc == NULL) { +state4: /* XXX ??? - why isn't this before the "if" statement */ + cmdloop(1); + } +#if PROFILE + monitor(0); +#endif + exitshell(exitstatus); + /* 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)); + setstackmark(&smark); + for (;;) { + if (pendingsigs) + dotrap(); + inter = 0; + if (iflag && top) { + inter++; + showjobs(1); + chkmail(0); + flushall(); + } + 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); + setstackmark(&smark); + if (evalskip == SKIPFILE) { + evalskip = 0; + break; + } + } + popstackmark(&smark); +} + + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +static void +read_profile(name) + 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(const 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 compatable we should do a path + * search for the file, which is necessary to find sub-commands. + */ + + +static inline char * +find_dot_file(mybasename) + char *mybasename; +{ + char *fullname; + const char *path = pathval(); + struct stat statb; + + /* don't try this for absolute or relative paths */ + if (strchr(mybasename, '/')) + return mybasename; + + while ((fullname = padvance(&path, mybasename)) != 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("%s: not found", mybasename); + /* NOTREACHED */ +} + +static int +dotcmd(argc, argv) + int argc; + char **argv; +{ + struct strlist *sp; + exitstatus = 0; + + for (sp = cmdenviron; sp ; sp = sp->next) + setvareq(savestr(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]); + setinputfile(fullname, 1); + commandname = fullname; + cmdloop(0); + popfile(); + popstackmark(&smark); + } + return exitstatus; +} + + +static int +exitcmd(argc, argv) + int argc; + char **argv; +{ + if (stoppedjobs()) + return 0; + if (argc > 1) + exitstatus = number(argv[1]); + else + exitstatus = oexitstatus; + exitshell(exitstatus); + /* NOTREACHED */ +} + +static pointer +stalloc(int nbytes) +{ + char *p; + + nbytes = ALIGN(nbytes); + if (nbytes > stacknleft) { + int blocksize; + struct stack_block *sp; + + blocksize = nbytes; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + INTOFF; + sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += nbytes; + stacknleft -= nbytes; + return p; +} + + +static void +stunalloc(pointer p) +{ +#ifdef DEBUG + if (p == NULL) { /*DEBUG */ + write(2, "stunalloc\n", 10); + abort(); + } +#endif + if (!(stacknxt >= (char *)p && (char *)p >= stackp->space)) { + p = stackp->space; + } + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + +static void +setstackmark(struct stackmark *mark) +{ + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; + mark->marknext = markp; + markp = mark; +} + + +static 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; + 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. + */ + +static void +growstackblock(void) { + char *p; + int newlen = ALIGN(stacknleft * 2 + 100); + char *oldspace = stacknxt; + int oldlen = stacknleft; + struct stack_block *sp; + struct stack_block *oldstackp; + + if (stacknxt == stackp->space && stackp != &stackbase) { + INTOFF; + oldstackp = stackp; + sp = stackp; + stackp = sp->prev; + sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen); + sp->prev = stackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + { + /* Stack marks pointing to the start of the old block + * must be relocated to point to the new block + */ + struct stackmark *xmark; + xmark = markp; + while (xmark != NULL && xmark->stackp == oldstackp) { + xmark->stackp = stackp; + xmark->stacknxt = stacknxt; + xmark->stacknleft = stacknleft; + xmark = xmark->marknext; + } + } + INTON; + } else { + p = stalloc(newlen); + memcpy(p, oldspace, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ + } +} + + + +static inline void +grabstackblock(int len) +{ + len = ALIGN(len); + stacknxt += len; + stacknleft -= len; +} + + + +/* + * The following routines are somewhat easier to use that the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + + +static char * +growstackstr(void) { + int len = stackblocksize(); + if (herefd >= 0 && len >= 1024) { + xwrite(herefd, stackblock(), len); + sstrnleft = len - 1; + return stackblock(); + } + growstackblock(); + sstrnleft = stackblocksize() - len - 1; + return stackblock() + len; +} + + +/* + * Called from CHECKSTRSPACE. + */ + +static char * +makestrspace(size_t newlen) { + int len = stackblocksize() - sstrnleft; + do { + growstackblock(); + sstrnleft = stackblocksize() - len; + } while (sstrnleft < newlen); + return stackblock() + len; +} + + + +static void +ungrabstackstr(char *s, char *p) +{ + stacknleft += stacknxt - s; + stacknxt = s; + sstrnleft = stacknleft - (p - s); +} +/* + * Miscelaneous builtins. + */ + + +#undef rflag + +//#ifdef __GLIBC__ +static mode_t getmode(const void *, mode_t); +static void *setmode(const char *); +//#endif + +#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 +typedef long rlim_t; +#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(argc, argv) + 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)) { + putprompt(prompt); + flushall(); + } + 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') + STPUTC(c, p); + continue; + } + if (!rflag && c == '\\') { + backslash++; + continue; + } + if (c == '\n') + break; + if (startword && *ifs == ' ' && strchr(ifs, c)) { + continue; + } + startword = 0; + if (backslash && c == '\\') { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + STPUTC(c, p); + } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + startword = 1; + STARTSTACKSTR(p); + } else { + STPUTC(c, p); + } + } + STACKSTRNUL(p); + /* Remove trailing blanks */ + while (stackblock() <= --p && strchr(ifs, *p) != NULL) + *p = '\0'; + setvar(*ap, stackblock(), 0); + while (*++ap != NULL) + setvar(*ap, nullstr, 0); + return status; +} + + + +static int +umaskcmd(argc, argv) + int argc; + char **argv; +{ + char *ap; + int 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 u[4], g[4], o[4]; + + i = 0; + if ((mask & S_IRUSR) == 0) + u[i++] = 'r'; + if ((mask & S_IWUSR) == 0) + u[i++] = 'w'; + if ((mask & S_IXUSR) == 0) + u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if ((mask & S_IRGRP) == 0) + g[i++] = 'r'; + if ((mask & S_IWGRP) == 0) + g[i++] = 'w'; + if ((mask & S_IXGRP) == 0) + g[i++] = 'x'; + g[i] = '\0'; + + i = 0; + if ((mask & S_IROTH) == 0) + o[i++] = 'r'; + if ((mask & S_IWOTH) == 0) + o[i++] = 'w'; + if ((mask & S_IXOTH) == 0) + o[i++] = 'x'; + o[i] = '\0'; + + printf("u=%s,g=%s,o=%s\n", u, g, o); + } else { + printf("%.4o\n", mask); + } + } else { + if (is_digit((unsigned char)*ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else { + void *set; + + INTOFF; + if ((set = setmode(ap)) != 0) { + mask = getmode(set, ~mask & 0777); + ckfree(set); + } + INTON; + if (!set) + 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(processes)", RLIMIT_NPROC, 1, 'p' }, +#endif +#ifdef RLIMIT_NOFILE + { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_VMEM + { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, +#endif +#ifdef RLIMIT_SWAP + { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' }, +#endif + { (char *) 0, 0, 0, '\0' } +}; + +static int +ulimitcmd(argc, argv) + int argc; + char **argv; +{ + int c; + rlim_t val = 0; + enum { SOFT = 0x1, HARD = 0x2 } + how = SOFT | HARD; + const struct limits *l; + int set, all = 0; + int optc, what; + struct rlimit limit; + + what = 'f'; + while ((optc = nextopt("HSatfdsmcnpl")) != '\0') + switch (optc) { + case 'H': + how = HARD; + break; + case 'S': + how = SOFT; + break; + case 'a': + all = 1; + break; + default: + what = optc; + } + + for (l = limits; l->name && l->option != what; l++) + ; + if (!l->name) + error("internal error (%c)", what); + + set = *argptr ? 1 : 0; + if (set) { + char *p = *argptr; + + if (all || argptr[1]) + error("too many arguments"); + if (strcmp(p, "unlimited") == 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); + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + + printf("%-20s ", l->name); + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else + { + val /= l->factor; + printf("%lld\n", (long long) val); + } + } + 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 { + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else + { + val /= l->factor; + printf("%lld\n", (long long) val); + } + } + return 0; +} +/* + * prefix -- see if pfx is a prefix of string. + */ + +static int +prefix(char const *pfx, char const *string) +{ + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return 1; +} + +/* + * Return true if s is a string of digits, and save munber in intptr + * nagative is bad + */ + +static int +is_number(const char *p, int *intptr) +{ + int ret = 0; + + do { + if (! is_digit(*p)) + return 0; + ret *= 10; + ret += digit_val(*p); + p++; + } while (*p != '\0'); + + *intptr = ret; + return 1; +} + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +static int +number(const char *s) +{ + int i; + if (! is_number(s, &i)) + error("Illegal number: %s", s); + return i; +} + +/* + * Produce a possibly single quoted string suitable as input to the shell. + * The return string is allocated on the stack. + */ + +static char * +single_quote(const char *s) { + char *p; + + STARTSTACKSTR(p); + + do { + char *q = p; + size_t len1, len1p, len2, len2p; + + len1 = strcspn(s, "'"); + len2 = strspn(s + len1, "'"); + + len1p = len1 ? len1 + 2 : len1; + switch (len2) { + case 0: + len2p = 0; + break; + case 1: + len2p = 2; + break; + default: + len2p = len2 + 2; + } + + CHECKSTRSPACE(len1p + len2p + 1, p); + + if (len1) { + *p = '\''; + q = p + 1 + len1; + memcpy(p + 1, s, len1); + *q++ = '\''; + s += len1; + } + + switch (len2) { + case 0: + break; + case 1: + *q++ = '\\'; + *q = '\''; + s++; + break; + default: + *q = '"'; + q += 1 + len2; + memcpy(q + 1, s, len2); + *q = '"'; + s += len2; + } + + STADJUST(len1p + len2p, p); + } while (*s); + + USTPUTC(0, p); + + return grabstackstr(p); +} + +/* + * Like strdup but works with the ash stack. + */ + +static char * +sstrdup(const char *p) +{ + size_t len = strlen(p) + 1; + return memcpy(stalloc(len), p, len); +} + + +/* + * Routine for dealing with parsed shell commands. + */ + + +static void sizenodelist (const struct nodelist *); +static struct nodelist *copynodelist (const struct nodelist *); +static char *nodesavestr (const char *); + +static void +calcsize(const union node *n) +{ + if (n == NULL) + return; + funcblocksize += nodesize[n->type]; + switch (n->type) { + case NSEMI: + case NAND: + case NOR: + case NWHILE: + case NUNTIL: + calcsize(n->nbinary.ch2); + calcsize(n->nbinary.ch1); + break; + case NCMD: + calcsize(n->ncmd.redirect); + calcsize(n->ncmd.args); + 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 NIF: + calcsize(n->nif.elsepart); + calcsize(n->nif.ifpart); + calcsize(n->nif.test); + break; + case NFOR: + funcstringsize += strlen(n->nfor.var) + 1; + calcsize(n->nfor.body); + calcsize(n->nfor.args); + break; + case NCASE: + calcsize(n->ncase.cases); + calcsize(n->ncase.expr); + break; + case NCLIST: + calcsize(n->nclist.body); + calcsize(n->nclist.pattern); + calcsize(n->nclist.next); + break; + case NDEFUN: + case NARG: + sizenodelist(n->narg.backquote); + funcstringsize += strlen(n->narg.text) + 1; + calcsize(n->narg.next); + break; + case NTO: + case NFROM: + case NFROMTO: + case NAPPEND: + case NTOOV: + 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(const struct nodelist *lp) +{ + while (lp) { + funcblocksize += ALIGN(sizeof(struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + +static union node * +copynode(const union node *n) +{ + union node *new; + + if (n == NULL) + return NULL; + new = funcblock; + funcblock = (char *) funcblock + nodesize[n->type]; + switch (n->type) { + case NSEMI: + case NAND: + case NOR: + case NWHILE: + case NUNTIL: + new->nbinary.ch2 = copynode(n->nbinary.ch2); + new->nbinary.ch1 = copynode(n->nbinary.ch1); + break; + case NCMD: + new->ncmd.redirect = copynode(n->ncmd.redirect); + new->ncmd.args = copynode(n->ncmd.args); + new->ncmd.assign = copynode(n->ncmd.assign); + new->ncmd.backgnd = n->ncmd.backgnd; + break; + case NPIPE: + new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); + new->npipe.backgnd = n->npipe.backgnd; + break; + case NREDIR: + case NBACKGND: + case NSUBSHELL: + new->nredir.redirect = copynode(n->nredir.redirect); + new->nredir.n = copynode(n->nredir.n); + break; + case NIF: + new->nif.elsepart = copynode(n->nif.elsepart); + new->nif.ifpart = copynode(n->nif.ifpart); + new->nif.test = copynode(n->nif.test); + break; + case NFOR: + new->nfor.var = nodesavestr(n->nfor.var); + new->nfor.body = copynode(n->nfor.body); + new->nfor.args = copynode(n->nfor.args); + break; + case NCASE: + new->ncase.cases = copynode(n->ncase.cases); + new->ncase.expr = copynode(n->ncase.expr); + break; + case NCLIST: + new->nclist.body = copynode(n->nclist.body); + new->nclist.pattern = copynode(n->nclist.pattern); + new->nclist.next = copynode(n->nclist.next); + break; + case NDEFUN: + case NARG: + new->narg.backquote = copynodelist(n->narg.backquote); + new->narg.text = nodesavestr(n->narg.text); + new->narg.next = copynode(n->narg.next); + break; + case NTO: + case NFROM: + case NFROMTO: + case NAPPEND: + case NTOOV: + 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(const struct nodelist *lp) +{ + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock = (char *) funcblock + ALIGN(sizeof(struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + +static char * +nodesavestr(const char *s) +{ + const char *p = s; + char *q = funcstring; + char *rtn = funcstring; + + while ((*q++ = *p++) != '\0') + continue; + funcstring = q; + return rtn; +} + +#ifdef ASH_GETOPTS +static int getopts (char *, char *, char **, int *, int *); +#endif + + +/* + * Process the shell command line arguments. + */ + +static void +procargs(argc, argv) + int argc; + char **argv; +{ + int i; + + argptr = argv; + if (argc > 0) + argptr++; + for (i = 0; i < NOPTS; i++) + optent_val(i) = 2; + options(1); + if (*argptr == NULL && minusc == NULL) + 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 (optent_val(i) == 2) + optent_val(i) = 0; + arg0 = argv[0]; + if (sflag == 0 && minusc == NULL) { + commandname = argv[0]; + arg0 = *argptr++; + setinputfile(arg0, 0); + commandname = arg0; + } + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (argptr && minusc && *argptr) + arg0 = *argptr++; + + shellparam.p = argptr; + shellparam.optind = 1; + shellparam.optoff = -1; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + optschanged(); +} + + + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +static inline void +minus_o(const char *name, int val) +{ + int i; + + if (name == NULL) { + out1str("Current option settings\n"); + for (i = 0; i < NOPTS; i++) + printf("%-16s%s\n", optent_name(optlist[i]), + optent_val(i) ? "on" : "off"); + } else { + for (i = 0; i < NOPTS; i++) + if (equal(name, optent_name(optlist[i]))) { + setoption(optent_letter(optlist[i]), val); + return; + } + error("Illegal option -o %s", name); + } +} + + +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) { + char *q; +#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ + if (*p == '\0') +#endif + q = *argptr++; + if (q == NULL || minusc != NULL) + error("Bad -c option"); + minusc = q; +#ifdef NOHACK + break; +#endif + } else if (c == 'o') { + minus_o(*argptr, val); + if (*argptr) + argptr++; + } else { + setoption(c, val); + } + } + } +} + + +static void +setoption(int flag, int val) +{ + int i; + + for (i = 0; i < NOPTS; i++) + if (optent_letter(optlist[i]) == flag) { + optent_val(i) = val; + if (val) { + /* #%$ hack for ksh semantics */ + if (flag == 'V') + Eflag = 0; + else if (flag == 'E') + Vflag = 0; + } + return; + } + error("Illegal option -%c", flag); + /* NOTREACHED */ +} + + + +/* + * Set the shell parameters. + */ + +static void +setparam(char **argv) +{ + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; + shellparam.optind = 1; + shellparam.optoff = -1; +} + + +/* + * Free the list of positional parameters. + */ + +static 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. + */ + +static int +shiftcmd(argc, argv) + 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); + shellparam.optind = 1; + shellparam.optoff = -1; + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +static int +setcmd(argc, argv) + int argc; + char **argv; +{ + if (argc == 1) + return showvarscmd(argc, argv); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +static void +getoptsreset(const char *value) +{ + shellparam.optind = number(value); + shellparam.optoff = -1; +} + +#ifdef BB_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 ASH_GETOPTS +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +static int +getoptscmd(argc, argv) + 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); +} + +/* + * Safe version of setvar, returns 1 on success 0 on failure. + */ + +static int +setvarsafe(name, val, flags) + const char *name, *val; + int flags; +{ + struct jmploc jmploc; + struct jmploc *volatile savehandler = handler; + int err = 0; +#ifdef __GNUC__ + (void) &err; +#endif + + if (setjmp(jmploc.loc)) + err = 1; + else { + handler = &jmploc; + setvar(name, val, flags); + } + handler = savehandler; + return err; +} + +static int +getopts(optstr, optvar, optfirst, myoptind, optoff) + char *optstr; + char *optvar; + char **optfirst; + int *myoptind; + int *optoff; +{ + char *p, *q; + char c = '?'; + int done = 0; + int err = 0; + char s[10]; + char **optnext = optfirst + *myoptind - 1; + + if (*myoptind <= 1 || *optoff < 0 || !(*(optnext - 1)) || + strlen(*(optnext - 1)) < *optoff) + p = NULL; + else + p = *(optnext - 1) + *optoff; + if (p == NULL || *p == '\0') { + /* Current word is done, advance */ + if (optnext == NULL) + return 1; + p = *optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + *myoptind = optnext - optfirst + 1; + 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 { + out2fmt("Illegal option -%c\n", c); + (void) unsetvar("OPTARG"); + } + c = '?'; + goto bad; + } + 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 { + out2fmt("No arg for -%c option\n", c); + (void) unsetvar("OPTARG"); + c = '?'; + } + goto bad; + } + + if (p == *optnext) + optnext++; + setvarsafe("OPTARG", p, 0); + p = NULL; + } + else + setvarsafe("OPTARG", "", 0); + *myoptind = optnext - optfirst + 1; + goto out; + +bad: + *myoptind = 1; + p = NULL; +out: + *optoff = p ? p - *(optnext - 1) : -1; + snprintf(s, sizeof(s), "%d", *myoptind); + err |= setvarsafe("OPTIND", s, VNOFUNC); + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(optvar, s, 0); + if (err) { + *myoptind = 1; + *optoff = -1; + exraise(EXERROR); + } + return done; +} +#endif + +/* + * 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; +} + +static void +flushall() { + INTOFF; + fflush(stdout); + INTON; +} + + +static void +out2fmt(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +/* + * Version of write which resumes after a signal is caught. + */ + +static int +xwrite(int fd, const char *buf, int nbytes) +{ + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } + } +} + + +/* + * 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 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 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 *list (int); +static union node *andor (void); +static union node *pipeline (void); +static union node *command (void); +static union node *simplecmd (void); +static void parsefname (void); +static void parseheredoc (void); +static int peektoken (void); +static int readtoken (void); +static int xxreadtoken (void); +static int readtoken1 (int, char const *, char *, int); +static int noexpand (char *); +static void synexpect (int) __attribute__((noreturn)); +static void synerror (const char *) __attribute__((noreturn)); +static void setprompt (int); + + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +static union node * +parsecmd(int interact) +{ + int t; + + tokpushback = 0; + doprompt = interact; + if (doprompt) + setprompt(1); + else + setprompt(0); + needprompt = 0; + t = readtoken(); + if (t == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +static union node * +list(nlflag) + int nlflag; +{ + union node *n1, *n2, *n3; + int tok; + + checkkwd = 2; + if (nlflag == 0 && tokendlist[peektoken()]) + return NULL; + n1 = NULL; + for (;;) { + n2 = andor(); + tok = readtoken(); + if (tok == TBACKGND) { + if (n2->type == NCMD || n2->type == NPIPE) { + n2->ncmd.backgnd = 1; + } else if (n2->type == NREDIR) { + n2->type = NBACKGND; + } else { + n3 = (union node *)stalloc(sizeof (struct nredir)); + n3->type = NBACKGND; + n3->nredir.n = n2; + n3->nredir.redirect = NULL; + n2 = n3; + } + } + 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) + return n1; + } else { + tokpushback++; + } + checkkwd = 2; + if (tokendlist[peektoken()]) + return n1; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return n1; + default: + if (nlflag) + synexpect(-1); + tokpushback++; + return n1; + } + } +} + + + +static union node * +andor() { + union node *n1, *n2, *n3; + int t; + + checkkwd = 1; + n1 = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n1; + } + checkkwd = 2; + 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() { + union node *n1, *n2, *pipenode; + struct nodelist *lp, *prev; + int negate; + + negate = 0; + TRACE(("pipeline: entered\n")); + if (readtoken() == TNOT) { + negate = !negate; + checkkwd = 1; + } 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 = 2; + 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() { + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + int t; + + redir = NULL; + n1 = NULL; + rpp = &redir; + + switch (readtoken()) { + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + n1->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + n2->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + if (readtoken() != TFI) + synexpect(TFI); + checkkwd = 1; + break; + case TWHILE: + case TUNTIL: { + int got; + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.ch1 = list(0); + if ((got=readtoken()) != TDO) { +TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); + synexpect(TDO); + } + n1->nbinary.ch2 = list(0); + if (readtoken() != TDONE) + synexpect(TDONE); + checkkwd = 1; + break; + } + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.var = wordtext; + checkkwd = 1; + if (readtoken() == TIN) { + app = ≈ + 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 { + static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, + '@', '=', '\0'}; + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = argvars; + 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 = 2; + if (readtoken() != TDO) + synexpect(TDO); + n1->nfor.body = list(0); + if (readtoken() != TDONE) + synexpect(TDONE); + checkkwd = 1; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + if (readtoken() != TWORD) + synexpect(TWORD); + n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + n2->narg.next = NULL; + do { + checkkwd = 1; + } while (readtoken() == TNL); + if (lasttoken != TIN) + synerror("expecting \"in\""); + cpp = &n1->ncase.cases; + checkkwd = 2, readtoken(); + do { + 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 (checkkwd = 2, readtoken() != TPIPE) + break; + app = &ap->narg.next; + readtoken(); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + synexpect(TRP); + cp->nclist.body = list(0); + + checkkwd = 2; + if ((t = readtoken()) != TESAC) { + if (t != TENDCASE) + synexpect(TENDCASE); + else + checkkwd = 2, readtoken(); + } + cpp = &cp->nclist.next; + } while(lasttoken != TESAC); + *cpp = NULL; + checkkwd = 1; + break; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + if (readtoken() != TRP) + synexpect(TRP); + checkkwd = 1; + break; + case TBEGIN: + n1 = list(0); + if (readtoken() != TEND) + synexpect(TEND); + checkkwd = 1; + break; + /* Handle an empty command like other simple commands. */ + case TSEMI: + case TAND: + case TOR: + case TNL: + case TEOF: + case TRP: + case TBACKGND: + /* + * An empty command before a ; doesn't make much sense, and + * should certainly be disallowed in the case of `if ;'. + */ + if (!redir) + synexpect(-1); + case TWORD: + case TREDIR: + tokpushback++; + n1 = simplecmd(); + return n1; + default: + synexpect(-1); + /* NOTREACHED */ + } + + /* Now check for redirection which may follow command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (n1->type != NSUBSHELL) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + + return n1; +} + + +static union node * +simplecmd() { + union node *args, **app; + union node *n = NULL; + union node *vars, **vpp; + union node **rpp, *redir; + + args = NULL; + app = &args; + vars = NULL; + vpp = &vars; + redir = NULL; + rpp = &redir; + + checkalias = 2; + for (;;) { + switch (readtoken()) { + case TWORD: + case TASSIGN: + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + if (lasttoken == TWORD) { + *app = n; + app = &n->narg.next; + } else { + *vpp = n; + vpp = &n->narg.next; + } + 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 + ) { + /* We have a function */ + if (readtoken() != TRP) + synexpect(TRP); + n->type = NDEFUN; + checkkwd = 2; + 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.backgnd = 0; + 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; +} + +static 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 (here->striptabs) { + while (*wordtext == '\t') + wordtext++; + } + if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) + synerror("Illegal eof marker for << redirection"); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + fixredir(n, wordtext, 0); + } else { + n->nfile.fname = makename(); + } +} + + +/* + * Input any here documents. + */ + +static void +parseheredoc() { + struct heredoc *here; + union node *n; + + while (heredoclist) { + here = heredoclist; + heredoclist = here->next; + 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; + } +} + +static int +peektoken() { + int t; + + t = readtoken(); + tokpushback++; + return (t); +} + +static int +readtoken() { + int t; + +#ifdef ASH_ALIAS + int savecheckalias = checkalias; + int savecheckkwd = checkkwd; + struct alias *ap; +#endif + +#ifdef DEBUG + int alreadyseen = tokpushback; +#endif + +#ifdef ASH_ALIAS +top: +#endif + + t = xxreadtoken(); + +#ifdef ASH_ALIAS + checkalias = savecheckalias; +#endif + + if (checkkwd) { + /* + * eat newlines + */ + if (checkkwd == 2) { + checkkwd = 0; + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } + checkkwd = 0; + /* + * check for keywords + */ + if (t == TWORD && !quoteflag) + { + const char *const *pp; + + if ((pp = findkwd(wordtext))) { + lasttoken = t = pp - parsekwd + KWDOFFSET; + TRACE(("keyword %s recognized\n", tokname[t])); + goto out; + } + } + } + + + if (t != TWORD) { + if (t != TREDIR) { + checkalias = 0; + } + } else if (checkalias == 2 && isassignment(wordtext)) { + lasttoken = t = TASSIGN; +#ifdef ASH_ALIAS + } else if (checkalias) { + if (!quoteflag && (ap = lookupalias(wordtext, 1)) != NULL) { + if (*ap->val) { + pushstring(ap->val, strlen(ap->val), ap); + } + checkkwd = savecheckkwd; + goto top; + } + checkalias = 0; +#endif + } +out: +#ifdef DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * In all cases, the variable startlinno is set to the number of the line + * on which the token starts. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define RETURN(token) return lasttoken = token + +static int +xxreadtoken() { + 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 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); + else + setprompt(0); + continue; + } + pungetc(); + goto breakloop; + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + if (pgetc() == ';') + RETURN(TENDCASE); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + default: + goto breakloop; + } + } +breakloop: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); +#undef RETURN +} + + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEARITH() {goto parsearith; parsearith_return:;} + +static int +readtoken1(firstc, syntax, eofmark, striptabs) + int firstc; + char const *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; + char const *prevsyntax; /* syntax before arithmetic */ +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &out; + (void) "ef; + (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(3, out); /* permit 3 calls to USTPUTC */ + switch(syntax[c]) { + case CNL: /* '\n' */ + if (syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + c = pgetc(); + goto loop; /* continue outer loop */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if ((eofmark == NULL || dblquote) && + dqvarnest == 0) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + case CBACK: /* backslash */ + c = pgetc2(); + if (c == PEOF) { + USTPUTC('\\', out); + pungetc(); + } else if (c == '\n') { + if (doprompt) + setprompt(2); + else + setprompt(0); + } else { + if (dblquote && c != '\\' && c != '`' && c != '$' + && (c != '"' || eofmark != NULL)) + USTPUTC('\\', out); + if (SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + else if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + USTPUTC(c, out); + quotef++; + } + break; + case CSQUOTE: + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + syntax = SQSYNTAX; + break; + case CDQUOTE: + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + syntax = DQSYNTAX; + dblquote = 1; + break; + case CENDQUOTE: + if (eofmark != NULL && arinest == 0 && + varnest == 0) { + USTPUTC(c, out); + } else { + if (arinest) { + syntax = ARISYNTAX; + dblquote = 0; + } else if (eofmark == NULL && + dqvarnest == 0) { + syntax = BASESYNTAX; + dblquote = 0; + } + quotef++; + } + 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 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 ASH_ALIAS + if (c != PEOA) +#endif + USTPUTC(c, out); + + } + c = pgetc_macro(); + } + } +endword: + if (syntax == ARISYNTAX) + synerror("Missing '))'"); + if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + synerror("Unterminated quoted string"); + if (varnest != 0) { + startlinno = plinno; + synerror("Missing '}'"); + } + USTPUTC('\0', out); + len = out - stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + PARSEREDIR(); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + return lasttoken = TWORD; +/* end of readtoken routine */ + + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + */ + +checkend: { + if (eofmark) { +#ifdef 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, strlen(line), NULL); + } + } + } + } + goto checkend_return; +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +parseredir: { + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc(); + if (c == '>') + np->type = NAPPEND; + else if (c == '&') + np->type = NTOFD; + else if (c == '|') + np->type = NTOOV; + 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 || + (c != '(' && c != '{' && !is_name(c) && !is_special(c)) + ) { + USTPUTC('$', out); + pungetc(); + } else if (c == '(') { /* $(command) or $((arith)) */ + if (pgetc() == '(') { + PARSEARITH(); + } else { + pungetc(); + PARSEBACKQNEW(); + } + } else { + USTPUTC(CTLVAR, out); + typeloc = out - 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 && is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (c > PEOA && is_in_name(c)); + } else if (is_digit(c)) { + do { + USTPUTC(c, out); + c = pgetc(); + } while (is_digit(c)); + } + else if (is_special(c)) { + USTPUTC(c, out); + c = pgetc(); + } + else +badsub: synerror("Bad substitution"); + + 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; + *(stackblock() + typeloc) = subtype | flags; + if (subtype != VSNORMAL) { + varnest++; + if (dblquote) { + 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; + int 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 - 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; + int 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); + else + setprompt(0); + /* + * 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) { + break; + } + /* fall through */ + + case PEOF: +#ifdef 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 - 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(0); + + 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; +} + +/* + * 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; +} + +} /* end of readtoken */ + + +/* + * Returns true if the text contains nothing to expand (no dollar signs + * or backquotes). + */ + +static int +noexpand(text) + char *text; + { + char *p; + char c; + + p = text; + while ((c = *p++) != '\0') { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + p++; + else if (BASESYNTAX[(int)c] == CCTL) + return 0; + } + return 1; +} + + +/* + * Return true if the argument is a legal variable name (a letter or + * underscore followed by zero or more letters, underscores, and digits). + */ + +static int +goodname(const char *name) +{ + const char *p; + + p = name; + if (! is_name(*p)) + return 0; + while (*++p) { + if (! is_in_name(*p)) + return 0; + } + return 1; +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +static void +synexpect(token) + int token; +{ + char msg[64]; + + if (token >= 0) { + snprintf(msg, 64, "%s unexpected (expecting %s)", + tokname[lasttoken], tokname[token]); + } else { + snprintf(msg, 64, "%s unexpected", tokname[lasttoken]); + } + synerror(msg); + /* NOTREACHED */ +} + + +static void +synerror(const char *msg) +{ + if (commandname) + out2fmt("%s: %d: ", commandname, startlinno); + out2fmt("Syntax error: %s\n", msg); + error((char *)NULL); + /* NOTREACHED */ +} + + +/* + * called by editline -- any expansions to the prompt + * should be added here. + */ +static void +setprompt(int whichprompt) +{ + char *prompt; + switch (whichprompt) { + case 1: + prompt = ps1val(); + break; + case 2: + prompt = ps2val(); + break; + default: /* 0 */ + prompt = ""; + } + putprompt(prompt); +} + + +/* + * 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(const union node *redir) +{ + int pip[2]; + int len = 0; + + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + xwrite(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) + xwrite(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 inline int +openredirect(const 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; + } + case NTOOV: + fname = redir->nfile.expfname; +#ifdef O_CREAT + if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + goto ecreate; +#else + if ((f = creat(fname, 0666)) < 0) + goto ecreate; +#endif + break; + case NAPPEND: + fname = redir->nfile.expfname; +#ifdef O_APPEND + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + goto ecreate; +#else + if ((f = open(fname, O_WRONLY)) < 0 + && (f = creat(fname, 0666)) < 0) + goto ecreate; + lseek(f, (off_t)0, 2); +#endif + 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)); +} + + +/* + * 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. + */ + +static void +redirect(union node *redir, int flags) +{ + union node *n; + struct redirtab *sv = NULL; + int i; + int fd; + int newfd; + int try; + int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */ + + if (flags & REDIR_PUSH) { + sv = ckmalloc(sizeof (struct redirtab)); + for (i = 0 ; i < 10 ; i++) + sv->renamed[i] = EMPTY; + sv->next = redirlist; + redirlist = sv; + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + try = 0; + if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && + n->ndup.dupfd == fd) + continue; /* redirect from/to same file descriptor */ + + INTOFF; + newfd = openredirect(n); + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + if (newfd == fd) { + try++; + } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { + switch (errno) { + case EBADF: + if (!try) { + dupredirect(n, newfd, fd1dup); + try++; + break; + } + /* FALLTHROUGH*/ + default: + if (newfd >= 0) { + close(newfd); + } + INTON; + error("%d: %m", fd); + /* NOTREACHED */ + } + } + if (!try) { + close(fd); + if (flags & REDIR_PUSH) { + sv->renamed[fd] = i; + } + } + } else if (fd != newfd) { + close(fd); + } + if (fd == 0) + fd0_redirected++; + if (!try) + dupredirect(n, newfd, fd1dup); + INTON; + } +} + + +static void +dupredirect(const union node *redir, int f, int fd1dup) +{ + int fd = redir->nfile.fd; + + if(fd==1) + fd1dup = 0; + if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (redir->ndup.dupfd!=1 || fd1dup!=1) + dup_as_newfd(redir->ndup.dupfd, fd); + } + return; + } + + if (f != fd) { + dup_as_newfd(f, fd); + close(f); + } + return; +} + + + +/* + * Undo the effects of the last redirection. + */ + +static void +popredir(void) +{ + struct redirtab *rp = redirlist; + int i; + + INTOFF; + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] != EMPTY) { + if (i == 0) + fd0_redirected--; + close(i); + if (rp->renamed[i] >= 0) { + dup_as_newfd(rp->renamed[i], i); + close(rp->renamed[i]); + } + } + } + redirlist = rp->next; + ckfree(rp); + INTON; +} + +/* + * Discard all saved file descriptors. + */ + +static void +clearredir(void) { + struct redirtab *rp; + int i; + + for (rp = redirlist ; rp ; rp = rp->next) { + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] >= 0) { + close(rp->renamed[i]); + } + rp->renamed[i] = EMPTY; + } + } +} + + +/* + * Copy a file descriptor to be >= to. Returns -1 + * if the source file descriptor is closed, EMPTY if there are no unused + * file descriptors left. + */ + +static int +dup_as_newfd(from, to) + 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; +} + +/*#ifdef __weak_alias +__weak_alias(getmode,_getmode) +__weak_alias(setmode,_setmode) +#endif*/ + +#ifndef S_ISTXT +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define S_ISTXT __S_ISVTX +#else +#define S_ISTXT S_ISVTX +#endif +#endif + +#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ +#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ + +typedef struct bitcmd { + char cmd; + char cmd2; + mode_t bits; +} BITCMD; + +#define CMD2_CLR 0x01 +#define CMD2_SET 0x02 +#define CMD2_GBITS 0x04 +#define CMD2_OBITS 0x08 +#define CMD2_UBITS 0x10 + +static BITCMD *addcmd (BITCMD *, int, int, int, u_int); +static void compress_mode (BITCMD *); +#ifdef SETMODE_DEBUG +static void dumpmode (BITCMD *); +#endif + +/* + * Given the old mode and an array of bitcmd structures, apply the operations + * described in the bitcmd structures to the old mode, and return the new mode. + * Note that there is no '=' command; a strict assignment is just a '-' (clear + * bits) followed by a '+' (set bits). + */ +static mode_t +getmode(bbox, omode) + const void *bbox; + mode_t omode; +{ + const BITCMD *set; + mode_t clrval, newmode, value; + + _DIAGASSERT(bbox != NULL); + + set = (const BITCMD *)bbox; + newmode = omode; + for (value = 0;; set++) + switch(set->cmd) { + /* + * When copying the user, group or other bits around, we "know" + * where the bits are in the mode so that we can do shifts to + * copy them around. If we don't use shifts, it gets real + * grundgy with lots of single bit checks and bit sets. + */ + case 'u': + value = (newmode & S_IRWXU) >> 6; + goto common; + + case 'g': + value = (newmode & S_IRWXG) >> 3; + goto common; + + case 'o': + value = newmode & S_IRWXO; +common: if (set->cmd2 & CMD2_CLR) { + clrval = + (set->cmd2 & CMD2_SET) ? S_IRWXO : value; + if (set->cmd2 & CMD2_UBITS) + newmode &= ~((clrval<<6) & set->bits); + if (set->cmd2 & CMD2_GBITS) + newmode &= ~((clrval<<3) & set->bits); + if (set->cmd2 & CMD2_OBITS) + newmode &= ~(clrval & set->bits); + } + if (set->cmd2 & CMD2_SET) { + if (set->cmd2 & CMD2_UBITS) + newmode |= (value<<6) & set->bits; + if (set->cmd2 & CMD2_GBITS) + newmode |= (value<<3) & set->bits; + if (set->cmd2 & CMD2_OBITS) + newmode |= value & set->bits; + } + break; + + case '+': + newmode |= set->bits; + break; + + case '-': + newmode &= ~set->bits; + break; + + case 'X': + if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) + newmode |= set->bits; + break; + + case '\0': + default: +#ifdef SETMODE_DEBUG + (void)printf("getmode:%04o -> %04o\n", omode, newmode); +#endif + return (newmode); + } +} + +#define ADDCMD(a, b, c, d) do { \ + if (set >= endset) { \ + BITCMD *newset; \ + setlen += SET_LEN_INCR; \ + newset = realloc(saveset, sizeof(BITCMD) * setlen); \ + if (newset == NULL) { \ + free(saveset); \ + return (NULL); \ + } \ + set = newset + (set - saveset); \ + saveset = newset; \ + endset = newset + (setlen - 2); \ + } \ + set = addcmd(set, (a), (b), (c), (d)); \ +} while (/*CONSTCOND*/0) + +#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) + +static void * +setmode(p) + const char *p; +{ + int perm, who; + char op, *ep; + BITCMD *set, *saveset, *endset; + sigset_t mysigset, sigoset; + mode_t mask; + int equalopdone = 0; /* pacify gcc */ + int permXbits, setlen; + + if (!*p) + return (NULL); + + /* + * Get a copy of the mask for the permissions that are mask relative. + * Flip the bits, we want what's not set. Since it's possible that + * the caller is opening files inside a signal handler, protect them + * as best we can. + */ + sigfillset(&mysigset); + (void)sigprocmask(SIG_BLOCK, &mysigset, &sigoset); + (void)umask(mask = umask(0)); + mask = ~mask; + (void)sigprocmask(SIG_SETMASK, &sigoset, NULL); + + setlen = SET_LEN + 2; + + if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL) + return (NULL); + saveset = set; + endset = set + (setlen - 2); + + /* + * If an absolute number, get it and return; disallow non-octal digits + * or illegal bits. + */ + if (is_digit((unsigned char)*p)) { + perm = (mode_t)strtol(p, &ep, 8); + if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) { + free(saveset); + return (NULL); + } + ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); + set->cmd = 0; + return (saveset); + } + + /* + * Build list of structures to set/clear/copy bits as described by + * each clause of the symbolic mode. + */ + for (;;) { + /* First, find out which bits might be modified. */ + for (who = 0;; ++p) { + switch (*p) { + case 'a': + who |= STANDARD_BITS; + break; + case 'u': + who |= S_ISUID|S_IRWXU; + break; + case 'g': + who |= S_ISGID|S_IRWXG; + break; + case 'o': + who |= S_IRWXO; + break; + default: + goto getop; + } + } + +getop: if ((op = *p++) != '+' && op != '-' && op != '=') { + free(saveset); + return (NULL); + } + if (op == '=') + equalopdone = 0; + + who &= ~S_ISTXT; + for (perm = 0, permXbits = 0;; ++p) { + switch (*p) { + case 'r': + perm |= S_IRUSR|S_IRGRP|S_IROTH; + break; + case 's': + /* + * If specific bits where requested and + * only "other" bits ignore set-id. + */ + if (who == 0 || (who & ~S_IRWXO)) + perm |= S_ISUID|S_ISGID; + break; + case 't': + /* + * If specific bits where requested and + * only "other" bits ignore set-id. + */ + if (who == 0 || (who & ~S_IRWXO)) { + who |= S_ISTXT; + perm |= S_ISTXT; + } + break; + case 'w': + perm |= S_IWUSR|S_IWGRP|S_IWOTH; + break; + case 'X': + permXbits = S_IXUSR|S_IXGRP|S_IXOTH; + break; + case 'x': + perm |= S_IXUSR|S_IXGRP|S_IXOTH; + break; + case 'u': + case 'g': + case 'o': + /* + * When ever we hit 'u', 'g', or 'o', we have + * to flush out any partial mode that we have, + * and then do the copying of the mode bits. + */ + if (perm) { + ADDCMD(op, who, perm, mask); + perm = 0; + } + if (op == '=') + equalopdone = 1; + if (op == '+' && permXbits) { + ADDCMD('X', who, permXbits, mask); + permXbits = 0; + } + ADDCMD(*p, who, op, mask); + break; + + default: + /* + * Add any permissions that we haven't already + * done. + */ + if (perm || (op == '=' && !equalopdone)) { + if (op == '=') + equalopdone = 1; + ADDCMD(op, who, perm, mask); + perm = 0; + } + if (permXbits) { + ADDCMD('X', who, permXbits, mask); + permXbits = 0; + } + goto apply; + } + } + +apply: if (!*p) + break; + if (*p != ',') + goto getop; + ++p; + } + set->cmd = 0; +#ifdef SETMODE_DEBUG + (void)printf("Before compress_mode()\n"); + dumpmode(saveset); +#endif + compress_mode(saveset); +#ifdef SETMODE_DEBUG + (void)printf("After compress_mode()\n"); + dumpmode(saveset); +#endif + return (saveset); +} + +static BITCMD * +addcmd(set, op, who, oparg, mask) + BITCMD *set; + int oparg, who; + int op; + u_int mask; +{ + + _DIAGASSERT(set != NULL); + + switch (op) { + case '=': + set->cmd = '-'; + set->bits = who ? who : STANDARD_BITS; + set++; + + op = '+'; + /* FALLTHROUGH */ + case '+': + case '-': + case 'X': + set->cmd = op; + set->bits = (who ? who : mask) & oparg; + break; + + case 'u': + case 'g': + case 'o': + set->cmd = op; + if (who) { + set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | + ((who & S_IRGRP) ? CMD2_GBITS : 0) | + ((who & S_IROTH) ? CMD2_OBITS : 0); + set->bits = (mode_t)~0; + } else { + set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; + set->bits = mask; + } + + if (oparg == '+') + set->cmd2 |= CMD2_SET; + else if (oparg == '-') + set->cmd2 |= CMD2_CLR; + else if (oparg == '=') + set->cmd2 |= CMD2_SET|CMD2_CLR; + break; + } + return (set + 1); +} + +#ifdef SETMODE_DEBUG +static void +dumpmode(set) + BITCMD *set; +{ + + _DIAGASSERT(set != NULL); + + for (; set->cmd; ++set) + (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", + set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", + set->cmd2 & CMD2_CLR ? " CLR" : "", + set->cmd2 & CMD2_SET ? " SET" : "", + set->cmd2 & CMD2_UBITS ? " UBITS" : "", + set->cmd2 & CMD2_GBITS ? " GBITS" : "", + set->cmd2 & CMD2_OBITS ? " OBITS" : ""); +} +#endif + +/* + * Given an array of bitcmd structures, compress by compacting consecutive + * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', + * 'g' and 'o' commands continue to be separate. They could probably be + * compacted, but it's not worth the effort. + */ +static void +compress_mode(set) + BITCMD *set; +{ + BITCMD *nset; + int setbits, clrbits, Xbits, op; + + _DIAGASSERT(set != NULL); + + for (nset = set;;) { + /* Copy over any 'u', 'g' and 'o' commands. */ + while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { + *set++ = *nset++; + if (!op) + return; + } + + for (setbits = clrbits = Xbits = 0;; nset++) { + if ((op = nset->cmd) == '-') { + clrbits |= nset->bits; + setbits &= ~nset->bits; + Xbits &= ~nset->bits; + } else if (op == '+') { + setbits |= nset->bits; + clrbits &= ~nset->bits; + Xbits &= ~nset->bits; + } else if (op == 'X') + Xbits |= nset->bits & ~setbits; + else + break; + } + if (clrbits) { + set->cmd = '-'; + set->cmd2 = 0; + set->bits = clrbits; + set++; + } + if (setbits) { + set->cmd = '+'; + set->cmd2 = 0; + set->bits = setbits; + set++; + } + if (Xbits) { + set->cmd = 'X'; + set->cmd2 = 0; + set->bits = Xbits; + set++; + } + } +} +#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 *); + + +static void +showtree(n) + union node *n; +{ + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static void +shtree(n, ind, pfx, fp) + 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, "", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + + +static void +shcmd(cmd, fp) + 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 NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NTOOV: 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(arg, fp) + union node *arg; + FILE *fp; + { + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + printf("\n", arg->type); + fflush(stdout); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch (*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + if (subtype == VSLENGTH) + putc('#', fp); + + while (*p != '=') + putc(*p++, fp); + + if (subtype & VSNUL) + putc(':', fp); + + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + case VSTRIMLEFT: + putc('#', fp); + break; + case VSTRIMLEFTMAX: + putc('#', fp); + putc('#', fp); + break; + case VSTRIMRIGHT: + putc('%', fp); + break; + case VSTRIMRIGHTMAX: + putc('%', fp); + putc('%', fp); + break; + case VSLENGTH: + break; + default: + printf("", subtype); + } + 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(amount, pfx, fp) + int amount; + char *pfx; + FILE *fp; +{ + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} +#endif + + + +/* + * Debugging stuff. + */ + + +#ifdef DEBUG +FILE *tracefile; + +#if DEBUG == 2 +static int debug = 1; +#else +static int debug = 0; +#endif + + +static void +trputc(c) + int c; +{ + if (tracefile == NULL) + return; + putc(c, tracefile); + if (c == '\n') + fflush(tracefile); +} + +static void +trace(const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + if (tracefile != NULL) { + (void) vfprintf(tracefile, fmt, va); + if (strchr(fmt, '\n')) + (void) fflush(tracefile); + } + va_end(va); +} + + +static void +trputs(s) + const char *s; +{ + if (tracefile == NULL) + return; + fputs(s, tracefile); + if (strchr(s, '\n')) + fflush(tracefile); +} + + +static void +trstring(s) + char *s; +{ + char *p; + char c; + + if (tracefile == NULL) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch (*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; + case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +} + + +static void +trargs(ap) + char **ap; +{ + if (tracefile == NULL) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } + fflush(tracefile); +} + + +static void +opentrace() { + char s[100]; +#ifdef O_APPEND + int flags; +#endif + + if (!debug) + return; +#ifdef not_this_way + { + char *p; + if ((p = getenv("HOME")) == NULL) { + if (geteuid() == 0) + p = "/"; + else + p = "/tmp"; + } + strcpy(s, p); + strcat(s, "/trace"); + } +#else + strcpy(s, "./trace"); +#endif /* not_this_way */ + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + return; + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + fputs("\nTracing started.\n", tracefile); + fflush(tracefile); +} +#endif /* DEBUG */ + + +/* + * The trap builtin. + */ + +static int +trapcmd(argc, argv) + int argc; + char **argv; +{ + char *action; + char **ap; + int signo; + + if (argc <= 1) { + for (signo = 0 ; signo < NSIG ; signo++) { + if (trap[signo] != NULL) { + char *p; + const char *sn; + + p = single_quote(trap[signo]); + sn = sys_siglist[signo]; + if(sn==NULL) + sn = u_signal_names(0, &signo, 0); + if(sn==NULL) + sn = "???"; + printf("trap -- %s %s\n", p, sn); + stunalloc(p); + } + } + return 0; + } + ap = argv + 1; + if (argc == 2) + 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; +} + + + + + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +static void +setsignal(int signo) +{ + int action; + char *t; + 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; +#ifdef JOBS + case SIGTSTP: + case SIGTTOU: + if (mflag) + action = S_IGN; + break; +#endif + } + } + + t = &sigmode[signo - 1]; + if (*t == 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)) { + *t = S_IGN; /* don't hard ignore these */ + } else + *t = S_HARD_IGN; + } else { + *t = S_RESET; /* force to be set */ + } + } + if (*t == S_HARD_IGN || *t == 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; + sigemptyset(&act.sa_mask); + sigaction(signo, &act, 0); +} + +/* + * Ignore a signal. + */ + +static void +ignoresig(signo) + 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. + */ + +static void +onsig(int signo) +{ + if (signo == SIGINT && trap[SIGINT] == NULL) { + onint(); + return; + } + gotsig[signo - 1] = 1; + pendingsigs++; +} + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +static void +dotrap(void) +{ + int i; + int savestatus; + + for (;;) { + for (i = 1 ; ; i++) { + if (gotsig[i - 1]) + break; + if (i >= NSIG - 1) + goto done; + } + gotsig[i - 1] = 0; + savestatus=exitstatus; + evalstring(trap[i], 0); + exitstatus=savestatus; + } +done: + pendingsigs = 0; +} + +/* + * Called to exit the shell. + */ + +static void +exitshell(int status) +{ + struct jmploc loc1, loc2; + char *p; + + TRACE(("exitshell(%d) pid=%d\n", status, getpid())); + if (setjmp(loc1.loc)) { + goto l1; + } + if (setjmp(loc2.loc)) { + goto l2; + } + handler = &loc1; + if ((p = trap[0]) != NULL && *p != '\0') { + trap[0] = NULL; + evalstring(p, 0); + } +l1: handler = &loc2; /* probably unnecessary */ + flushall(); +#ifdef JOBS + setjobctl(0); +#endif +l2: _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; +} + +static struct var **hashvar (const char *); +static void showvars (const char *, int, int); +static struct var **findvar (struct var **, const char *); + +/* + * Initialize the varable symbol tables and import the environment + */ + +/* + * This routine initializes the builtin variables. It is called when the + * shell is initialized and again when a shell procedure is spawned. + */ + +static void +initvar() { + const struct varinit *ip; + struct var *vp; + struct var **vpp; + + for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { + if ((vp->flags & VEXPORT) == 0) { + vpp = hashvar(ip->text); + vp->next = *vpp; + *vpp = vp; + vp->text = strdup(ip->text); + vp->flags = ip->flags; + vp->func = ip->func; + } + } + /* + * PS1 depends on uid + */ + if ((vps1.flags & VEXPORT) == 0) { + vpp = hashvar("PS1="); + vps1.next = *vpp; + *vpp = &vps1; + vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# "); + vps1.flags = VSTRFIXED|VTEXTFIXED; + } +} + +/* + * Set the value of a variable. The flags argument is ored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +static void +setvar(name, val, flags) + const char *name, *val; + int flags; +{ + const char *p; + int len; + int namelen; + char *nameeq; + int isbad; + int vallen = 0; + + isbad = 0; + p = name; + if (! is_name(*p)) + isbad = 1; + p++; + for (;;) { + if (! is_in_name(*p)) { + if (*p == '\0' || *p == '=') + break; + isbad = 1; + } + p++; + } + namelen = p - name; + if (isbad) + error("%.*s: bad variable name", namelen, name); + len = namelen + 2; /* 2 is space for '=' and '\0' */ + if (val == NULL) { + flags |= VUNSET; + } else { + len += vallen = strlen(val); + } + INTOFF; + nameeq = ckmalloc(len); + memcpy(nameeq, name, namelen); + nameeq[namelen] = '='; + if (val) { + memcpy(nameeq + namelen + 1, val, vallen + 1); + } else { + nameeq[namelen + 1] = '\0'; + } + setvareq(nameeq, flags); + 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. + */ + +static void +setvareq(s, flags) + char *s; + int flags; +{ + struct var *vp, **vpp; + + vpp = hashvar(s); + flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); + if ((vp = *findvar(vpp, s))) { + if (vp->flags & VREADONLY) { + size_t len = strchr(s, '=') - s; + error("%.*s: is read only", len, s); + } + INTOFF; + + if (vp->func && (flags & VNOFUNC) == 0) + (*vp->func)(strchr(s, '=') + 1); + + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + + vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags; + vp->text = s; + + /* + * We could roll this to a function, to handle it as + * a regular variable function callback, but why bother? + */ + if (iflag && (vp == &vmpath || (vp == &vmail && !mpathset()))) + chkmail(1); + INTON; + return; + } + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags; + vp->text = s; + vp->next = *vpp; + vp->func = NULL; + *vpp = vp; +} + + + +/* + * Process a linked list of variable assignments. + */ + +static void +listsetvar(mylist) + struct strlist *mylist; + { + struct strlist *lp; + + INTOFF; + for (lp = mylist ; lp ; lp = lp->next) { + setvareq(savestr(lp->text), 0); + } + INTON; +} + + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +static const char * +lookupvar(name) + const char *name; + { + struct var *v; + + if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { + return strchr(v->text, '=') + 1; + } + return NULL; +} + + + +/* + * Search the environment of a builtin command. + */ + +static const char * +bltinlookup(const char *name) +{ + const struct strlist *sp; + + for (sp = cmdenviron ; sp ; sp = sp->next) { + if (varequal(sp->text, name)) + return strchr(sp->text, '=') + 1; + } + return lookupvar(name); +} + + + +/* + * Generate a list of exported variables. This routine is used to construct + * the third argument to execve when executing a program. + */ + +static char ** +environment() { + int nenv; + struct var **vpp; + struct var *vp; + char **env; + char **ep; + + nenv = 0; + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + nenv++; + } + ep = env = stalloc((nenv + 1) * sizeof *env); + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + *ep++ = vp->text; + } + *ep = NULL; + return env; +} + + +/* + * Called when a shell procedure is invoked to clear out nonexported + * variables. It is also necessary to reallocate variables of with + * VSTACK set since these are currently allocated on the stack. + */ + +static void +shprocvar(void) { + struct var **vpp; + struct var *vp, **prev; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (prev = vpp ; (vp = *prev) != NULL ; ) { + if ((vp->flags & VEXPORT) == 0) { + *prev = vp->next; + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + if ((vp->flags & VSTRFIXED) == 0) + ckfree(vp); + } else { + if (vp->flags & VSTACK) { + vp->text = savestr(vp->text); + vp->flags &=~ VSTACK; + } + prev = &vp->next; + } + } + } + initvar(); +} + + + +/* + * Command to list all variables which are set. Currently this command + * is invoked from the set command when the set command is called without + * any variables. + */ + +static int +showvarscmd(argc, argv) + int argc; + char **argv; +{ + showvars(nullstr, VUNSET, VUNSET); + return 0; +} + + + +/* + * The export and readonly commands. + */ + +static int +exportcmd(argc, argv) + int argc; + char **argv; +{ + struct var *vp; + char *name; + const char *p; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + int pflag; + + listsetvar(cmdenviron); + pflag = (nextopt("p") == 'p'); + if (argc > 1 && !pflag) { + while ((name = *argptr++) != NULL) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + if ((vp = *findvar(hashvar(name), name))) { + vp->flags |= flag; + goto found; + } + } + setvar(name, p, flag); +found:; + } + } else { + showvars(argv[0], flag, 0); + } + return 0; +} + + +/* + * The "local" command. + */ + +/* funcnest nonzero if we are currently evaluating a function */ + +static int +localcmd(argc, argv) + int argc; + char **argv; +{ + char *name; + + if (! funcnest) + error("Not in a function"); + while ((name = *argptr++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +static void +mklocal(name) + 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 optet_vals); + lvp->text = memcpy(p, optet_vals, sizeof optet_vals); + vp = NULL; + } else { + vpp = hashvar(name); + vp = *findvar(vpp, name); + if (vp == NULL) { + if (strchr(name, '=')) + setvareq(savestr(name), VSTRFIXED); + else + setvar(name, NULL, VSTRFIXED); + vp = *vpp; /* the new variable */ + lvp->text = NULL; + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (strchr(name, '=')) + setvareq(savestr(name), 0); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + + +/* + * Called after a function returns. + */ + +static void +poplocalvars() { + struct localvar *lvp; + struct var *vp; + + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + if (vp == NULL) { /* $- saved */ + memcpy(optet_vals, lvp->text, sizeof optet_vals); + ckfree(lvp->text); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + (void)unsetvar(vp->text); + } else { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } +} + + +static int +setvarcmd(argc, argv) + int argc; + char **argv; +{ + if (argc <= 2) + return unsetcmd(argc, argv); + else if (argc == 3) + setvar(argv[1], argv[2], 0); + else + error("List assignment not implemented"); + return 0; +} + + +/* + * The unset builtin command. We unset the function before we unset the + * variable to allow a function to be unset when there is a readonly variable + * with the same name. + */ + +static int +unsetcmd(argc, argv) + int argc; + char **argv; +{ + char **ap; + int i; + int flg_func = 0; + int flg_var = 0; + int ret = 0; + + while ((i = nextopt("vf")) != '\0') { + if (i == 'f') + flg_func = 1; + else + flg_var = 1; + } + if (flg_func == 0 && flg_var == 0) + flg_var = 1; + + for (ap = argptr; *ap ; ap++) { + if (flg_func) + unsetfunc(*ap); + if (flg_var) + ret |= unsetvar(*ap); + } + return ret; +} + + +/* + * Unset the specified variable. + */ + +static int +unsetvar(const char *s) +{ + struct var **vpp; + struct var *vp; + + vpp = findvar(hashvar(s), s); + vp = *vpp; + if (vp) { + if (vp->flags & VREADONLY) + return (1); + INTOFF; + if (*(strchr(vp->text, '=') + 1) != '\0') + setvar(s, nullstr, 0); + vp->flags &= ~VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + } + INTON; + return (0); + } + + return (0); +} + + + +/* + * 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]; +} + + + +/* + * Returns true if the two strings specify the same varable. The first + * variable name is terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +static int +varequal(const char *p, const char *q) +{ + while (*p == *q++) { + if (*p++ == '=') + return 1; + } + if (*p == '=' && *(q - 1) == '\0') + return 1; + return 0; +} + +static void +showvars(const char *myprefix, int mask, int xor) +{ + struct var **vpp; + struct var *vp; + const char *sep = myprefix == nullstr ? myprefix : spcstr; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if ((vp->flags & mask) ^ xor) { + char *p; + int len; + + p = strchr(vp->text, '=') + 1; + len = p - vp->text; + p = single_quote(p); + + printf("%s%s%.*s%s\n", myprefix, sep, len, + vp->text, p); + stunalloc(p); + } + } + } +} + +static struct var ** +findvar(struct var **vpp, const char *name) +{ + for (; *vpp; vpp = &(*vpp)->next) { + if (varequal((*vpp)->text, name)) { + break; + } + } + return vpp; +} + +/* + * Copyright (c) 1999 Herbert Xu + * This file contains code for the times builtin. + * $Id: ash.c,v 1.17 2001/08/02 05:02:45 andersen Exp $ + */ +static int timescmd (int argc, char **argv) +{ + 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; +} + +#ifdef ASH_MATH_SUPPORT +/* The let builtin. */ +int letcmd(int argc, char **argv) +{ + int errcode; + long result=0; + if (argc == 2) { + char *tmp, *expression, p[13]; + expression = strchr(argv[1], '='); + if (!expression) { + /* Cannot use 'error()' here, or the return code + * will be incorrect */ + out2fmt("sh: let: syntax error: \"%s\"\n", argv[1]); + return 0; + } + *expression = '\0'; + tmp = ++expression; + result = arith(tmp, &errcode); + if (errcode < 0) { + /* Cannot use 'error()' here, or the return code + * will be incorrect */ + out2fmt("sh: let: "); + if(errcode == -2) + out2fmt("divide by zero"); + else + out2fmt("syntax error: \"%s=%s\"\n", argv[1], expression); + return 0; + } + snprintf(p, 12, "%ld", result); + setvar(argv[1], savestr(p), 0); + } else if (argc >= 3) + synerror("invalid operand"); + return !result; +} +#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. + * + * 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/basename.c b/busybox/basename.c new file mode 100644 index 000000000..b83f387c2 --- /dev/null +++ b/busybox/basename.c @@ -0,0 +1,52 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini basename implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ + +#include +#include "busybox.h" +#include + +extern int basename_main(int argc, char **argv) +{ + int m, n; + char *s; + + if ((argc < 2) || (**(argv + 1) == '-')) { + show_usage(); + } + + argv++; + + s = get_last_path_component(*argv); + + if (argc>2) { + argv++; + n = strlen(*argv); + m = strlen(s); + if (m>=n && strncmp(s+m-n, *argv, n)==0) + s[m-n] = '\0'; + } + puts(s); + return EXIT_SUCCESS; +} diff --git a/busybox/busybox.c b/busybox/busybox.c new file mode 100644 index 000000000..7a220f7b0 --- /dev/null +++ b/busybox/busybox.c @@ -0,0 +1,181 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include "busybox.h" +#ifdef BB_LOCALE_SUPPORT +#include +#endif + +int been_there_done_that = 0; /* Also used in applets.c */ +const char *applet_name; + +#ifdef BB_FEATURE_INSTALLER +/* + * directory table + * this should be consistent w/ the enum, busybox.h::Location, + * or else... + */ +static char* install_dir[] = { + "/", + "/bin", + "/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 char *busybox_fullpath() +{ + 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) { + perror_msg("%s", fpc); + } + free(fpc); + } +} + +#endif /* BB_FEATURE_INSTALLER */ + +int main(int argc, char **argv) +{ + const char *s; + + for (s = applet_name = argv[0]; *s != '\0';) { + if (*s++ == '/') + applet_name = s; + } + + /* Add in a special case hack for a leading hyphen */ + if (**argv == '-' && *(*argv+1)!= '-') { + applet_name = (*argv+1); + } + +#ifdef BB_LOCALE_SUPPORT +#ifdef BB_INIT + if(getpid()!=1) /* Do not set locale for `init' */ +#endif + { + setlocale(LC_ALL, ""); + } +#endif + + run_applet_by_name(applet_name, argc, argv); + error_msg_and_die("applet not found"); +} + + +int busybox_main(int argc, char **argv) +{ + int col = 0, len, i; + +#ifdef BB_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 /* BB_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; + + 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", full_version); + + while (a->name != 0) { + col += + fprintf(stderr, "%s%s", ((col == 0) ? "\t" : ", "), + (a++)->name); + if (col > 60 && 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 */ + 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/busybox.h b/busybox/busybox.h new file mode 100644 index 000000000..f79dac8c8 --- /dev/null +++ b/busybox/busybox.h @@ -0,0 +1,106 @@ +/* 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 +#include +#include +#include + +#define BB_BANNER "BusyBox v" BB_VER " (" BB_BT ")" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#include + + +enum Location { + _BB_DIR_ROOT = 0, + _BB_DIR_BIN, + _BB_DIR_SBIN, + _BB_DIR_USR_BIN, + _BB_DIR_USR_SBIN +}; + +struct BB_applet { + const char* name; + int (*main)(int argc, char** argv); + enum Location location; +}; +/* 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 BB_FEATURE_BUFFERS_GO_ON_STACK +#define RESERVE_BB_BUFFER(buffer,len) char buffer[len] +#define RESERVE_BB_UBUFFER(buffer,len) unsigned char buffer[len] +#define RELEASE_BB_BUFFER(buffer) ((void)0) +#else +#ifdef BB_FEATURE_BUFFERS_GO_IN_BSS +#define RESERVE_BB_BUFFER(buffer,len) static char buffer[len] +#define RESERVE_BB_UBUFFER(buffer,len) static unsigned char buffer[len] +#define RELEASE_BB_BUFFER(buffer) ((void)0) +#else +#define RESERVE_BB_BUFFER(buffer,len) char *buffer=xmalloc(len) +#define RESERVE_BB_UBUFFER(buffer,len) unsigned char *buffer=xmalloc(len) +#define RELEASE_BB_BUFFER(buffer) free (buffer) +#endif +#endif + + +/* Bit map related macros -- libc5 doens't provide these... sigh. */ +#ifndef setbit +#define NBBY CHAR_BIT +#define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY)) +#define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) +#define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY))) +#define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) +#endif + +#ifndef RB_POWER_OFF +/* Stop system and switch power off if possible. */ +#define RB_POWER_OFF 0x4321fedc +#endif + + +/* Pull in the utility routines from libbb */ +#include "libbb/libbb.h" + + + +#endif /* _BB_INTERNAL_H_ */ diff --git a/busybox/busybox.mkll b/busybox/busybox.mkll new file mode 100755 index 000000000..4e15e1611 --- /dev/null +++ b/busybox/busybox.mkll @@ -0,0 +1,24 @@ +#!/bin/sh +# Make busybox links list file. + +# input $1: full path to Config.h +# input $2: full path to applets.h +# output (stdout): list of pathnames that should be linked to busybox + +# Maintainer: Larry Doolittle + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +CONFIG_H=${1:-Config.h} +APPLETS_H=${2:-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/busybox.sh b/busybox/busybox.sh new file mode 100755 index 000000000..7c3deb20e --- /dev/null +++ b/busybox/busybox.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +RAW=` \ + gcc -E -dM ${1:-Config.h} | \ + sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\/\1.c/gp;' \ + | tr A-Z a-z | sort +` +test "${RAW}" != "" || exit +if [ -d "$BB_SRC_DIR" ]; then cd $BB_SRC_DIR; fi +# By running $RAW through "ls", we avoid listing +# source files that don't exist. +ls $RAW 2>/dev/null | tr '\n' ' ' + diff --git a/busybox/busybox.spec b/busybox/busybox.spec new file mode 100644 index 000000000..339311770 --- /dev/null +++ b/busybox/busybox.spec @@ -0,0 +1,44 @@ +%define name busybox +%define epoch 0 +%define version 0.60.0 +%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.lineo.com/ +Source: ftp://oss.lineo.com/busybox/%{name}-%{version}.tar.gz +Buildroot: /var/tmp/%{name}-%{version} +Packager : Erik Andersen + +%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/cat.c b/busybox/cat.c new file mode 100644 index 000000000..aa8528d6a --- /dev/null +++ b/busybox/cat.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini Cat implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include "busybox.h" + +extern int cat_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + + if (argc == 1) { + print_file(stdin); + return status; + } + + while (--argc > 0) { + if(!(strcmp(*++argv, "-"))) { + print_file(stdin); + } else if (print_file_by_name(*argv) == FALSE) { + status = EXIT_FAILURE; + } + } + return status; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/chgrp.c b/busybox/chgrp.c new file mode 100644 index 000000000..fbc1036a8 --- /dev/null +++ b/busybox/chgrp.c @@ -0,0 +1,91 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown/chmod/chgrp implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include "busybox.h" + +/* Don't use lchown for libc5 or glibc older then 2.1.x */ +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) +#define lchown chown +#endif + + +static long gid; + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +{ + if (lchown(fileName, statbuf->st_uid, (gid == -1) ? statbuf->st_gid : gid) == 0) { + return (TRUE); + } + perror(fileName); + return (FALSE); +} + +int chgrp_main(int argc, char **argv) +{ + int opt; + int recursiveFlag = FALSE; + char *p=NULL; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "R")) > 0) { + switch (opt) { + case 'R': + recursiveFlag = TRUE; + break; + default: + show_usage(); + } + } + + if (argc > optind && argc > 2 && argv[optind]) { + /* Find the selected group */ + gid = strtoul(argv[optind], &p, 10); /* maybe it's already numeric */ + if (argv[optind] == p) + gid = my_getgrnam(argv[optind]); + } else { + error_msg_and_die(too_few_args); + } + + /* Ok, ready to do the deed now */ + while (++optind < argc) { + if (recursive_action (argv[optind], recursiveFlag, FALSE, FALSE, + fileAction, fileAction, NULL) == FALSE) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; + +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/chmod.c b/busybox/chmod.c new file mode 100644 index 000000000..9139b3f4d --- /dev/null +++ b/busybox/chmod.c @@ -0,0 +1,85 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown/chmod/chgrp implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include "busybox.h" + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +{ + if (!parse_mode((char *)junk, &(statbuf->st_mode))) + error_msg_and_die("internal error"); + if (chmod(fileName, statbuf->st_mode) == 0) + return (TRUE); + perror(fileName); + return (FALSE); +} + +int chmod_main(int argc, char **argv) +{ + int i; + int opt; + int recursiveFlag = FALSE; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "R")) > 0) { + switch (opt) { + case 'R': + recursiveFlag = TRUE; + break; + default: + show_usage(); + } + } + + if (argc > optind && argc > 2 && argv[optind]) { + /* Parse the specified mode */ + mode_t mode; + if (parse_mode(argv[optind], &mode) == FALSE) { + error_msg_and_die( "unknown mode: %s", argv[optind]); + } + } else { + error_msg_and_die(too_few_args); + } + + /* Ok, ready to do the deed now */ + for (i = optind + 1; i < argc; i++) { + if (recursive_action (argv[i], recursiveFlag, FALSE, FALSE, fileAction, + fileAction, argv[optind]) == FALSE) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/chown.c b/busybox/chown.c new file mode 100644 index 000000000..d1e52deda --- /dev/null +++ b/busybox/chown.c @@ -0,0 +1,113 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown/chmod/chgrp implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include "busybox.h" + +/* Don't use lchown for libc5 or 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) { + return (TRUE); + } + perror(fileName); + return (FALSE); +} + +int chown_main(int argc, char **argv) +{ + int opt; + int recursiveFlag = FALSE, + noderefFlag = FALSE; + char *groupName=NULL; + char *p=NULL; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "Rh")) > 0) { + switch (opt) { + case 'R': + recursiveFlag = TRUE; + break; + case 'h': + noderefFlag = TRUE; + break; + default: + show_usage(); + } + } + + if (noderefFlag) chown_func = lchown; + + if (argc > optind && argc > 2 && argv[optind]) { + /* First, check if there is a group name here */ + groupName = strchr(argv[optind], '.'); + if (groupName == NULL) + groupName = strchr(argv[optind], ':'); + if (groupName) { + *groupName++ = '\0'; + gid = strtoul(groupName, &p, 10); + if (groupName == p) + gid = my_getgrnam(groupName); + } else { + gid = -1; + } + /* Now check for the username */ + uid = strtoul(argv[optind], &p, 10); /* Is is numeric? */ + if (argv[optind] == p) { + uid = my_getpwnam(argv[optind]); + } + } else { + error_msg_and_die(too_few_args); + } + + /* Ok, ready to do the deed now */ + while (++optind < argc) { + if (recursive_action (argv[optind], recursiveFlag, FALSE, FALSE, + fileAction, fileAction, NULL) == FALSE) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; + +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/chroot.c b/busybox/chroot.c new file mode 100644 index 000000000..0440e46b9 --- /dev/null +++ b/busybox/chroot.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chroot implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include "busybox.h" + +int chroot_main(int argc, char **argv) +{ + char *prog; + + if ((argc < 2) || (**(argv + 1) == '-')) { + show_usage(); + } + argc--; + argv++; + + if (chroot(*argv) || (chdir("/"))) { + perror_msg_and_die("cannot change root directory to %s", *argv); + } + + argc--; + argv++; + if (argc >= 1) { + prog = *argv; + execvp(*argv, argv); + } else { +#if defined(BB_SH) && defined BB_FEATURE_SH_STANDALONE_SHELL + char shell[] = "/bin/sh"; + char *shell_argv[2] = { shell, NULL }; + applet_name = shell; + shell_main(1, shell_argv); + return EXIT_SUCCESS; +#else + prog = getenv("SHELL"); + if (!prog) + prog = "/bin/sh"; + execlp(prog, prog, NULL); +#endif + } + perror_msg_and_die("cannot execute %s", prog); + +} + + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/chvt.c b/busybox/chvt.c new file mode 100644 index 000000000..c76e9c780 --- /dev/null +++ b/busybox/chvt.c @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * chvt.c - aeb - 940227 - Change virtual terminal + * + * busyboxed by Erik Andersen + */ + +/* getopt not needed */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +/* From */ +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) || (**(argv + 1) == '-')) + show_usage(); + fd = get_console_fd("/dev/console"); + num = atoi(argv[1]); + if (ioctl(fd, VT_ACTIVATE, num)) + perror_msg_and_die("VT_ACTIVATE"); + if (ioctl(fd, VT_WAITACTIVE, num)) + 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/clear.c b/busybox/clear.c new file mode 100644 index 000000000..503bafa16 --- /dev/null +++ b/busybox/clear.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini clear implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include "busybox.h" + + +extern int clear_main(int argc, char **argv) +{ + printf("\033[H\033[J"); + return EXIT_SUCCESS; +} diff --git a/busybox/cmdedit.c b/busybox/cmdedit.c new file mode 100644 index 000000000..16ec2f823 --- /dev/null +++ b/busybox/cmdedit.c @@ -0,0 +1,1521 @@ +/* vi: set sw=4 ts=4: */ +/* + * Termios command line History and Editting. + * + * Copyright (c) 1986-2001 may safely be consumed by a BSD or GPL license. + * Written by: Vladimir Oleynik + * + * Used ideas: + * Adam Rogoyski + * Dave Cinege + * Jakub Jelinek (c) 1995 + * Erik Andersen (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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +#ifdef BB_LOCALE_SUPPORT +#define Isprint(c) isprint((c)) +#else +#define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') ) +#endif + +#ifndef TEST + +#define D(x) + +#else + +#define BB_FEATURE_COMMAND_EDITING +#define BB_FEATURE_COMMAND_TAB_COMPLETION +#define BB_FEATURE_COMMAND_USERNAME_COMPLETION +#define BB_FEATURE_NONPRINTABLE_INVERSE_PUT +#define BB_FEATURE_CLEAN_UP + +#define D(x) x + +#endif /* TEST */ + +#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION +#include +#include +#endif + +#ifdef BB_FEATURE_COMMAND_EDITING + +#ifndef BB_FEATURE_COMMAND_TAB_COMPLETION +#undef BB_FEATURE_COMMAND_USERNAME_COMPLETION +#endif + +#if defined(BB_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(BB_FEATURE_SH_FANCY_PROMPT) +#define BB_FEATURE_GETUSERNAME_AND_HOMEDIR +#endif + +#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR +# ifndef TEST +# include "pwd_grp/pwd.h" +# else +# include +# endif /* TEST */ +#endif /* advanced FEATURES */ + + + +struct history { + char *s; + struct history *p; + struct history *n; +}; + +/* Maximum length of the linked list for the command line history */ +static const int MAX_HISTORY = 15; + +/* First element in command line list */ +static struct history *his_front = NULL; + +/* Last element in command line list */ +static struct history *his_end = NULL; + + +#include +#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 int history_counter = 0; /* Number of commands in history list */ +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 BB_FEATURE_SH_FANCY_PROMPT + const +#endif +char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */ + +#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR +static char *user_buf = ""; +static char *home_pwd_buf = ""; +static int my_euid; +#endif + +#ifdef BB_FEATURE_SH_FANCY_PROMPT +static char *hostname_buf = ""; +static int num_ok_lines = 1; +#endif + + +#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION + +#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR +static int my_euid; +#endif + +static int my_uid; +static int my_gid; + +#endif /* BB_FEATURE_COMMAND_TAB_COMPLETION */ + +/* It seems that libc5 doesn't know what a sighandler_t is... */ +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) +typedef void (*sighandler_t) (int); +#endif + +static void cmdedit_setwidth(int w, int redraw_flg); + +static void win_changed(int nsig) +{ + struct winsize win = { 0, 0, 0, 0 }; + static sighandler_t previous_SIGWINCH_handler; /* for reset */ + + /* emulate || signal call */ + if (nsig == -SIGWINCH || nsig == SIGWINCH) { + ioctl(0, TIOCGWINSZ, &win); + if (win.ws_col > 0) { + cmdedit_setwidth(win.ws_col, 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(fileno(stdin), (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); +#ifdef BB_FEATURE_CLEAN_UP + if (his_front) { + struct history *n; + + while (his_front != his_end) { + n = his_front->n; + free(his_front->s); + free(his_front); + his_front = n; + } + } +#endif +} + + +/* 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 BB_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) +{ + fputs(s, stdout); +} +static inline void beep(void) +{ + putchar('\007'); +} + +/* Move back one charactor */ +/* 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 BB_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 *)unknown; + } + + while (*prmt_ptr) { + pbuf = buf; + pbuf[1] = 0; + c = *prmt_ptr++; + if (c == '\\') { + const char *cp = prmt_ptr; + int l; + + c = process_escape_sequence(&prmt_ptr); + if(prmt_ptr==cp) { + if (*cp == 0) + break; + c = *prmt_ptr++; + switch (c) { +#ifdef BB_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 BB_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) 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 *)unknown) + free(pwd_buf); + cmdedit_prompt = prmt_mem_ptr; + cmdedit_prmt_len = prmt_len - sub_len; + put_prompt(); +} +#endif + + +/* draw promt, 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 charactor */ +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 BB_FEATURE_GETUSERNAME_AND_HOMEDIR + struct passwd *entry; + + my_euid = geteuid(); + entry = getpwuid(my_euid); + if (entry) { + user_buf = xstrdup(entry->pw_name); + home_pwd_buf = xstrdup(entry->pw_dir); + } +#endif + +#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION + +#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR + my_euid = geteuid(); +#endif + my_uid = getuid(); + my_gid = getgid(); +#endif /* BB_FEATURE_COMMAND_TAB_COMPLETION */ + handlers_sets |= SET_ATEXIT; + atexit(cmdedit_reset_term); /* be sure to do this only once */ + } +} + +#ifdef BB_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 BB_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)) { + + temp = xmalloc(3 + strlen(entry->pw_name)); + sprintf(temp, "~%s/", entry->pw_name); + matches = xrealloc(matches, (nm + 1) * sizeof(char *)); + + matches[nm++] = temp; + } + } + + endpwent(); + (*num_matches) = nm; + return (matches); + } +} +#endif /* BB_FEATURE_COMMAND_USERNAME_COMPLETION */ + +enum { + FIND_EXE_ONLY = 0, + FIND_DIR_ONLY = 1, + FIND_FILE_ONLY = 2, +}; + +static int path_parse(char ***p, int flags) +{ + int npth; + char *tmp; + char *pth; + + /* if not setenv PATH variable, to search cur dir "." */ + if (flags != FIND_EXE_ONLY || (pth = getenv("PATH")) == 0 || + /* PATH= or PATH=: */ + *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; /* : */ + } else + break; + } + + *p = xmalloc(npth * sizeof(char *)); + + tmp = pth; + (*p)[0] = 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; /* : */ + } 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 BB_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) == TRUE)) + 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 BB_FEATURE_NONPRINTABLE_INVERSE_PUT + if (matchBuf[i] == '\t') /* algorithm equivalent */ + int_buf[j] = ' ' | QUOT; +#endif + } +#ifdef BB_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; +} + + +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 == FALSE) { + + 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 BB_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 1) { + char *tmp1; + + beep(); + if (!matches) + return; /* not found */ + /* sort */ + qsort(matches, num_matches, sizeof(char *), match_compare); + + /* find minimal match */ + tmp = 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 i, col, l; + int sav_cursor = cursor; /* change goto_new_line() */ + + /* Go to the next line */ + goto_new_line(); + for (i = 0, col = 0; i < num_matches; i++) { + l = strlen(matches[i]); + if (l < 14) + l = 14; + printf("%-14s ", matches[i]); + if ((l += 2) > 16) + while (l % 16) { + putchar(' '); + l++; + } + col += l; + col -= (col / cmdedit_termw) * cmdedit_termw; + if (col > 60 && matches[i + 1] != NULL) { + putchar('\n'); + col = 0; + } + } + /* Go to the next line and rewrite */ + putchar('\n'); + redraw(0, len - sav_cursor); + } + } +} +#endif /* BB_FEATURE_COMMAND_TAB_COMPLETION */ + +static void get_previous_history(struct history **hp, struct history *p) +{ + if ((*hp)->s) + free((*hp)->s); + (*hp)->s = xstrdup(command_ps); + *hp = p; +} + +static inline void get_next_history(struct history **hp) +{ + get_previous_history(hp, (*hp)->n); +} + +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 (sortof 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; + struct history *hp = his_end; + + /* 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); +#ifndef linux + /* 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; +#endif + 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(); + command[0] = 0; + len = 0; + lastWasTab = FALSE; + put_prompt(); + break; + case 4: + /* Control-d -- Delete one character, or exit + * if the len=0 and no chars to delete */ + if (len == 0) { +prepare_to_die: +#if !defined(BB_ASH) + printf("exit"); + goto_new_line(); + /* cmdedit_reset_term() called in atexit */ + exit(EXIT_SUCCESS); +#else + break_out = -1; /* for control stoped jobs */ + 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 BB_FEATURE_COMMAND_TAB_COMPLETION + input_tab(&lastWasTab); +#endif + break; + case 14: + /* Control-n -- Get next command in history */ + if (hp && hp->n && hp->n->s) { + get_next_history(&hp); + goto rewrite_line; + } else { + beep(); + } + break; + case 16: + /* Control-p -- Get previous command from history */ + if (hp && hp->p) { + get_previous_history(&hp, hp->p); + goto rewrite_line; + } else { + beep(); + } + break; + case 21: + /* Control-U -- Clear line before cursor */ + if (cursor) { + strcpy(command, command + cursor); + redraw(cmdedit_y, len -= cursor); + } + 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; + } + switch (c) { +#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION + case '\t': /* Alt-Tab */ + + input_tab(&lastWasTab); + break; +#endif + case 'A': + /* Up Arrow -- Get previous command from history */ + if (hp && hp->p) { + get_previous_history(&hp, hp->p); + goto rewrite_line; + } else { + beep(); + } + break; + case 'B': + /* Down Arrow -- Get next command in history */ + if (hp && hp->n && hp->n->s) { + get_next_history(&hp); + goto rewrite_line; + } else { + beep(); + } + break; + + /* Rewrite the line with the selected history item */ + rewrite_line: + /* change command */ + len = strlen(strcpy(command, hp->s)); + /* redraw and go to end line */ + redraw(cmdedit_y, 0); + break; + 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 (Ctrl-A) */ + input_backward(cursor); + break; + case '4': + case 'F': + /* End (Ctrl-E) */ + input_end(); + break; + default: + if (!(c >= '1' && c <= '9')) + c = 0; + beep(); + } + if (c >= '1' && c <= '9') + do + if (safe_read(0, &c, 1) < 1) + goto prepare_to_die; + while (c != '~'); + break; + } + + default: /* If it's regular input, do the normal thing */ +#ifdef BB_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; + + /* Handle command history log */ + if (len) { /* no put empty line */ + + struct history *h = his_end; + char *ss; + + ss = xstrdup(command); /* duplicate */ + + if (h == 0) { + /* No previous history -- this memory is never freed */ + h = his_front = xmalloc(sizeof(struct history)); + h->n = xmalloc(sizeof(struct history)); + + h->p = NULL; + h->s = ss; + h->n->p = h; + h->n->n = NULL; + h->n->s = NULL; + his_end = h->n; + history_counter++; + } else { + /* Add a new history command -- this memory is never freed */ + h->n = xmalloc(sizeof(struct history)); + + h->n->p = h; + h->n->n = NULL; + h->n->s = NULL; + h->s = ss; + his_end = h->n; + + /* After max history, remove the oldest command */ + if (history_counter >= MAX_HISTORY) { + + struct history *p = his_front->n; + + p->p = NULL; + free(his_front->s); + free(his_front); + his_front = p; + } else { + history_counter++; + } + } +#if defined(BB_FEATURE_SH_FANCY_PROMPT) + num_ok_lines++; +#endif + } + if(break_out>0) { + command[len++] = '\n'; /* set '\n' */ + command[len] = 0; + } +#if defined(BB_FEATURE_CLEAN_UP) && defined(BB_FEATURE_COMMAND_TAB_COMPLETION) + input_tab(0); /* strong free */ +#endif +#if defined(BB_FEATURE_SH_FANCY_PROMPT) + free(cmdedit_prompt); +#endif + cmdedit_reset_term(); + return len; +} + + + +#endif /* BB_FEATURE_COMMAND_EDITING */ + + +#ifdef TEST + +const char *applet_name = "debug stuff usage"; +const char *memory_exhausted = "Memory exhausted"; + +#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT +#include +#endif + +int main(int argc, char **argv) +{ + char buff[BUFSIZ]; + char *prompt = +#if defined(BB_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 BB_FEATURE_NONPRINTABLE_INVERSE_PUT + setlocale(LC_ALL, ""); +#endif + while(1) { + int l; + cmdedit_read_input(prompt, buff); + l = strlen(buff); + if(l==0) + break; + if(l > 0 && buff[l-1] == '\n') + buff[l-1] = 0; + printf("*** cmdedit_read_input() returned line =%s=\n", buff); + } + printf("*** cmdedit_read_input() detect ^C\n"); + return 0; +} + +#endif /* TEST */ diff --git a/busybox/cmdedit.h b/busybox/cmdedit.h new file mode 100644 index 000000000..83893572a --- /dev/null +++ b/busybox/cmdedit.h @@ -0,0 +1,6 @@ +#ifndef CMDEDIT_H +#define CMDEDIT_H + +int cmdedit_read_input(char* promptStr, char* command); + +#endif /* CMDEDIT_H */ diff --git a/busybox/cmp.c b/busybox/cmp.c new file mode 100644 index 000000000..6d579461d --- /dev/null +++ b/busybox/cmp.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cmp implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include "busybox.h" + +int cmp_main(int argc, char **argv) +{ + FILE *fp1 = NULL, *fp2 = stdin; + char *filename1, *filename2 = "-"; + int c, c1, c2, char_pos = 1, line_pos = 1, silent = FALSE; + + while ((c = getopt(argc, argv, "s")) != EOF) { + switch (c) { + case 's': + silent = TRUE; + break; + default: + show_usage(); + } + } + + filename1 = argv[optind]; + switch (argc - optind) { + case 2: + fp2 = xfopen(filename2 = argv[optind + 1], "r"); + case 1: + fp1 = xfopen(filename1, "r"); + break; + default: + show_usage(); + } + + do { + c1 = fgetc(fp1); + c2 = fgetc(fp2); + if (c1 != c2) { + if (silent) + return EXIT_FAILURE; + if (c1 == EOF) + printf("EOF on %s\n", filename1); + else if (c2 == EOF) + printf("EOF on %s\n", filename2); + else + printf("%s %s differ: char %d, line %d\n", filename1, filename2, + char_pos, line_pos); + return EXIT_FAILURE; + } + char_pos++; + if (c1 == '\n') + line_pos++; + } while (c1 != EOF); + + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/chvt.c b/busybox/console-tools/chvt.c new file mode 100644 index 000000000..c76e9c780 --- /dev/null +++ b/busybox/console-tools/chvt.c @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * chvt.c - aeb - 940227 - Change virtual terminal + * + * busyboxed by Erik Andersen + */ + +/* getopt not needed */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +/* From */ +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) || (**(argv + 1) == '-')) + show_usage(); + fd = get_console_fd("/dev/console"); + num = atoi(argv[1]); + if (ioctl(fd, VT_ACTIVATE, num)) + perror_msg_and_die("VT_ACTIVATE"); + if (ioctl(fd, VT_WAITACTIVE, num)) + 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 index 000000000..503bafa16 --- /dev/null +++ b/busybox/console-tools/clear.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini clear implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#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 index 000000000..15cd0c9b9 --- /dev/null +++ b/busybox/console-tools/deallocvt.c @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * disalloc.c - aeb - 940501 - Disallocate virtual terminal(s) + * Renamed deallocvt. + */ +#include +#include +#include +#include +#include +#include "busybox.h" + +/* From */ +static const int VT_DISALLOCATE = 0x5608; /* free memory associated to vt */ + +int deallocvt_main(int argc, char *argv[]) +{ + int fd, num, i; + + //if ((argc > 2) || ((argv == 2) && (**(argv + 1) == '-'))) + if (argc > 2) + show_usage(); + + fd = get_console_fd("/dev/console"); + + if (argc == 1) { + /* deallocate all unused consoles */ + if (ioctl(fd, VT_DISALLOCATE, 0)) + perror_msg_and_die("VT_DISALLOCATE"); + } else { + for (i = 1; i < argc; i++) { + num = atoi(argv[i]); + if (num == 0) + error_msg("0: illegal VT number"); + else if (num == 1) + error_msg("VT 1 cannot be deallocated"); + else if (ioctl(fd, VT_DISALLOCATE, num)) + 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 index 000000000..22652a5e2 --- /dev/null +++ b/busybox/console-tools/dumpkmap.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dumpkmap implementation for busybox + * + * Copyright (C) Arne Bernin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* From */ +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 */ +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]=='-') { + show_usage(); + } + + fd = open(CURRENT_VC, O_RDWR); + if (fd < 0) { + perror_msg("Error opening " CURRENT_VC); + return EXIT_FAILURE; + } + + 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) { + + error_msg("ioctl returned: %s, %s, %s, %xqq", strerror(errno),(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/loadacm.c b/busybox/console-tools/loadacm.c new file mode 100644 index 000000000..3fb4e7665 --- /dev/null +++ b/busybox/console-tools/loadacm.c @@ -0,0 +1,357 @@ +/* vi: set sw=4 ts=4: */ +/* + * Derived from + * mapscrn.c - version 0.92 + * + * Was taken from console-tools and adapted by + * Peter Novodvorsky + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +typedef unsigned short unicode; + +static long int ctoi(unsigned char *s, int *is_unicode); +static int old_screen_map_read_ascii(FILE * fp, unsigned char buf[]); +static int uni_screen_map_read_ascii(FILE * fp, unicode buf[], int *is_unicode); +static unicode utf8_to_ucs2(char *buf); +static int screen_map_load(int fd, FILE * fp); + +int loadacm_main(int argc, char **argv) +{ + int fd; + + if (argc>=2 && *argv[1]=='-') { + show_usage(); + } + + fd = open(CURRENT_VC, O_RDWR); + if (fd < 0) { + perror_msg_and_die("Error opening " CURRENT_VC); + } + + if (screen_map_load(fd, stdin)) { + perror_msg_and_die("Error loading acm"); + } + + write(fd, "\033(K", 3); + + return EXIT_SUCCESS; +} + +static int screen_map_load(int fd, FILE * fp) +{ + struct stat stbuf; + unicode wbuf[E_TABSZ]; + unsigned char buf[E_TABSZ]; + int parse_failed = 0; + int is_unicode; + + if (fstat(fileno(fp), &stbuf)) + perror_msg_and_die("Cannot stat map file"); + + /* first try a UTF screen-map: either ASCII (no restriction) or binary (regular file) */ + if (! + (parse_failed = + (-1 == uni_screen_map_read_ascii(fp, wbuf, &is_unicode))) +|| (S_ISREG(stbuf.st_mode) && (stbuf.st_size == (sizeof(unicode) * E_TABSZ)))) { /* test for binary UTF map by size */ + if (parse_failed) { + if (-1 == fseek(fp, 0, SEEK_SET)) { + if (errno == ESPIPE) + error_msg_and_die("16bit screen-map MUST be a regular file."); + else + perror_msg_and_die("fseek failed reading binary 16bit screen-map"); + } + + if (fread(wbuf, sizeof(unicode) * E_TABSZ, 1, fp) != 1) + perror_msg_and_die("Cannot read [new] map from file"); +#if 0 + else + error_msg("Input screen-map is binary."); +#endif + } + + /* if it was effectively a 16-bit ASCII, OK, else try to read as 8-bit map */ + /* same if it was binary, ie. if parse_failed */ + if (parse_failed || is_unicode) { + if (ioctl(fd, PIO_UNISCRNMAP, wbuf)) + perror_msg_and_die("PIO_UNISCRNMAP ioctl"); + else + return 0; + } + } + + /* rewind... */ + if (-1 == fseek(fp, 0, SEEK_SET)) { + if (errno == ESPIPE) + error_msg("Assuming 8bit screen-map - MUST be a regular file."), + exit(1); + else + perror_msg_and_die("fseek failed assuming 8bit screen-map"); + } + + /* ... and try an old 8-bit screen-map */ + if (!(parse_failed = (-1 == old_screen_map_read_ascii(fp, buf))) || + (S_ISREG(stbuf.st_mode) && (stbuf.st_size == E_TABSZ))) { /* test for binary old 8-bit map by size */ + if (parse_failed) { + if (-1 == fseek(fp, 0, SEEK_SET)) { + if (errno == ESPIPE) + /* should not - it succedeed above */ + error_msg_and_die("fseek() returned ESPIPE !"); + else + perror_msg_and_die("fseek for binary 8bit screen-map"); + } + + if (fread(buf, E_TABSZ, 1, fp) != 1) + perror_msg_and_die("Cannot read [old] map from file"); +#if 0 + else + error_msg("Input screen-map is binary."); +#endif + } + + if (ioctl(fd, PIO_SCRNMAP, buf)) + perror_msg_and_die("PIO_SCRNMAP ioctl"); + else + return 0; + } + error_msg("Error parsing symbolic map"); + return(1); +} + + +/* + * - reads `fp' as a 16-bit ASCII SFM file. + * - returns -1 on error. + * - returns it in `unicode' in an E_TABSZ-elements array. + * - sets `*is_unicode' flagiff there were any non-8-bit + * (ie. real 16-bit) mapping. + * + * FIXME: ignores everything after second word + */ +static int uni_screen_map_read_ascii(FILE * fp, unicode buf[], int *is_unicode) +{ + char buffer[256]; /* line buffer reading file */ + char *p, *q; /* 1st + 2nd words in line */ + int in, on; /* the same, as numbers */ + int tmp_is_unicode; /* tmp for is_unicode calculation */ + int i; /* loop index - result holder */ + int ret_code = 0; /* return code */ + sigset_t acmsigset, old_sigset; + + assert(is_unicode); + + *is_unicode = 0; + + /* first 128 codes defaults to ASCII */ + for (i = 0; i < 128; i++) + buf[i] = i; + /* remaining defaults to replacement char (usually E_TABSZ = 256) */ + for (; i < E_TABSZ; i++) + buf[i] = 0xfffd; + + /* block SIGCHLD */ + sigemptyset(&acmsigset); + sigaddset(&acmsigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &acmsigset, &old_sigset); + + do { + if (NULL == fgets(buffer, sizeof(buffer), fp)) { + if (feof(fp)) + break; + else + perror_msg_and_die("uni_screen_map_read_ascii() can't read line"); + } + + /* get "charset-relative charcode", stripping leading spaces */ + p = strtok(buffer, " \t\n"); + + /* skip empty lines and comments */ + if (!p || *p == '#') + continue; + + /* get unicode mapping */ + q = strtok(NULL, " \t\n"); + if (q) { + in = ctoi(p, NULL); + if (in < 0 || in > 255) { + ret_code = -1; + break; + } + + on = ctoi(q, &tmp_is_unicode); + if (in < 0 && on > 65535) { + ret_code = -1; + break; + } + + *is_unicode |= tmp_is_unicode; + buf[in] = on; + } else { + ret_code = -1; + break; + } + } + while (1); /* terminated by break on feof() */ + + /* restore sig mask */ + sigprocmask(SIG_SETMASK, &old_sigset, NULL); + + return ret_code; +} + + +static int old_screen_map_read_ascii(FILE * fp, unsigned char buf[]) +{ + char buffer[256]; + int in, on; + char *p, *q; + + for (in = 0; in < 256; in++) + buf[in] = in; + + while (fgets(buffer, sizeof(buffer) - 1, fp)) { + p = strtok(buffer, " \t\n"); + + if (!p || *p == '#') + continue; + + q = strtok(NULL, " \t\n#"); + if (q) { + in = ctoi(p, NULL); + if (in < 0 || in > 255) + return -1; + + on = ctoi(q, NULL); + if (in < 0 && on > 255) + return -1; + + buf[in] = on; + } else + return -1; + } + + return (0); +} + + +/* + * - converts a string into an int. + * - supports dec and hex bytes, hex UCS2, single-quoted byte and UTF8 chars. + * - returns the converted value + * - if `is_unicode != NULL', use it to tell whether it was unicode + * + * CAVEAT: will report valid UTF mappings using only 1 byte as 8-bit ones. + */ +static long int ctoi(unsigned char *s, int *is_unicode) +{ + int i; + size_t ls; + + ls = strlen(s); + if (is_unicode) + *is_unicode = 0; + + /* hex-specified UCS2 */ + if ((strncmp(s, "U+", 2) == 0) && + (strspn(s + 2, "0123456789abcdefABCDEF") == ls - 2)) { + sscanf(s + 2, "%x", &i); + if (is_unicode) + *is_unicode = 1; + } + + /* hex-specified byte */ + else if ((ls <= 4) && (strncmp(s, "0x", 2) == 0) && + (strspn(s + 2, "0123456789abcdefABCDEF") == ls - 2)) + sscanf(s + 2, "%x", &i); + + /* oct-specified number (byte) */ + else if ((*s == '0') && (strspn(s, "01234567") == ls)) + sscanf(s, "%o", &i); + + /* dec-specified number (byte) */ + else if (strspn(s, "0123456789") == ls) + sscanf(s, "%d", &i); + + /* single-byte quoted char */ + else if ((strlen(s) == 3) && (s[0] == '\'') && (s[2] == '\'')) + i = s[1]; + + /* multi-byte UTF8 quoted char */ + else if ((s[0] == '\'') && (s[ls - 1] == '\'')) { + s[ls - 1] = 0; /* ensure we'll not "parse UTF too far" */ + i = utf8_to_ucs2(s + 1); + if (is_unicode) + *is_unicode = 1; + } else + return (-1); + + return (i); +} + + +static unicode utf8_to_ucs2(char *buf) +{ + int utf_count = 0; + long utf_char = 0; + unicode tc = 0; + unsigned char c; + + do { + c = *buf; + buf++; + + /* if byte should be part of multi-byte sequence */ + if (c & 0x80) { + /* if we have already started to parse a UTF8 sequence */ + if (utf_count > 0 && (c & 0xc0) == 0x80) { + utf_char = (utf_char << 6) | (c & 0x3f); + utf_count--; + if (utf_count == 0) + tc = utf_char; + else + continue; + } else { /* Possibly 1st char of a UTF8 sequence */ + + if ((c & 0xe0) == 0xc0) { + utf_count = 1; + utf_char = (c & 0x1f); + } else if ((c & 0xf0) == 0xe0) { + utf_count = 2; + utf_char = (c & 0x0f); + } else if ((c & 0xf8) == 0xf0) { + utf_count = 3; + utf_char = (c & 0x07); + } else if ((c & 0xfc) == 0xf8) { + utf_count = 4; + utf_char = (c & 0x03); + } else if ((c & 0xfe) == 0xfc) { + utf_count = 5; + utf_char = (c & 0x01); + } else + utf_count = 0; + continue; + } + } else { /* not part of multi-byte sequence - treat as ASCII + * this makes incomplete sequences to be ignored + */ + tc = c; + utf_count = 0; + } + } + while (utf_count); + + return tc; +} diff --git a/busybox/console-tools/loadfont.c b/busybox/console-tools/loadfont.c new file mode 100644 index 000000000..d66500195 --- /dev/null +++ b/busybox/console-tools/loadfont.c @@ -0,0 +1,209 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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) + show_usage(); + + fd = open(CURRENT_VC, O_RDWR); + if (fd < 0) + perror_msg_and_die("Error opening " CURRENT_VC); + 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) + 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 */ + perror_msg("PIO_FONTX ioctl error (trying PIO_FONT)"); + } +#endif + if (ioctl(fd, PIO_FONT, buf)) + 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) { + error_msg("It seems this kernel is older than 1.1.92"); + error_msg_and_die("No Unicode mapping table loaded."); + } else +#endif + 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 + 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)) + 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)) + 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) + error_msg_and_die("Unsupported psf file mode"); + fontsize = ((psfhdr.mode & PSF_MODE512) ? 512 : 256); +#if !defined( PIO_FONTX ) || defined( __sparc__ ) + if (fontsize != 256) + 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)) + 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) + 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 index 000000000..4f217d630 --- /dev/null +++ b/busybox/console-tools/loadkmap.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini loadkmap implementation for busybox + * + * Copyright (C) 1998 Enrique Zanardi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define BINARY_KEYMAP_MAGIC "bkeymap" + +/* From */ +struct kbentry { + unsigned char kb_table; + unsigned char kb_index; + unsigned short kb_value; +}; +static const int KDSKBENT = 0x4B47; /* sets one entry in translation table */ + +/* From */ +static const int NR_KEYS = 128; +static const int MAX_NR_KEYMAPS = 256; + +int loadkmap_main(int argc, char **argv) +{ + struct kbentry ke; + u_short *ibuff; + int i, j, fd, readsz, pos, ibuffsz = NR_KEYS * sizeof(u_short); + char flags[MAX_NR_KEYMAPS], buff[7]; + + if (argc != 1) + show_usage(); + + fd = open(CURRENT_VC, O_RDWR); + if (fd < 0) + perror_msg_and_die("Error opening " CURRENT_VC); + + read(0, buff, 7); + if (0 != strncmp(buff, BINARY_KEYMAP_MAGIC, 7)) + error_msg_and_die("This is not a valid binary keymap."); + + if (MAX_NR_KEYMAPS != read(0, flags, MAX_NR_KEYMAPS)) + perror_msg_and_die("Error reading keymap flags"); + + ibuff = (u_short *) xmalloc(ibuffsz); + + for (i = 0; i < MAX_NR_KEYMAPS; i++) { + if (flags[i] == 1) { + pos = 0; + while (pos < ibuffsz) { + if ((readsz = read(0, (char *) ibuff + pos, ibuffsz - pos)) < 0) + perror_msg_and_die("Error reading keymap"); + pos += readsz; + } + 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/reset.c b/busybox/console-tools/reset.c new file mode 100644 index 000000000..755c4c335 --- /dev/null +++ b/busybox/console-tools/reset.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini reset implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * and Kent Robotti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "busybox.h" + +extern int reset_main(int argc, char **argv) +{ + printf("\033c"); + return EXIT_SUCCESS; +} + diff --git a/busybox/console-tools/setkeycodes.c b/busybox/console-tools/setkeycodes.c new file mode 100644 index 000000000..c3c7e09aa --- /dev/null +++ b/busybox/console-tools/setkeycodes.c @@ -0,0 +1,72 @@ +/* vi: set sw=4 ts=4: */ +/* + * setkeycodes + * + * Copyright (C) 1994-1998 Andries E. Brouwer + * + * Adjusted for BusyBox by 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 +#include +#include +#include +#include "busybox.h" + + +/* From */ +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) { + show_usage(); + } + + fd = get_console_fd("/dev/console"); + + while (argc > 2) { + a.keycode = atoi(argv[2]); + a.scancode = sc = strtol(argv[1], &ep, 16); + if (*ep) { + 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) { + error_msg_and_die("SCANCODE or KEYCODE outside bounds"); + } + if (ioctl(fd,KDSETKEYCODE,&a)) { + perror("KDSETKEYCODE"); + 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/basename.c b/busybox/coreutils/basename.c new file mode 100644 index 000000000..b83f387c2 --- /dev/null +++ b/busybox/coreutils/basename.c @@ -0,0 +1,52 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini basename implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ + +#include +#include "busybox.h" +#include + +extern int basename_main(int argc, char **argv) +{ + int m, n; + char *s; + + if ((argc < 2) || (**(argv + 1) == '-')) { + show_usage(); + } + + argv++; + + s = get_last_path_component(*argv); + + if (argc>2) { + argv++; + n = strlen(*argv); + m = strlen(s); + if (m>=n && strncmp(s+m-n, *argv, n)==0) + s[m-n] = '\0'; + } + puts(s); + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/cat.c b/busybox/coreutils/cat.c new file mode 100644 index 000000000..aa8528d6a --- /dev/null +++ b/busybox/coreutils/cat.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini Cat implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include "busybox.h" + +extern int cat_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + + if (argc == 1) { + print_file(stdin); + return status; + } + + while (--argc > 0) { + if(!(strcmp(*++argv, "-"))) { + print_file(stdin); + } else if (print_file_by_name(*argv) == FALSE) { + status = EXIT_FAILURE; + } + } + return status; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/chgrp.c b/busybox/coreutils/chgrp.c new file mode 100644 index 000000000..fbc1036a8 --- /dev/null +++ b/busybox/coreutils/chgrp.c @@ -0,0 +1,91 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown/chmod/chgrp implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include "busybox.h" + +/* Don't use lchown for libc5 or glibc older then 2.1.x */ +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) +#define lchown chown +#endif + + +static long gid; + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +{ + if (lchown(fileName, statbuf->st_uid, (gid == -1) ? statbuf->st_gid : gid) == 0) { + return (TRUE); + } + perror(fileName); + return (FALSE); +} + +int chgrp_main(int argc, char **argv) +{ + int opt; + int recursiveFlag = FALSE; + char *p=NULL; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "R")) > 0) { + switch (opt) { + case 'R': + recursiveFlag = TRUE; + break; + default: + show_usage(); + } + } + + if (argc > optind && argc > 2 && argv[optind]) { + /* Find the selected group */ + gid = strtoul(argv[optind], &p, 10); /* maybe it's already numeric */ + if (argv[optind] == p) + gid = my_getgrnam(argv[optind]); + } else { + error_msg_and_die(too_few_args); + } + + /* Ok, ready to do the deed now */ + while (++optind < argc) { + if (recursive_action (argv[optind], recursiveFlag, FALSE, FALSE, + fileAction, fileAction, NULL) == FALSE) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; + +} + +/* +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 index 000000000..9139b3f4d --- /dev/null +++ b/busybox/coreutils/chmod.c @@ -0,0 +1,85 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown/chmod/chgrp implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include "busybox.h" + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +{ + if (!parse_mode((char *)junk, &(statbuf->st_mode))) + error_msg_and_die("internal error"); + if (chmod(fileName, statbuf->st_mode) == 0) + return (TRUE); + perror(fileName); + return (FALSE); +} + +int chmod_main(int argc, char **argv) +{ + int i; + int opt; + int recursiveFlag = FALSE; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "R")) > 0) { + switch (opt) { + case 'R': + recursiveFlag = TRUE; + break; + default: + show_usage(); + } + } + + if (argc > optind && argc > 2 && argv[optind]) { + /* Parse the specified mode */ + mode_t mode; + if (parse_mode(argv[optind], &mode) == FALSE) { + error_msg_and_die( "unknown mode: %s", argv[optind]); + } + } else { + error_msg_and_die(too_few_args); + } + + /* Ok, ready to do the deed now */ + for (i = optind + 1; i < argc; i++) { + if (recursive_action (argv[i], recursiveFlag, FALSE, FALSE, fileAction, + fileAction, argv[optind]) == FALSE) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + +/* +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 index 000000000..d1e52deda --- /dev/null +++ b/busybox/coreutils/chown.c @@ -0,0 +1,113 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown/chmod/chgrp implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include "busybox.h" + +/* Don't use lchown for libc5 or 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) { + return (TRUE); + } + perror(fileName); + return (FALSE); +} + +int chown_main(int argc, char **argv) +{ + int opt; + int recursiveFlag = FALSE, + noderefFlag = FALSE; + char *groupName=NULL; + char *p=NULL; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "Rh")) > 0) { + switch (opt) { + case 'R': + recursiveFlag = TRUE; + break; + case 'h': + noderefFlag = TRUE; + break; + default: + show_usage(); + } + } + + if (noderefFlag) chown_func = lchown; + + if (argc > optind && argc > 2 && argv[optind]) { + /* First, check if there is a group name here */ + groupName = strchr(argv[optind], '.'); + if (groupName == NULL) + groupName = strchr(argv[optind], ':'); + if (groupName) { + *groupName++ = '\0'; + gid = strtoul(groupName, &p, 10); + if (groupName == p) + gid = my_getgrnam(groupName); + } else { + gid = -1; + } + /* Now check for the username */ + uid = strtoul(argv[optind], &p, 10); /* Is is numeric? */ + if (argv[optind] == p) { + uid = my_getpwnam(argv[optind]); + } + } else { + error_msg_and_die(too_few_args); + } + + /* Ok, ready to do the deed now */ + while (++optind < argc) { + if (recursive_action (argv[optind], recursiveFlag, FALSE, FALSE, + fileAction, fileAction, NULL) == FALSE) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; + +} + +/* +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 index 000000000..0440e46b9 --- /dev/null +++ b/busybox/coreutils/chroot.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chroot implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include "busybox.h" + +int chroot_main(int argc, char **argv) +{ + char *prog; + + if ((argc < 2) || (**(argv + 1) == '-')) { + show_usage(); + } + argc--; + argv++; + + if (chroot(*argv) || (chdir("/"))) { + perror_msg_and_die("cannot change root directory to %s", *argv); + } + + argc--; + argv++; + if (argc >= 1) { + prog = *argv; + execvp(*argv, argv); + } else { +#if defined(BB_SH) && defined BB_FEATURE_SH_STANDALONE_SHELL + char shell[] = "/bin/sh"; + char *shell_argv[2] = { shell, NULL }; + applet_name = shell; + shell_main(1, shell_argv); + return EXIT_SUCCESS; +#else + prog = getenv("SHELL"); + if (!prog) + prog = "/bin/sh"; + execlp(prog, prog, NULL); +#endif + } + perror_msg_and_die("cannot execute %s", prog); + +} + + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/cmp.c b/busybox/coreutils/cmp.c new file mode 100644 index 000000000..6d579461d --- /dev/null +++ b/busybox/coreutils/cmp.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cmp implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include "busybox.h" + +int cmp_main(int argc, char **argv) +{ + FILE *fp1 = NULL, *fp2 = stdin; + char *filename1, *filename2 = "-"; + int c, c1, c2, char_pos = 1, line_pos = 1, silent = FALSE; + + while ((c = getopt(argc, argv, "s")) != EOF) { + switch (c) { + case 's': + silent = TRUE; + break; + default: + show_usage(); + } + } + + filename1 = argv[optind]; + switch (argc - optind) { + case 2: + fp2 = xfopen(filename2 = argv[optind + 1], "r"); + case 1: + fp1 = xfopen(filename1, "r"); + break; + default: + show_usage(); + } + + do { + c1 = fgetc(fp1); + c2 = fgetc(fp2); + if (c1 != c2) { + if (silent) + return EXIT_FAILURE; + if (c1 == EOF) + printf("EOF on %s\n", filename1); + else if (c2 == EOF) + printf("EOF on %s\n", filename2); + else + printf("%s %s differ: char %d, line %d\n", filename1, filename2, + char_pos, line_pos); + return EXIT_FAILURE; + } + char_pos++; + if (c1 == '\n') + line_pos++; + } while (c1 != EOF); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/cp.c b/busybox/coreutils/cp.c new file mode 100644 index 000000000..82d43adff --- /dev/null +++ b/busybox/coreutils/cp.c @@ -0,0 +1,114 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cp implementation for busybox + * + * + * Copyright (C) 2000 by 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 +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +extern int cp_main(int argc, char **argv) +{ + int status = 0; + int opt; + int flags = 0; + int i; + + while ((opt = getopt(argc, argv, "adfipR")) != -1) + switch (opt) { + case 'a': + flags |= FILEUTILS_PRESERVE_STATUS | FILEUTILS_RECUR; + /* fallthrough */ + case 'd': + flags |= FILEUTILS_PRESERVE_SYMLINKS; + break; + case 'f': + flags |= FILEUTILS_FORCE; + break; + case 'i': + flags |= FILEUTILS_INTERACTIVE; + break; + case 'p': + flags |= FILEUTILS_PRESERVE_STATUS; + break; + case 'R': + flags |= FILEUTILS_RECUR; + break; + default: + show_usage(); + } + + if (optind + 2 > argc) + show_usage(); + + /* If there are only two arguments and... */ + if (optind + 2 == argc) { + struct stat source_stat; + struct stat dest_stat; + int source_exists = 1; + int dest_exists = 1; + + if (((flags & FILEUTILS_PRESERVE_SYMLINKS) && + lstat(argv[optind], &source_stat) < 0) || + (!(flags & FILEUTILS_PRESERVE_SYMLINKS) && + stat(argv[optind], &source_stat))) { + if (errno != ENOENT) + perror_msg_and_die("unable to stat `%s'", argv[optind]); + source_exists = 0; + } + + if (stat(argv[optind + 1], &dest_stat) < 0) { + if (errno != ENOENT) + perror_msg_and_die("unable to stat `%s'", argv[optind + 1]); + dest_exists = 0; + } + + /* ...if neither is a directory or... */ + if (((!source_exists || !S_ISDIR(source_stat.st_mode)) && + (!dest_exists || !S_ISDIR(dest_stat.st_mode))) || + /* ...recursing, the first is a directory, and the + * second doesn't exist, then... */ + ((flags & FILEUTILS_RECUR) && S_ISDIR(source_stat.st_mode) && + !dest_exists)) { + /* ...do a simple copy. */ + if (copy_file(argv[optind], argv[optind + 1], flags) < 0) + status = 1; + return status; + } + } + + for (i = optind; i < argc - 1; i++) { + char *dest = concat_path_file(argv[argc - 1], + get_last_path_component(argv[i])); + if (copy_file(argv[i], dest, flags) < 0) + status = 1; + free(dest); + } + + return status; +} diff --git a/busybox/coreutils/cut.c b/busybox/coreutils/cut.c new file mode 100644 index 000000000..3ed264870 --- /dev/null +++ b/busybox/coreutils/cut.c @@ -0,0 +1,356 @@ +/* + * cut.c - minimalist version of cut + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include /* getopt */ +#include +#include +#include "busybox.h" + + +/* globals from other files */ +extern int optind; +extern char *optarg; + + +/* option vars */ +static char part = 0; /* (b)yte, (c)har, (f)ields */ +static unsigned int supress_non_delimited_lines = 0; +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 seperated 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 seperated 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(<ok, "-"); + 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) + 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(<ok, "-"); + if (ntok == NULL) { + e = NON_RANGE; + } else if (strlen(ntok) == 0) { + e = EOL; + } else { + e = strtoul(ntok, &junk, 10); + if(*junk != '\0' || e < 0) + 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) + 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) + 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 = get_line_from_file(file)) != NULL) { + chomp(line); + + /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ + if (part == 'c' || part == 'b') + cut_line_by_chars(line); + + /* cut based on fields */ + else if (part == 'f') { + 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) +{ + int opt; + + while ((opt = getopt(argc, argv, "b:c:d:f:ns")) > 0) { + switch (opt) { + case 'b': + case 'c': + case 'f': + /* make sure they didn't ask for two types of lists */ + if (part != 0) { + error_msg_and_die("only one type of list may be specified"); + } + part = (char)opt; + parse_lists(optarg); + break; + case 'd': + if (strlen(optarg) > 1) { + error_msg_and_die("the delimiter must be a single character"); + } + delim = optarg[0]; + break; + case 'n': + /* no-op */ + break; + case 's': + supress_non_delimited_lines++; + break; + } + } + + if (part == 0) { + error_msg_and_die("you must specify a list of bytes, characters, or fields"); + } + + /* non-field (char or byte) cutting has some special handling */ + if (part != 'f') { + if (supress_non_delimited_lines) { + error_msg_and_die("suppressing non-delimited lines makes sense" + " only when operating on fields"); + } + if (delim != '\t' && part != 'f') { + 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 = 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 index 000000000..6db3e2838 --- /dev/null +++ b/busybox/coreutils/date.c @@ -0,0 +1,247 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini date implementation for busybox + * + * by Matthew Grant + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#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 suprising some people */ + +static struct tm *date_conv_time(struct tm *tm_time, const char *t_string) +{ + int nr; + + 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) { + error_msg_and_die(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) { + + t.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + + } 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) { + + 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:%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 { + error_msg_and_die(invalid_date, t_string); + } + *tm_time = t; + return (tm_time); +} + + +int date_main(int argc, char **argv) +{ + char *date_str = NULL; + char *date_fmt = NULL; + char *t_buff; + int c; + int set_time = 0; + int rfc822 = 0; + int utc = 0; + int use_arg = 0; + time_t tm; + struct tm tm_time; + + /* Interpret command line args */ + while ((c = getopt(argc, argv, "Rs:ud:")) != EOF) { + switch (c) { + case 'R': + rfc822 = 1; + break; + case 's': + set_time = 1; + if ((date_str != NULL) || ((date_str = optarg) == NULL)) { + show_usage(); + } + break; + case 'u': + utc = 1; + if (putenv("TZ=UTC0") != 0) + error_msg_and_die(memory_exhausted); + break; + case 'd': + use_arg = 1; + if ((date_str != NULL) || ((date_str = optarg) == NULL)) + show_usage(); + break; + default: + show_usage(); + } + } + + 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]; + } +#if 0 + else { + error_msg("date_str='%s' date_fmt='%s'\n", date_str, date_fmt); + show_usage(); + } +#endif + + /* Now we have parsed all the information except the date format + which depends on whether the clock is being set or read */ + + 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 (date_str != NULL) { + + 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 = mktime(&tm_time); + if (tm < 0) + error_msg_and_die(invalid_date, date_str); + if ( utc ) { + if (putenv("TZ=UTC0") != 0) + error_msg_and_die(memory_exhausted); + } + + /* if setting time, set it */ + if (set_time) { + if (stime(&tm) < 0) { + perror_msg("cannot set date"); + } + } + } + + /* Display output */ + + /* Deal with format string */ + if (date_fmt == NULL) { + date_fmt = (rfc822 + ? (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"); + + } 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 index 000000000..d46db82a0 --- /dev/null +++ b/busybox/coreutils/dd.c @@ -0,0 +1,154 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dd implementation for busybox + * + * + * Copyright (C) 2000 by 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 +#include +#include +#include +#include +#include +#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) +{ + int i, ifd, ofd, oflag, sync_flag = FALSE, trunc = TRUE; + size_t in_full = 0, in_part = 0, out_full = 0, out_part = 0; + size_t bs = 512, count = -1; + ssize_t n; + off_t seek = 0, skip = 0; + char *infile = NULL, *outfile = NULL, *buf; + + for (i = 1; i < argc; i++) { + if (strncmp("bs=", argv[i], 3) == 0) + bs = parse_number(argv[i]+3, dd_suffixes); + else if (strncmp("count=", argv[i], 6) == 0) + count = parse_number(argv[i]+6, dd_suffixes); + else if (strncmp("seek=", argv[i], 5) == 0) + seek = parse_number(argv[i]+5, dd_suffixes); + else if (strncmp("skip=", argv[i], 5) == 0) + skip = parse_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 = FALSE; + buf += 7; + } else if (strncmp("sync", buf, 4) == 0) { + sync_flag = TRUE; + buf += 4; + } else { + error_msg_and_die("invalid conversion `%s'", argv[i]+5); + } + if (buf[0] == '\0') + break; + if (buf[0] == ',') + buf++; + } + } else + show_usage(); + } + + buf = xmalloc(bs); + + if (infile != NULL) { + if ((ifd = open(infile, O_RDONLY)) < 0) + perror_msg_and_die("%s", infile); + } else { + ifd = STDIN_FILENO; + infile = "standard input"; + } + + if (outfile != NULL) { + oflag = O_WRONLY | O_CREAT; + + if (!seek && trunc) + oflag |= O_TRUNC; + + if ((ofd = open(outfile, oflag, 0666)) < 0) + perror_msg_and_die("%s", outfile); + + if (seek && trunc) { + if (ftruncate(ofd, seek * bs) < 0) + perror_msg_and_die("%s", outfile); + } + } else { + ofd = STDOUT_FILENO; + outfile = "standard output"; + } + + if (skip) { + if (lseek(ifd, skip * bs, SEEK_CUR) < 0) + perror_msg_and_die("%s", infile); + } + + if (seek) { + if (lseek(ofd, seek * bs, SEEK_CUR) < 0) + perror_msg_and_die("%s", outfile); + } + + while (in_full + in_part != count) { + n = safe_read(ifd, buf, bs); + if (n < 0) + 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 = full_write(ofd, buf, n); + if (n < 0) + perror_msg_and_die("%s", outfile); + if (n == bs) + out_full++; + else + out_part++; + } + + fprintf(stderr, "%ld+%ld records in\n", (long)in_full, (long)in_part); + fprintf(stderr, "%ld+%ld records out\n", (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 index 000000000..8cb13fa6d --- /dev/null +++ b/busybox/coreutils/df.c @@ -0,0 +1,158 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini df implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * based on original code by (I think) Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include "busybox.h" + +extern const char mtab_file[]; /* Defined in utility.c */ +#ifdef BB_FEATURE_HUMAN_READABLE +static unsigned long df_disp_hr = KILOBYTE; +#endif + +static int do_df(char *device, const char *mount_point) +{ + struct statfs s; + long blocks_used; + long blocks_percent_used; + + if (statfs(mount_point, &s) != 0) { + perror_msg("%s", mount_point); + return FALSE; + } + + if (s.f_blocks > 0) { + blocks_used = s.f_blocks - s.f_bfree; + if(blocks_used == 0) + blocks_percent_used = 0; + else { + blocks_percent_used = (long) + (blocks_used * 100.0 / (blocks_used + s.f_bavail) + 0.5); + } + if (strcmp(device, "/dev/root") == 0) { + /* Adjusts device to be the real root device, + * or leaves device alone if it can't find it */ + device = find_real_root_device_name(device); + if(device==NULL) + return FALSE; + } +#ifdef BB_FEATURE_HUMAN_READABLE + printf("%-20s %9s ", device, + make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr)); + + printf("%9s ", + make_human_readable_str( (s.f_blocks - s.f_bfree), s.f_bsize, df_disp_hr)); + + printf("%9s %3ld%% %s\n", + make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), + blocks_percent_used, mount_point); +#else + printf("%-20s %9ld %9ld %9ld %3ld%% %s\n", + device, + (long) (s.f_blocks * (s.f_bsize / (double)KILOBYTE)), + (long) ((s.f_blocks - s.f_bfree)*(s.f_bsize/(double)KILOBYTE)), + (long) (s.f_bavail * (s.f_bsize / (double)KILOBYTE)), + blocks_percent_used, mount_point); +#endif + } + + return TRUE; +} + +extern int df_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int opt = 0; + int i = 0; + char disp_units_hdr[80] = "1k-blocks"; /* default display is kilobytes */ + + while ((opt = getopt(argc, argv, "k" +#ifdef BB_FEATURE_HUMAN_READABLE + "hm" +#endif +)) > 0) + { + switch (opt) { +#ifdef BB_FEATURE_HUMAN_READABLE + case 'h': + df_disp_hr = 0; + strcpy(disp_units_hdr, " Size"); + break; + case 'm': + df_disp_hr = MEGABYTE; + strcpy(disp_units_hdr, "1M-blocks"); + break; +#endif + case 'k': + /* default display is kilobytes */ + break; + default: + show_usage(); + } + } + + printf("%-20s %-14s %s %s %s %s\n", "Filesystem", disp_units_hdr, + "Used", "Available", "Use%", "Mounted on"); + + if(optind < argc) { + struct mntent *mount_entry; + for(i = optind; i < argc; i++) + { + if ((mount_entry = find_mount_point(argv[i], mtab_file)) == 0) { + error_msg("%s: can't find mount point.", argv[i]); + status = EXIT_FAILURE; + } else if (!do_df(mount_entry->mnt_fsname, mount_entry->mnt_dir)) + status = EXIT_FAILURE; + } + } else { + FILE *mount_table; + struct mntent *mount_entry; + + mount_table = setmntent(mtab_file, "r"); + if (mount_table == 0) { + perror_msg("%s", mtab_file); + return EXIT_FAILURE; + } + + while ((mount_entry = getmntent(mount_table))) { + if (!do_df(mount_entry->mnt_fsname, mount_entry->mnt_dir)) + status = EXIT_FAILURE; + } + endmntent(mount_table); + } + + return 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 index 000000000..b534e6950 --- /dev/null +++ b/busybox/coreutils/dirname.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dirname implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ + +#include +#include +#include +#include "busybox.h" + +extern int dirname_main(int argc, char **argv) +{ + if ((argc < 2) || (**(argv + 1) == '-')) + show_usage(); + argv++; + + puts (dirname (argv[0])); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/dos2unix.c b/busybox/coreutils/dos2unix.c new file mode 100644 index 000000000..02b70d915 --- /dev/null +++ b/busybox/coreutils/dos2unix.c @@ -0,0 +1,188 @@ +/* + * 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 . + * 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 +#include +#include +#include +#include +#include "busybox.h" + +/* Teach libc5 what a uint64_t is */ +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) +typedef unsigned long int uint64_t; +#endif + +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 uint64_t value=0; + FILE *in = stdin, *out = stdout; + + if (fn != NULL) { + if ((in = wfopen(fn, "rw")) == NULL) { + return -1; + } + strcpy(tempFn, fn); + c = strlen(tempFn); + tempFn[c] = '.'; + while(1) { + if (c >=BUFSIZ) + 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 += ((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) { + perror_msg(NULL); + 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)) { + perror_msg(NULL); + 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) { + perror_msg(NULL); + remove(tempFn); + return -2; + } + + /* Assume they are both on the same filesystem */ + if (rename(tempFn, fn) < 0) { + 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: + 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 index 000000000..fb649aee5 --- /dev/null +++ b/busybox/coreutils/du.c @@ -0,0 +1,257 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini du implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +#ifdef BB_FEATURE_HUMAN_READABLE +static unsigned long disp_hr = KILOBYTE; +#endif + +typedef void (Display) (long, char *); + +static int du_depth = 0; +static int count_hardlinks = 0; + +static Display *print; + +static void print_normal(long size, char *filename) +{ +#ifdef BB_FEATURE_HUMAN_READABLE + printf("%s\t%s\n", make_human_readable_str(size<<10, 1, disp_hr), filename); +#else + printf("%ld\t%s\n", size, filename); +#endif +} + +static void print_summary(long size, char *filename) +{ + if (du_depth == 1) { + print_normal(size, filename); + } +} + +#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. + */ +static 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 */ +static 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; +} + +/* Clear statbuf hash table */ +static 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; + } + } +} + +/* tiny recursive du */ +static long du(char *filename) +{ + struct stat statbuf; + long sum; + + if ((lstat(filename, &statbuf)) != 0) { + perror_msg("%s", filename); + return 0; + } + + du_depth++; + sum = (statbuf.st_blocks >> 1); + + /* Don't add in stuff pointed to by symbolic links */ + if (S_ISLNK(statbuf.st_mode)) { + sum = 0L; + if (du_depth == 1) { + } + } + if (S_ISDIR(statbuf.st_mode)) { + DIR *dir; + struct dirent *entry; + char *newfile; + + dir = opendir(filename); + if (!dir) { + du_depth--; + return 0; + } + + newfile = last_char_is(filename, '/'); + if (newfile) + *newfile = '\0'; + + while ((entry = readdir(dir))) { + char *name = entry->d_name; + + if ((strcmp(name, "..") == 0) + || (strcmp(name, ".") == 0)) { + continue; + } + newfile = concat_path_file(filename, name); + sum += du(newfile); + free(newfile); + } + closedir(dir); + print(sum, filename); + } + else if (statbuf.st_nlink > 1 && !count_hardlinks) { + /* Add files with hard links only once */ + if (is_in_ino_dev_hashtable(&statbuf, NULL)) { + sum = 0L; + if (du_depth == 1) + print(sum, filename); + } + else { + add_to_ino_dev_hashtable(&statbuf, NULL); + } + } + du_depth--; + return sum; +} + +int du_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int i; + int c; + + /* default behaviour */ + print = print_normal; + + /* parse argv[] */ + while ((c = getopt(argc, argv, "sl" +#ifdef BB_FEATURE_HUMAN_READABLE +"hm" +#endif +"k")) != EOF) { + switch (c) { + case 's': + print = print_summary; + break; + case 'l': + count_hardlinks = 1; + break; +#ifdef BB_FEATURE_HUMAN_READABLE + case 'h': disp_hr = 0; break; + case 'm': disp_hr = MEGABYTE; break; +#endif + case 'k': break; + default: + show_usage(); + } + } + + /* go through remaining args (if any) */ + if (optind >= argc) { + if (du(".") == 0) + status = EXIT_FAILURE; + } else { + long sum; + + for (i=optind; i < argc; i++) { + sum = du(argv[i]); + if(is_directory(argv[i], FALSE, NULL)==FALSE) { + print_normal(sum, argv[i]); + } + reset_ino_dev_hashtable(); + } + } + + return status; +} + +/* $Id: du.c,v 1.50 2001/06/30 17:54:20 andersen Exp $ */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/echo.c b/busybox/coreutils/echo.c new file mode 100644 index 000000000..31c031528 --- /dev/null +++ b/busybox/coreutils/echo.c @@ -0,0 +1,152 @@ +/* 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. + */ + +#include +#include +#include +#include "busybox.h" + +extern int +echo_main(int argc, char** argv) +{ + int nflag = 0; + int eflag = 0; + + /* Skip argv[0]. */ + argc--; + argv++; + + while (argc > 0 && *argv[0] == '-') + { + register char *temp; + register int ix; + + /* + * 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. + */ + temp = argv[0] + 1; + + for (ix = 0; temp[ix]; ix++) + { + if (strrchr("neE", temp[ix]) == 0) + goto just_echo; + } + + if (!*temp) + goto just_echo; + + /* + * All of the options in temp are valid options to echo. + * Handle them. + */ + while (*temp) + { + if (*temp == 'n') + nflag = 1; + else if (*temp == 'e') + eflag = 1; + else if (*temp == 'E') + eflag = 0; + else + goto just_echo; + + temp++; + } + argc--; + argv++; + } + +just_echo: + while (argc > 0) { + const char *arg = argv[0]; + register int c; + + while ((c = *arg++)) { + + /* Check for escape sequence. */ + if (c == '\\' && eflag && *arg) { + if (*arg == 'c') { + /* '\c' means cancel newline. */ + nflag = 1; + arg++; + continue; + } else { + c = process_escape_sequence(&arg); + } + } + + putchar(c); + } + argc--; + argv++; + if (argc > 0) + putchar(' '); + } + if (!nflag) + putchar('\n'); + fflush(stdout); + + return 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. + * + * 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 index 000000000..8bb690b72 --- /dev/null +++ b/busybox/coreutils/env.c @@ -0,0 +1,106 @@ +/* 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 , + */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int env_main(int argc, char** argv) +{ + char **ep, *p; + char *cleanenv[1]; + int ignore_environment = 0; + int ch; + + while ((ch = getopt(argc, argv, "+iu:")) != -1) { + switch(ch) { + case 'i': + ignore_environment = 1; + break; + case 'u': + unsetenv(optarg); + break; + default: + show_usage(); + } + } + if (optind != argc && !strcmp(argv[optind], "-")) { + ignore_environment = 1; + argv++; + } + if (ignore_environment) { + environ = cleanenv; + cleanenv[0] = NULL; + } + for (argv += optind; *argv && (p = strchr(*argv, '=')); ++argv) + if (putenv(*argv) < 0) + perror_msg_and_die("%s", *argv); + if (*argv) { + execvp(*argv, argv); + perror_msg_and_die("%s", *argv); + } + for (ep = environ; *ep; ep++) + puts(*ep); + return 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. + * + * 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 index 000000000..d6cc82e3e --- /dev/null +++ b/busybox/coreutils/expr.c @@ -0,0 +1,535 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini expr implementation for busybox + * + * based on GNU expr Mike Parker. + * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc. + * + * Busybox modifications + * Copyright (c) 2000 Edward Betts . + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the gnu + * general public license for more details. + * + * you should have received a copy of the gnu general public 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 seperate 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 +#include +#include +#include +#include +#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) { + error_msg_and_die("too few arguments"); + } + + args = argv + 1; + + v = eval (); + if (*args) + 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 = strdup (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; + case string: + return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0; + default: + abort (); + } +} + +/* Coerce V to a string value (can't fail). */ + +static void tostring (VALUE *v) +{ + char *temp; + + if (v->type == integer) { + temp = xmalloc (4 * (sizeof (int) / sizeof (char))); + sprintf (temp, "%d", v->u.i); + v->u.s = temp; + v->type = string; + } +} + +/* Coerce V to an integer value. Return 1 on success, 0 on failure. */ + +static int toarith (VALUE *v) +{ + int i; + + switch (v->type) { + case integer: + return 1; + case string: + i = 0; + /* Don't interpret the empty string as an integer. */ + if (v->u.s == 0) + return 0; + i = atoi(v->u.s); + free (v->u.s); + v->u.i = i; + v->type = integer; + return 1; + default: + abort (); + } +} + +/* 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. */ + +#define cmpf(name, rel) \ +static int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (l->type == string || r->type == string) { \ + tostring (l); \ + tostring (r); \ + return strcmp (l->u.s, r->u.s) rel 0; \ + } \ + else \ + return l->u.i rel r->u.i; \ +} + cmpf (less_than, <) + cmpf (less_equal, <=) + cmpf (equal, ==) + cmpf (not_equal, !=) + cmpf (greater_equal, >=) + cmpf (greater_than, >) + +#undef cmpf + +/* The arithmetic operator handling functions. */ + +#define arithf(name, op) \ +static \ +int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (!toarith (l) || !toarith (r)) \ + error_msg_and_die ("non-numeric argument"); \ + return l->u.i op r->u.i; \ +} + +#define arithdivf(name, op) \ +static int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (!toarith (l) || !toarith (r)) \ + error_msg_and_die ( "non-numeric argument"); \ + if (r->u.i == 0) \ + error_msg_and_die ( "division by zero"); \ + return l->u.i op r->u.i; \ +} + + arithf (plus, +) + arithf (minus, -) + arithf (multiply, *) + arithdivf (divide, /) + arithdivf (mod, %) + +#undef arithf +#undef arithdivf + +/* 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) { + 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) + error_msg_and_die ( "syntax error"); + + if (nextarg ("(")) { + args++; + v = eval (); + if (!nextarg (")")) + error_msg_and_die ( "syntax error"); + args++; + return v; + } + + if (nextarg (")")) + 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) + 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 = strncpy ((char *) xmalloc (i2->u.i + 1), + l->u.s + i1->u.i - 1, i2->u.i); + v->u.s[i2->u.i] = 0; + } + 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 (*fxn) (), val; + + l = eval5 (); + while (1) { + if (nextarg ("*")) + fxn = multiply; + else if (nextarg ("/")) + fxn = divide; + else if (nextarg ("%")) + fxn = mod; + else + return l; + args++; + r = eval5 (); + val = (*fxn) (l, r); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle +, - operators. */ + +static VALUE *eval3 (void) +{ + VALUE *l, *r; + int (*fxn) (), val; + + l = eval4 (); + while (1) { + if (nextarg ("+")) + fxn = plus; + else if (nextarg ("-")) + fxn = minus; + else + return l; + args++; + r = eval4 (); + val = (*fxn) (l, r); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle comparisons. */ + +static VALUE *eval2 (void) +{ + VALUE *l, *r; + int (*fxn) (), val; + + l = eval3 (); + while (1) { + if (nextarg ("<")) + fxn = less_than; + else if (nextarg ("<=")) + fxn = less_equal; + else if (nextarg ("=") || nextarg ("==")) + fxn = equal; + else if (nextarg ("!=")) + fxn = not_equal; + else if (nextarg (">=")) + fxn = greater_equal; + else if (nextarg (">")) + fxn = greater_than; + else + return l; + args++; + r = eval3 (); + toarith (l); + toarith (r); + val = (*fxn) (l, r); + 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/head.c b/busybox/coreutils/head.c new file mode 100644 index 000000000..0c8ef3d59 --- /dev/null +++ b/busybox/coreutils/head.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini head implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "busybox.h" + +static int head(int len, FILE *fp) +{ + int i; + char *input; + + for (i = 0; i < len; i++) { + if ((input = get_line_from_file(fp)) == NULL) + break; + fputs(input, stdout); + free(input); + } + return 0; +} + +/* BusyBoxed head(1) */ +int head_main(int argc, char **argv) +{ + FILE *fp; + int need_headers, opt, len = 10, status = EXIT_SUCCESS; + + /* parse argv[] */ + while ((opt = getopt(argc, argv, "n:")) > 0) { + switch (opt) { + case 'n': + len = atoi(optarg); + if (len >= 1) + break; + /* fallthrough */ + default: + show_usage(); + } + } + + /* get rest of argv[] or stdin if nothing's left */ + if (argv[optind] == NULL) { + head(len, stdin); + return status; + } + + need_headers = optind != (argc - 1); + while (argv[optind]) { + if (strcmp(argv[optind], "-") == 0) { + fp = stdin; + argv[optind] = "standard input"; + } else { + if ((fp = wfopen(argv[optind], "r")) == NULL) + status = EXIT_FAILURE; + } + if (fp) { + if (need_headers) { + printf("==> %s <==\n", argv[optind]); + } + head(len, fp); + if (errno) { + perror_msg("%s", argv[optind]); + status = EXIT_FAILURE; + errno = 0; + } + if (optind < argc - 1) + putchar('\n'); + if (fp != stdin) + fclose(fp); + } + optind++; + } + + return status; +} diff --git a/busybox/coreutils/hostid.c b/busybox/coreutils/hostid.c new file mode 100644 index 000000000..68a2cc659 --- /dev/null +++ b/busybox/coreutils/hostid.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini hostid implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int hostid_main(int argc, char **argv) +{ + printf("%lx\n", gethostid()); + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/id.c b/busybox/coreutils/id.c new file mode 100644 index 000000000..85b288c0c --- /dev/null +++ b/busybox/coreutils/id.c @@ -0,0 +1,97 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini id implementation for busybox + * + * Copyright (C) 2000 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include + +extern int id_main(int argc, char **argv) +{ + int no_user = 0, no_group = 0, print_real = 0; + int name_not_number = 0; + char user[9], group[9]; + long gid; + long pwnam, grnam; + int opt; + + gid = 0; + + while ((opt = getopt(argc, argv, "ugrn")) > 0) { + switch (opt) { + case 'u': + no_group++; + break; + case 'g': + no_user++; + break; + case 'r': + print_real++; + break; + case 'n': + name_not_number++; + break; + default: + show_usage(); + } + } + + if (no_user && no_group) show_usage(); + + if (argv[optind] == NULL) { + if (print_real) { + my_getpwuid(user, getuid()); + my_getgrgid(group, getgid()); + } else { + my_getpwuid(user, geteuid()); + my_getgrgid(group, getegid()); + } + } else { + strncpy(user, argv[optind], 8); + user[8] = '\0'; + gid = my_getpwnamegid(user); + my_getgrgid(group, gid); + } + + pwnam=my_getpwnam(user); + grnam=my_getgrnam(group); + + if (no_group) { + if(name_not_number && user) + puts(user); + else + printf("%ld\n", pwnam); + } else if (no_user) { + if(name_not_number && group) + puts(group); + else + printf("%ld\n", grnam); + } else { + printf("uid=%ld(%s) gid=%ld(%s)\n", pwnam, user, grnam, group); + } + return(0); +} + + +/* END CODE */ diff --git a/busybox/coreutils/length.c b/busybox/coreutils/length.c new file mode 100644 index 000000000..73becd28a --- /dev/null +++ b/busybox/coreutils/length.c @@ -0,0 +1,13 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include "busybox.h" + +extern int length_main(int argc, char **argv) +{ + if (argc != 2 || **(argv + 1) == '-') + show_usage(); + printf("%lu\n", (long)strlen(argv[1])); + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/ln.c b/busybox/coreutils/ln.c new file mode 100644 index 000000000..7412a86fd --- /dev/null +++ b/busybox/coreutils/ln.c @@ -0,0 +1,131 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ln implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include "busybox.h" + + +static const int LN_SYMLINK = 1; +static const int LN_FORCE = 2; +static const int LN_NODEREFERENCE = 4; + +/* + * linkDestName is where the link points to, + * linkSrcName is the name of the link to be created. + */ +static int fs_link(const char *link_destname, const char *link_srcname, + const int flag) +{ + int status; + int src_is_dir; + char *src_name; + + if (link_destname==NULL) + return(FALSE); + + src_name = (char *) xmalloc(strlen(link_srcname)+strlen(link_destname)+1); + + if (link_srcname==NULL) + strcpy(src_name, link_destname); + else + strcpy(src_name, link_srcname); + + if (flag&LN_NODEREFERENCE) + src_is_dir = is_directory(src_name, TRUE, NULL); + else + src_is_dir = is_directory(src_name, FALSE, NULL); + + if ((src_is_dir==TRUE)&&((flag&LN_NODEREFERENCE)==0)) { + char* srcdir_name; + + srcdir_name = xstrdup(link_destname); + strcat(src_name, "/"); + strcat(src_name, get_last_path_component(srcdir_name)); + free(srcdir_name); + } + + if (flag&LN_FORCE) + unlink(src_name); + + if (flag&LN_SYMLINK) + status = symlink(link_destname, src_name); + else + status = link(link_destname, src_name); + + if (status != 0) { + perror_msg(src_name); + return(FALSE); + } + return(TRUE); +} + +extern int ln_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int flag = 0; + int opt; + + /* Parse any options */ + while ((opt=getopt(argc, argv, "sfn")) != -1) { + switch(opt) { + case 's': + flag |= LN_SYMLINK; + break; + case 'f': + flag |= LN_FORCE; + break; + case 'n': + flag |= LN_NODEREFERENCE; + break; + default: + show_usage(); + } + } + if (optind > (argc-1)) { + show_usage(); + } + if (optind == (argc-1)) { + if (fs_link(argv[optind], + get_last_path_component(argv[optind]), flag)==FALSE) + status = EXIT_FAILURE; + } + while(optind<(argc-1)) { + if (fs_link(argv[optind], argv[argc-1], flag)==FALSE) + status = EXIT_FAILURE; + optind++; + } + exit(status); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/logname.c b/busybox/coreutils/logname.c new file mode 100644 index 000000000..0924b2471 --- /dev/null +++ b/busybox/coreutils/logname.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logname implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int logname_main(int argc, char **argv) +{ + char user[9]; + + if (argc > 1) + show_usage(); + + my_getpwuid(user, geteuid()); + if (*user) { + puts(user); + return EXIT_SUCCESS; + } + error_msg_and_die("no login name"); +} diff --git a/busybox/coreutils/ls.c b/busybox/coreutils/ls.c new file mode 100644 index 000000000..8d0282dfe --- /dev/null +++ b/busybox/coreutils/ls.c @@ -0,0 +1,937 @@ +/* vi: set sw=4 ts=4: */ +/* + * tiny-ls.c version 0.1.0: A minimalist 'ls' + * Copyright (C) 1996 Brian Candler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 " 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_WIDTH = 14, /* default if AUTOWIDTH not defined */ + COLUMN_GAP = 2, /* includes the file type char */ +}; + + +/************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#ifdef BB_FEATURE_LS_TIMESTAMPS +#include +#endif + +#ifndef MAJOR +#define MAJOR(dev) (((dev)>>8)&0xff) +#define MINOR(dev) ((dev)&0xff) +#endif + +/* what is the overall style of the listing */ +enum { +STYLE_AUTO = 0, +STYLE_LONG = 1, /* one record per line, extended info */ +STYLE_SINGLE = 2, /* one record per line */ +STYLE_COLUMNS = 3 /* fill columns */ +}; + +/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ +/* what file information will be listed */ +#define LIST_INO (1<<0) +#define LIST_BLOCKS (1<<1) +#define LIST_MODEBITS (1<<2) +#define LIST_NLINKS (1<<3) +#define LIST_ID_NAME (1<<4) +#define LIST_ID_NUMERIC (1<<5) +#define LIST_SIZE (1<<6) +#define LIST_DEV (1<<7) +#define LIST_DATE_TIME (1<<8) +#define LIST_FULLTIME (1<<9) +#define LIST_FILENAME (1<<10) +#define LIST_SYMLINK (1<<11) +#define LIST_FILETYPE (1<<12) +#define LIST_EXEC (1<<13) + +/* what files will be displayed */ +#define DISP_NORMAL (0) /* show normal filenames */ +#define DISP_DIRNAME (1<<0) /* 2 or more items? label directories */ +#define DISP_HIDDEN (1<<1) /* show filenames starting with . */ +#define DISP_DOT (1<<2) /* show . and .. */ +#define DISP_NOLIST (1<<3) /* show directory as itself, not contents */ +#define DISP_RECURSIVE (1<<4) /* show directory and everything below it */ +#define DISP_ROWS (1<<5) /* print across rows */ + +#ifdef BB_FEATURE_LS_SORTFILES +/* how will the files be sorted */ +static const int SORT_FORWARD = 0; /* sort in reverse order */ +static const int SORT_REVERSE = 1; /* sort in reverse order */ +static const int SORT_NAME = 2; /* sort by file name */ +static const int SORT_SIZE = 3; /* sort by file size */ +static const int SORT_ATIME = 4; /* sort by last access time */ +static const int SORT_CTIME = 5; /* sort by last change time */ +static const int SORT_MTIME = 6; /* sort by last modification time */ +static const int SORT_VERSION = 7; /* sort by version */ +static const int SORT_EXT = 8; /* sort by file name extension */ +static const int SORT_DIR = 9; /* sort by file or directory */ +#endif + +#ifdef BB_FEATURE_LS_TIMESTAMPS +/* which of the three times will be used */ +static const int TIME_MOD = 0; +static const int TIME_CHANGE = 1; +static const int TIME_ACCESS = 2; +#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) + +static const int SPLIT_DIR = 0; +static const int SPLIT_FILE = 1; +static const int SPLIT_SUBDIR = 2; + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) +#ifdef BB_FEATURE_LS_FILETYPES +#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [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 */ + struct dnode *next; /* point at the next node */ +}; +typedef struct dnode dnode_t; + +static struct dnode **list_dir(char *); +static struct dnode **dnalloc(int); +static int list_single(struct dnode *); + +static unsigned int disp_opts; +static unsigned int style_fmt; +static unsigned int list_fmt; +#ifdef BB_FEATURE_LS_SORTFILES +static unsigned int sort_opts; +static unsigned int sort_order; +#endif +#ifdef BB_FEATURE_LS_TIMESTAMPS +static unsigned int time_fmt; +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS +static unsigned int follow_links=FALSE; +#endif + +static unsigned short column = 0; +#ifdef BB_FEATURE_AUTOWIDTH +static unsigned short terminal_width = TERMINAL_WIDTH; +static unsigned short column_width = COLUMN_WIDTH; +static unsigned short tabstops = COLUMN_GAP; +#else +static unsigned short column_width = COLUMN_WIDTH; +#endif + +static int status = EXIT_SUCCESS; + +#ifdef BB_FEATURE_HUMAN_READABLE +static unsigned long ls_disp_hr = 0; +#endif + +static int my_stat(struct dnode *cur) +{ +#ifdef BB_FEATURE_LS_FOLLOWLINKS + if (follow_links == TRUE) { + if (stat(cur->fullname, &cur->dstat)) { + perror_msg("%s", cur->fullname); + status = EXIT_FAILURE; + free(cur->fullname); + free(cur); + return -1; + } + } else +#endif + if (lstat(cur->fullname, &cur->dstat)) { + perror_msg("%s", cur->fullname); + status = EXIT_FAILURE; + free(cur->fullname); + free(cur); + return -1; + } + return 0; +} + +static void newline(void) +{ + if (column > 0) { + putchar('\n'); + column = 0; + } +} + +/*----------------------------------------------------------------------*/ +#ifdef BB_FEATURE_LS_FILETYPES +static char append_char(mode_t mode) +{ + if ( !(list_fmt & LIST_FILETYPE)) + return '\0'; + if ((list_fmt & LIST_EXEC) && S_ISREG(mode) + && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; + return APPCHAR(mode); +} +#endif + +/*----------------------------------------------------------------------*/ +static void nexttabstop( void ) +{ + static short nexttab= 0; + int n=0; + + if (column > 0) { + n= nexttab - column; + if (n < 1) n= 1; + while (n--) { + putchar(' '); + column++; + } + } + nexttab= column + column_width + COLUMN_GAP; +} + +/*----------------------------------------------------------------------*/ +static int is_subdir(struct dnode *dn) +{ + return (S_ISDIR(dn->dstat.st_mode) && strcmp(dn->name, ".") != 0 && + strcmp(dn->name, "..") != 0); +} + +static int countdirs(struct dnode **dn, int nfiles) +{ + int i, dirs; + + if (dn==NULL || nfiles < 1) return(0); + dirs= 0; + for (i=0; idstat.st_mode)) dirs++; + } + return(dirs); +} + +static int countsubdirs(struct dnode **dn, int nfiles) +{ + int i, subdirs; + + if (dn == NULL || nfiles < 1) return 0; + subdirs = 0; + for (i = 0; i < nfiles; i++) + if (is_subdir(dn[i])) + subdirs++; + return subdirs; +} + +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 BB_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp) +{ + struct dnode *cur, *next; + + if(dnp == NULL) return; + + cur=dnp[0]; + while (cur != NULL) { + if (cur->fullname != 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; idstat.st_mode)) { + dnp[d++]= dn[i]; + } /* else skip the file */ + } else if (which == SPLIT_SUBDIR) { + if (is_subdir(dn[i])) { + dnp[d++]= dn[i]; + } /* else skip the file or dir */ + } else { + if (!(S_ISDIR(dn[i]->dstat.st_mode))) { + dnp[d++]= dn[i]; + } /* else skip the dir */ + } + } + return(dnp); +} + +/*----------------------------------------------------------------------*/ +#ifdef BB_FEATURE_LS_SORTFILES +static int sortcmp(struct dnode *d1, struct dnode *d2) +{ + int cmp, dif; + + cmp= 0; + if (sort_opts == SORT_SIZE) { + dif= (int)(d1->dstat.st_size - d2->dstat.st_size); + } else if (sort_opts == SORT_ATIME) { + dif= (int)(d1->dstat.st_atime - d2->dstat.st_atime); + } else if (sort_opts == SORT_CTIME) { + dif= (int)(d1->dstat.st_ctime - d2->dstat.st_ctime); + } else if (sort_opts == SORT_MTIME) { + dif= (int)(d1->dstat.st_mtime - d2->dstat.st_mtime); + } else if (sort_opts == SORT_DIR) { + dif= S_ISDIR(d1->dstat.st_mode) - S_ISDIR(d2->dstat.st_mode); + /* } else if (sort_opts == SORT_VERSION) { */ + /* } else if (sort_opts == SORT_EXT) { */ + } else { /* assume SORT_NAME */ + dif= 0; + } + + if (dif > 0) cmp= -1; + if (dif < 0) cmp= 1; + if (dif == 0) { + /* sort by name- may be a tie_breaker for time or size cmp */ + dif= strcmp(d1->name, d2->name); + if (dif > 0) cmp= 1; + if (dif < 0) cmp= -1; + } + + if (sort_order == SORT_REVERSE) { + cmp= -1 * cmp; + } + return(cmp); +} + +/*----------------------------------------------------------------------*/ +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=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; +#ifdef BB_FEATURE_AUTOWIDTH + int len; +#endif + + if(dn==NULL || nfiles < 1) return; + +#ifdef BB_FEATURE_AUTOWIDTH + /* find the longest file name- use that as the column width */ + column_width= 0; + for (i=0; iname) + + ((list_fmt & LIST_INO) ? 8 : 0) + + ((list_fmt & LIST_BLOCKS) ? 5 : 0) + ; + if (column_width < len) + column_width= len; + } + if (column_width >= 6) + ncols = (int)(terminal_width / (column_width + COLUMN_GAP)); + else { + ncols = 1; + column_width = COLUMN_WIDTH; + } +#else + ncols= TERMINAL_WIDTH; +#endif + switch (style_fmt) { + case STYLE_LONG: /* one record per line, extended info */ + case STYLE_SINGLE: /* one record per line */ + ncols= 1; + break; + } + + if (ncols > 1) { + nrows = nfiles / ncols; + } else { + nrows = nfiles; + ncols = 1; + } + if ((nrows * ncols) < nfiles) nrows++; /* round up fractionals */ + + if (nrows > nfiles) nrows= nfiles; + for (row=0; rowfullname); + } + subdnp= list_dir(dn[i]->fullname); + nfiles= countfiles(subdnp); + if (nfiles > 0) { + /* list all files at this level */ +#ifdef BB_FEATURE_LS_SORTFILES + shellsort(subdnp, nfiles); +#endif + showfiles(subdnp, nfiles); +#ifdef BB_FEATURE_LS_RECURSIVE + if (disp_opts & DISP_RECURSIVE) { + /* recursive- list the sub-dirs */ + dnd= splitdnarray(subdnp, nfiles, SPLIT_SUBDIR); + dndirs= countsubdirs(subdnp, nfiles); + if (dndirs > 0) { +#ifdef BB_FEATURE_LS_SORTFILES + shellsort(dnd, dndirs); +#endif + showdirs(dnd, dndirs); + 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(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) { + perror_msg("%s", path); + status = EXIT_FAILURE; + return(NULL); /* could not open the dir */ + } + while ((entry = readdir(dir)) != NULL) { + /* are we going to list the file- it may be . or .. or a hidden file */ + if ((strcmp(entry->d_name, ".")==0) && !(disp_opts & DISP_DOT)) + continue; + if ((strcmp(entry->d_name, "..")==0) && !(disp_opts & DISP_DOT)) + continue; + if ((entry->d_name[0] == '.') && !(disp_opts & DISP_HIDDEN)) + continue; + cur= (struct dnode *)xmalloc(sizeof(struct dnode)); + cur->fullname = concat_path_file(path, entry->d_name); + cur->name = cur->fullname + + (strlen(cur->fullname) - strlen(entry->d_name)); + if (my_stat(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 (nfiles < 1) return(NULL); + dnp= dnalloc(nfiles); + for (i=0, cur=dn; inext; + } + + return(dnp); +} + +/*----------------------------------------------------------------------*/ +static int list_single(struct dnode *dn) +{ + int i; + char scratch[BUFSIZ + 1]; +#ifdef BB_FEATURE_LS_TIMESTAMPS + char *filetime; + time_t ttime, age; +#endif +#if defined (BB_FEATURE_LS_FILETYPES) + struct stat info; +#endif +#ifdef BB_FEATURE_LS_FILETYPES + char append; +#endif + + if (dn==NULL || dn->fullname==NULL) return(0); + +#ifdef BB_FEATURE_LS_TIMESTAMPS + ttime= dn->dstat.st_mtime; /* the default time */ + if (time_fmt & TIME_ACCESS) ttime= dn->dstat.st_atime; + if (time_fmt & TIME_CHANGE) ttime= dn->dstat.st_ctime; + filetime= ctime(&ttime); +#endif +#ifdef BB_FEATURE_LS_FILETYPES + append = append_char(dn->dstat.st_mode); +#endif + + for (i=0; i<=31; i++) { + switch (list_fmt & (1<dstat.st_ino); + column += 8; + break; + case LIST_BLOCKS: +#ifdef BB_FEATURE_HUMAN_READABLE + fprintf(stdout, "%6s ", make_human_readable_str(dn->dstat.st_blocks>>1, + KILOBYTE, (ls_disp_hr==TRUE)? 0: KILOBYTE)); +#else +#if _FILE_OFFSET_BITS == 64 + printf("%4lld ", dn->dstat.st_blocks>>1); +#else + printf("%4ld ", dn->dstat.st_blocks>>1); +#endif +#endif + column += 5; + break; + case LIST_MODEBITS: + printf("%-10s ", (char *)mode_string(dn->dstat.st_mode)); + column += 10; + break; + case LIST_NLINKS: + printf("%4ld ", (long)dn->dstat.st_nlink); + column += 10; + break; + case LIST_ID_NAME: +#ifdef BB_FEATURE_LS_USERNAME + my_getpwuid(scratch, dn->dstat.st_uid); + printf("%-8.8s ", scratch); + my_getgrgid(scratch, dn->dstat.st_gid); + printf("%-8.8s", scratch); + column += 17; + break; +#endif + case LIST_ID_NUMERIC: + printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid); + column += 17; + break; + case LIST_SIZE: + case LIST_DEV: + if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { + printf("%4d, %3d ", (int)MAJOR(dn->dstat.st_rdev), (int)MINOR(dn->dstat.st_rdev)); + } else { +#ifdef BB_FEATURE_HUMAN_READABLE + if (ls_disp_hr==TRUE) { + fprintf(stdout, "%8s ", make_human_readable_str(dn->dstat.st_size, 1, 0)); + } else +#endif + { +#if _FILE_OFFSET_BITS == 64 + printf("%9lld ", (long long)dn->dstat.st_size); +#else + printf("%9ld ", dn->dstat.st_size); +#endif + } + } + column += 10; + break; +#ifdef BB_FEATURE_LS_TIMESTAMPS + case LIST_FULLTIME: + case LIST_DATE_TIME: + if (list_fmt & LIST_FULLTIME) { + printf("%24.24s ", filetime); + column += 25; + break; + } + 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 + case LIST_FILENAME: + printf("%s", dn->name); + column += strlen(dn->name); + break; + case LIST_SYMLINK: + if (S_ISLNK(dn->dstat.st_mode)) { + char *lpath = xreadlink(dn->fullname); + if (lpath) { + printf(" -> %s", lpath); +#ifdef BB_FEATURE_LS_FILETYPES + if (!stat(dn->fullname, &info)) { + append = append_char(info.st_mode); + } +#endif + column += strlen(lpath) + 4; + free(lpath); + } + } + break; +#ifdef BB_FEATURE_LS_FILETYPES + case LIST_FILETYPE: + if (append != '\0') { + printf("%1c", append); + column++; + } + break; +#endif + } + } + + return(0); +} + +/*----------------------------------------------------------------------*/ +extern int ls_main(int argc, char **argv) +{ + struct dnode **dnf, **dnd; + int dnfiles, dndirs; + struct dnode *dn, *cur, **dnp; + int i, nfiles; + int opt; + int oi, ac; + char **av; +#ifdef BB_FEATURE_AUTOWIDTH + struct winsize win = { 0, 0, 0, 0 }; +#endif + + disp_opts= DISP_NORMAL; + style_fmt= STYLE_AUTO; + list_fmt= LIST_SHORT; +#ifdef BB_FEATURE_LS_SORTFILES + sort_opts= SORT_NAME; + sort_order= SORT_FORWARD; +#endif +#ifdef BB_FEATURE_LS_TIMESTAMPS + time_fmt= TIME_MOD; +#endif +#ifdef BB_FEATURE_AUTOWIDTH + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (win.ws_row > 4) + column_width = win.ws_row - 2; + if (win.ws_col > 0) + terminal_width = win.ws_col - 1; +#endif + nfiles=0; + + /* process options */ + while ((opt = getopt(argc, argv, "1AaCdgilnsx" +#ifdef BB_FEATURE_AUTOWIDTH +"T:w:" +#endif +#ifdef BB_FEATURE_LS_FILETYPES +"Fp" +#endif +#ifdef BB_FEATURE_LS_RECURSIVE +"R" +#endif +#ifdef BB_FEATURE_LS_SORTFILES +"rSvX" +#endif +#ifdef BB_FEATURE_LS_TIMESTAMPS +"cetu" +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS +"L" +#endif +#ifdef BB_FEATURE_HUMAN_READABLE +"h" +#endif +"k")) > 0) { + switch (opt) { + case '1': style_fmt = STYLE_SINGLE; break; + case 'A': disp_opts |= DISP_HIDDEN; break; + case 'a': disp_opts |= DISP_HIDDEN | DISP_DOT; break; + case 'C': style_fmt = STYLE_COLUMNS; break; + case 'd': disp_opts |= DISP_NOLIST; break; + case 'g': /* ignore -- for ftp servers */ break; + case 'i': list_fmt |= LIST_INO; break; + case 'l': + style_fmt = STYLE_LONG; + list_fmt |= LIST_LONG; +#ifdef BB_FEATURE_HUMAN_READABLE + ls_disp_hr = FALSE; +#endif + break; + case 'n': list_fmt |= LIST_ID_NUMERIC; break; + case 's': list_fmt |= LIST_BLOCKS; break; + case 'x': disp_opts = DISP_ROWS; break; +#ifdef BB_FEATURE_LS_FILETYPES + case 'F': list_fmt |= LIST_FILETYPE | LIST_EXEC; break; + case 'p': list_fmt |= LIST_FILETYPE; break; +#endif +#ifdef BB_FEATURE_LS_RECURSIVE + case 'R': disp_opts |= DISP_RECURSIVE; break; +#endif +#ifdef BB_FEATURE_LS_SORTFILES + case 'r': sort_order |= SORT_REVERSE; break; + case 'S': sort_opts= SORT_SIZE; break; + case 'v': sort_opts= SORT_VERSION; break; + case 'X': sort_opts= SORT_EXT; break; +#endif +#ifdef BB_FEATURE_LS_TIMESTAMPS + case 'e': list_fmt |= LIST_FULLTIME; break; + case 'c': + time_fmt = TIME_CHANGE; +#ifdef BB_FEATURE_LS_SORTFILES + sort_opts= SORT_CTIME; +#endif + break; + case 'u': + time_fmt = TIME_ACCESS; +#ifdef BB_FEATURE_LS_SORTFILES + sort_opts= SORT_ATIME; +#endif + break; + case 't': +#ifdef BB_FEATURE_LS_SORTFILES + sort_opts= SORT_MTIME; +#endif + break; +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS + case 'L': follow_links= TRUE; break; +#endif +#ifdef BB_FEATURE_AUTOWIDTH + case 'T': tabstops= atoi(optarg); break; + case 'w': terminal_width= atoi(optarg); break; +#endif +#ifdef BB_FEATURE_HUMAN_READABLE + case 'h': ls_disp_hr = TRUE; break; +#endif + case 'k': break; + default: + goto print_usage_message; + } + } + + /* sort out which command line options take precedence */ +#ifdef BB_FEATURE_LS_RECURSIVE + if (disp_opts & DISP_NOLIST) + disp_opts &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ +#endif +#if defined (BB_FEATURE_LS_TIMESTAMPS) && defined (BB_FEATURE_LS_SORTFILES) + if (time_fmt & TIME_CHANGE) sort_opts= SORT_CTIME; + if (time_fmt & TIME_ACCESS) sort_opts= SORT_ATIME; +#endif + if (style_fmt != STYLE_LONG) + list_fmt &= ~LIST_ID_NUMERIC; /* numeric uid only for long list */ +#ifdef BB_FEATURE_LS_USERNAME + if (style_fmt == STYLE_LONG && (list_fmt & LIST_ID_NUMERIC)) + list_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ +#endif + + /* choose a display format */ + if (style_fmt == STYLE_AUTO) + style_fmt = isatty(fileno(stdout)) ? STYLE_COLUMNS : STYLE_SINGLE; + + /* + * 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]= 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) + disp_opts |= 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++) { + cur= (struct dnode *)xmalloc(sizeof(struct dnode)); + cur->fullname= xstrdup(av[oi]); + cur->name= cur->fullname; + if (my_stat(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; inext; + } + + + if (disp_opts & DISP_NOLIST) { +#ifdef BB_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 BB_FEATURE_LS_SORTFILES + shellsort(dnf, dnfiles); +#endif + showfiles(dnf, dnfiles); + } + if (dndirs > 0) { +#ifdef BB_FEATURE_LS_SORTFILES + shellsort(dnd, dndirs); +#endif + showdirs(dnd, dndirs); + } + } + return(status); + + print_usage_message: + show_usage(); +} diff --git a/busybox/coreutils/md5sum.c b/busybox/coreutils/md5sum.c new file mode 100644 index 000000000..bb4d115ca --- /dev/null +++ b/busybox/coreutils/md5sum.c @@ -0,0 +1,1074 @@ +/* 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Written by Ulrich Drepper */ +/* Hacked to work with BusyBox by Alfred M. Szmidt */ + +/* + * 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 + +/**********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined HAVE_LIMITS_H +# include +#endif +#include "busybox.h" + +/* For some silly reason, this file uses backwards TRUE and FALSE conventions */ +#undef TRUE +#undef FALSE +#define FALSE ((int) 1) +#define TRUE ((int) 0) + +//---------------------------------------------------------------------------- +//--------md5.c +//---------------------------------------------------------------------------- + +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + * according to the definition of MD5 in RFC 1321 from April 1992. + */ + +/* Written by Ulrich Drepper , 1995. */ + +//---------------------------------------------------------------------------- +//--------md5.h +//---------------------------------------------------------------------------- + +/* md5.h - Declaration of functions and data types used for MD5 sum + computing library functions. */ + +typedef u_int32_t md5_uint32; + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +static void md5_init_ctx __P ((struct md5_ctx *ctx)); + +/* 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_process_block __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* 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_process_bytes __P ((const void *buffer, size_t len, + struct md5_ctx *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_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf)); + + + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +static int md5_stream __P ((FILE *stream, void *resblock)); + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. 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. */ +static void *md5_buffer __P ((const char *buffer, size_t len, void *resblock)); + +//---------------------------------------------------------------------------- +//--------end of md5.h +//---------------------------------------------------------------------------- + +/* Handle endian-ness */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + #define SWAP(n) (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 + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void md5_init_ctx(struct md5_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +static void *md5_finish_ctx(struct md5_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 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 + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) & ctx->buffer[bytes + pad] = SWAP(ctx->total[0] << 3); + *(md5_uint32 *) & ctx->buffer[bytes + pad + 4] = + SWAP( ((ctx->total[1] << 3) | (ctx->total[0] >> 29)) ); + + /* Process last bytes. */ + md5_process_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. */ + ((md5_uint32 *) resbuf)[0] = SWAP(ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP(ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP(ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP(ctx->D); + + return resbuf; +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +static int md5_stream(FILE *stream, void *resblock) +{ + /* Important: BLOCKSIZE must be a multiple of 64. */ +static const int BLOCKSIZE = 4096; + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx(&ctx); + + /* Iterate over full file contents. */ + while (1) { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + do { + n = fread(buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + } + while (sum < BLOCKSIZE && n != 0); + if (n == 0 && ferror(stream)) + return 1; + + /* If end of file is reached, end the loop. */ + if (n == 0) + break; + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block(buffer, BLOCKSIZE, &ctx); + } + + /* Add the last bytes if necessary. */ + if (sum > 0) + md5_process_bytes(buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx(&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. 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. */ +static void *md5_buffer(const char *buffer, size_t len, void *resblock) +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx(&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes(buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx(&ctx, resblock); +} + +static void md5_process_bytes(const void *buffer, size_t len, struct md5_ctx *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_process_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_process_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; + } +} + +/* 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)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ +static void md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx) +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof(md5_uint32); + const md5_uint32 *endp = words + nwords; +#if MD5SUM_SIZE_VS_SPEED > 0 + static const md5_uint32 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 + 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 +#endif + + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 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) { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + +#if MD5SUM_SIZE_VS_SPEED > 1 +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + const md5_uint32 *pc; + const char *pp; + const char *ps; + int i; + md5_uint32 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++; + temp = 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++; + temp = 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++; + temp = 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++; + temp = 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++; + temp = CYCLIC (temp, ps[i&3]); + temp += B; + A = D; D = C; C = B; B = temp; + } + +#endif +#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 md5_uint32 *pc; + const char *pp; + int i; +#endif + + /* 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 + + /* 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 + + /* 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 + + /* 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 +#endif + + /* 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; +} + +//---------------------------------------------------------------------------- +//--------end of md5.c +//---------------------------------------------------------------------------- + +#define ISWHITE(c) ((c) == ' ' || (c) == '\t') +#define ISXDIGIT(c) (isxdigit (c)) + +/* The minimum length of a valid digest line in a file produced + by `md5sum FILE' and read by `md5sum -c'. This length does + not include any newline character at the end of a line. */ +static const int MIN_DIGEST_LINE_LENGTH = 35; /* 32 - message digest length + 2 - blank and binary indicator + 1 - minimum filename length */ + +static int have_read_stdin; /* Nonzero if any of the files read were + the standard input. */ + +static int status_only = 0; /* With -c, don't generate any output. + The exit code indicates success or failure */ +static int warn = 0; /* With -w, print a message to standard error warning + about each improperly formatted MD5 checksum line */ + +static int split_3(char *s, + size_t s_len, + unsigned char **u, + char **w) +{ + size_t i = 0; + int escaped_filename = 0; + + while (ISWHITE(s[i])) + ++i; + + /* The line must have at least 35 (36 if the first is a backslash) + more characters to contain correct message digest information. + Ignore this line if it is too short. */ + if (!(s_len - i >= MIN_DIGEST_LINE_LENGTH + || (s[i] == '\\' && s_len - i >= 1 + MIN_DIGEST_LINE_LENGTH))) + return FALSE; + + if (s[i] == '\\') { + ++i; + escaped_filename = 1; + } + *u = (unsigned char *) &s[i]; + + /* The first field has to be the 32-character hexadecimal + representation of the message digest. If it is not followed + immediately by a white space it's an error. */ + i += 32; + if (!ISWHITE(s[i])) + return FALSE; + + s[i++] = '\0'; + + if (s[i] != ' ' && s[i++] != '*') + return FALSE; + + /* All characters between the type indicator and end of line are + significant -- that includes leading and trailing white space. */ + *w = &s[i]; + + if (escaped_filename) { + /* Translate each `\n' string in the file name to a NEWLINE, + and each `\\' string to a backslash. */ + + char *dst = &s[i]; + + while (i < s_len) { + switch (s[i]) { + case '\\': + if (i == s_len - 1) { + /* A valid line does not end with a backslash. */ + return FALSE; + } + ++i; + switch (s[i++]) { + case 'n': + *dst++ = '\n'; + break; + case '\\': + *dst++ = '\\'; + break; + default: + /* Only `\' or `n' may follow a backslash. */ + return FALSE; + } + break; + + case '\0': + /* The file name may not contain a NUL. */ + return FALSE; + break; + + default: + *dst++ = s[i++]; + break; + } + } + *dst = '\0'; + } + return TRUE; +} + +static inline int hex_digits(unsigned char const *s) +{ + while (*s) { + if (!ISXDIGIT(*s)) + return TRUE; + ++s; + } + return FALSE; +} + +/* An interface to md5_stream. Operate on FILENAME (it may be "-") and + put the result in *MD5_RESULT. Return non-zero upon failure, zero + to indicate success. */ +static int md5_file(const char *filename, + unsigned char *md5_result) +{ + FILE *fp; + + if (filename[0] == '-' && filename[1] == '\0') { + have_read_stdin = 1; + fp = stdin; + } else { + fp = wfopen(filename, "r"); + if (fp == NULL) + return FALSE; + } + + if (md5_stream(fp, md5_result)) { + perror_msg("%s", filename); + + if (fp != stdin) + fclose(fp); + return FALSE; + } + + if (fp != stdin && fclose(fp) == EOF) { + perror_msg("%s", filename); + return FALSE; + } + + return TRUE; +} + +static int md5_check(const char *checkfile_name) +{ + FILE *checkfile_stream; + int n_properly_formated_lines = 0; + int n_mismatched_checksums = 0; + int n_open_or_read_failures = 0; + unsigned char md5buffer[16]; + size_t line_number; + char line[BUFSIZ]; + + if (checkfile_name[0] == '-' && checkfile_name[1] == '\0') { + have_read_stdin = 1; + checkfile_stream = stdin; + } else { + checkfile_stream = wfopen(checkfile_name, "r"); + if (checkfile_stream == NULL) + return FALSE; + } + + line_number = 0; + + do { + char *filename; + unsigned char *md5num; + int line_length; + + ++line_number; + + fgets(line, BUFSIZ-1, checkfile_stream); + line_length = strlen(line); + + if (line_length <= 0 || line==NULL) + break; + + /* Ignore comment lines, which begin with a '#' character. */ + if (line[0] == '#') + continue; + + /* Remove any trailing newline. */ + if (line[line_length - 1] == '\n') + line[--line_length] = '\0'; + + if (split_3(line, line_length, &md5num, &filename) + || !hex_digits(md5num)) { + if (warn) { + error_msg("%s: %lu: improperly formatted MD5 checksum line", + checkfile_name, (unsigned long) line_number); + } + } else { + static const char bin2hex[] = { + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f' + }; + + ++n_properly_formated_lines; + + if (md5_file(filename, md5buffer)) { + ++n_open_or_read_failures; + if (!status_only) { + printf("%s: FAILED open or read\n", filename); + fflush(stdout); + } + } else { + size_t cnt; + /* Compare generated binary number with text representation + in check file. Ignore case of hex digits. */ + for (cnt = 0; cnt < 16; ++cnt) { + if (tolower(md5num[2 * cnt]) + != bin2hex[md5buffer[cnt] >> 4] + || (tolower(md5num[2 * cnt + 1]) + != (bin2hex[md5buffer[cnt] & 0xf]))) + break; + } + if (cnt != 16) + ++n_mismatched_checksums; + + if (!status_only) { + printf("%s: %s\n", filename, + (cnt != 16 ? "FAILED" : "OK")); + fflush(stdout); + } + } + } + } + + while (!feof(checkfile_stream) && !ferror(checkfile_stream)); + + if (ferror(checkfile_stream)) { + error_msg("%s: read error", checkfile_name); + return FALSE; + } + + if (checkfile_stream != stdin && fclose(checkfile_stream) == EOF) { + perror_msg("md5sum: %s", checkfile_name); + return FALSE; + } + + if (n_properly_formated_lines == 0) { + /* Warn if no tests are found. */ + error_msg("%s: no properly formatted MD5 checksum lines found", + checkfile_name); + return FALSE; + } else { + if (!status_only) { + int n_computed_checkums = (n_properly_formated_lines + - n_open_or_read_failures); + + if (n_open_or_read_failures > 0) { + error_msg("WARNING: %d of %d listed files could not be read", + n_open_or_read_failures, n_properly_formated_lines); + return FALSE; + } + + if (n_mismatched_checksums > 0) { + error_msg("WARNING: %d of %d computed checksums did NOT match", + n_mismatched_checksums, n_computed_checkums); + return FALSE; + } + } + } + + return ((n_properly_formated_lines > 0 && n_mismatched_checksums == 0 + && n_open_or_read_failures == 0) ? 0 : 1); +} + +int md5sum_main(int argc, + char **argv) +{ + unsigned char md5buffer[16]; + int do_check = 0; + int opt; + char **string = NULL; + size_t n_strings = 0; + size_t err = 0; + char file_type_specified = 0; + char binary = 0; + + while ((opt = getopt(argc, argv, "g:bcstw")) != -1) { + switch (opt) { + case 'g': { /* read a string */ + if (string == NULL) + string = (char **) xmalloc ((argc - 1) * sizeof (char *)); + + string[n_strings++] = optarg; + break; + } + + case 'b': /* read files in binary mode */ + file_type_specified = 1; + binary = 1; + break; + + case 'c': /* check MD5 sums against given list */ + do_check = 1; + break; + + case 's': /* don't output anything, status code shows success */ + status_only = 1; + warn = 0; + break; + + case 't': /* read files in text mode (default) */ + file_type_specified = 1; + binary = 0; + break; + + case 'w': /* warn about improperly formated MD5 checksum lines */ + status_only = 0; + warn = 1; + break; + + default: + show_usage(); + } + } + + if (file_type_specified && do_check) { + error_msg_and_die("the -b and -t options are meaningless when verifying checksums"); + } + + if (n_strings > 0 && do_check) { + error_msg_and_die("the -g and -c options are mutually exclusive"); + } + + if (status_only && !do_check) { + error_msg_and_die("the -s option is meaningful only when verifying checksums"); + } + + if (warn && !do_check) { + error_msg_and_die("the -w option is meaningful only when verifying checksums"); + } + + if (n_strings > 0) { + size_t i; + + if (optind < argc) { + error_msg_and_die("no files may be specified when using -g"); + } + for (i = 0; i < n_strings; ++i) { + size_t cnt; + md5_buffer (string[i], strlen (string[i]), md5buffer); + + for (cnt = 0; cnt < 16; ++cnt) + printf ("%02x", md5buffer[cnt]); + + printf (" \"%s\"\n", string[i]); + } + } else if (do_check) { + if (optind + 1 < argc) { + error_msg("only one argument may be specified when using -c"); + } + + err = md5_check ((optind == argc) ? "-" : argv[optind]); + } else { + if (optind == argc) + argv[argc++] = "-"; + + for (; optind < argc; ++optind) { + int fail; + char *file = argv[optind]; + + fail = md5_file (file, md5buffer); + err |= fail; + if (!fail && file[0]=='-' && file[1] == '\0') { + size_t i; + for (i = 0; i < 16; ++i) + printf ("%02x", md5buffer[i]); + putchar ('\n'); + } else if (!fail) { + size_t i; + /* Output a leading backslash if the file name contains + a newline or backslash. */ + if (strchr (file, '\n') || strchr (file, '\\')) + putchar ('\\'); + + for (i = 0; i < 16; ++i) + printf ("%02x", md5buffer[i]); + + putchar (' '); + if (binary) + putchar ('*'); + else + putchar (' '); + + /* Translate each NEWLINE byte to the string, "\\n", + and each backslash to "\\\\". */ + for (i = 0; i < strlen (file); ++i) { + switch (file[i]) { + case '\n': + fputs ("\\n", stdout); + break; + + case '\\': + fputs ("\\\\", stdout); + break; + + default: + putchar (file[i]); + break; + } + } + putchar ('\n'); + } + } + } + + if (fclose (stdout) == EOF) { + error_msg_and_die("write error"); + } + + if (have_read_stdin && fclose (stdin) == EOF) { + error_msg_and_die("standard input"); + } + + if (err == 0) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} diff --git a/busybox/coreutils/mkdir.c b/busybox/coreutils/mkdir.c new file mode 100644 index 000000000..03c49f098 --- /dev/null +++ b/busybox/coreutils/mkdir.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mkdir implementation for busybox + * + * Copyright (C) 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 +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +extern int mkdir_main (int argc, char **argv) +{ + mode_t mode = -1; + int flags = 0; + int status = 0; + int i, opt; + + while ((opt = getopt (argc, argv, "m:p")) != -1) { + switch (opt) { + case 'm': + mode = 0777; + if (!parse_mode (optarg, &mode)) + error_msg_and_die ("invalid mode `%s'", optarg); + break; + case 'p': + flags |= FILEUTILS_RECUR; + break; + default: + show_usage (); + } + } + + if (optind == argc) + show_usage (); + + for (i = optind; i < argc; i++) + if (make_directory (argv[i], mode, flags) < 0) + status = 1; + + return status; +} diff --git a/busybox/coreutils/mkfifo.c b/busybox/coreutils/mkfifo.c new file mode 100644 index 000000000..ca217fa23 --- /dev/null +++ b/busybox/coreutils/mkfifo.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mkfifo implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +extern int mkfifo_main(int argc, char **argv) +{ + char *thisarg; + mode_t mode = 0666; + + argc--; + argv++; + + /* Parse any options */ + while (argc > 1) { + if (**argv != '-') + show_usage(); + thisarg = *argv; + thisarg++; + switch (*thisarg) { + case 'm': + argc--; + argv++; + parse_mode(*argv, &mode); + break; + default: + show_usage(); + } + argc--; + argv++; + } + if (argc < 1 || *argv[0] == '-') + show_usage(); + if (mkfifo(*argv, mode) < 0) + perror_msg_and_die("mkfifo"); + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/mknod.c b/busybox/coreutils/mknod.c new file mode 100644 index 000000000..b4d4b82a1 --- /dev/null +++ b/busybox/coreutils/mknod.c @@ -0,0 +1,92 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mknod implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +int mknod_main(int argc, char **argv) +{ + char *thisarg; + mode_t mode = 0; + mode_t perm = 0666; + dev_t dev = 0; + + argc--; + argv++; + + /* Parse any options */ + while (argc > 1) { + if (**argv != '-') + break; + thisarg = *argv; + thisarg++; + switch (*thisarg) { + case 'm': + argc--; + argv++; + parse_mode(*argv, &perm); + umask(0); + break; + default: + show_usage(); + } + argc--; + argv++; + } + if (argc != 4 && argc != 2) { + show_usage(); + } + switch (argv[1][0]) { + case 'c': + case 'u': + mode = S_IFCHR; + break; + case 'b': + mode = S_IFBLK; + break; + case 'p': + mode = S_IFIFO; + if (argc!=2) { + show_usage(); + } + break; + default: + show_usage(); + } + + if (mode == S_IFCHR || mode == S_IFBLK) { + dev = (atoi(argv[2]) << 8) | atoi(argv[3]); + } + + mode |= perm; + + if (mknod(argv[0], mode, dev) != 0) + perror_msg_and_die("%s", argv[0]); + return EXIT_SUCCESS; +} + diff --git a/busybox/coreutils/mv.c b/busybox/coreutils/mv.c new file mode 100644 index 000000000..b890abf6e --- /dev/null +++ b/busybox/coreutils/mv.c @@ -0,0 +1,168 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mv implementation for busybox + * + * + * Copyright (C) 2000 by 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 +#include +#include +#include +#include +#include + +#include "busybox.h" + +static int flags; + +static int manual_rename(const char *source, const char *dest) +{ + struct stat source_stat; + struct stat dest_stat; + int source_exists = 1; + int dest_exists = 1; + + if (stat(source, &source_stat) < 0) { + if (errno != ENOENT) { + perror_msg("unable to stat `%s'", source); + return -1; + } + source_exists = 0; + } + + if (stat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + perror_msg("unable to stat `%s'", dest); + return -1; + } + dest_exists = 0; + } + + if (dest_exists) { + if (S_ISDIR(dest_stat.st_mode) && + (!source_exists || !S_ISDIR(source_stat.st_mode))) { + error_msg("cannot overwrite directory with non-directory"); + return -1; + } + + if (!S_ISDIR(dest_stat.st_mode) && source_exists && + S_ISDIR(source_stat.st_mode)) { + error_msg("cannot overwrite non-directory with directory"); + return -1; + } + + if (unlink(dest) < 0) { + perror_msg("cannot remove `%s'", dest); + return -1; + } + } + + if (copy_file(source, dest, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS | + FILEUTILS_PRESERVE_SYMLINKS) < 0) + return -1; + + if (remove_file(source, FILEUTILS_RECUR | FILEUTILS_FORCE) < 0) + return -1; + + return 0; +} + +static int move_file(const char *source, const char *dest) +{ + struct stat dest_stat; + int dest_exists = 1; + + if (stat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + perror_msg("unable to stat `%s'", dest); + return -1; + } + dest_exists = 0; + } + + if (dest_exists && !(flags & FILEUTILS_FORCE) && + ((access(dest, W_OK) < 0 && isatty(0)) || + (flags & FILEUTILS_INTERACTIVE))) { + fprintf(stderr, "mv: overwrite `%s'? ", dest); + if (!ask_confirmation()) + return 0; + } + + if (rename(source, dest) < 0) { + if (errno == EXDEV) + return manual_rename(source, dest); + + perror_msg("unable to rename `%s'", source); + return -1; + } + + return 0; +} + +extern int mv_main(int argc, char **argv) +{ + int status = 0; + int opt; + int i; + + while ((opt = getopt(argc, argv, "fi")) != -1) + switch (opt) { + case 'f': + flags &= ~FILEUTILS_INTERACTIVE; + flags |= FILEUTILS_FORCE; + break; + case 'i': + flags &= ~FILEUTILS_FORCE; + flags |= FILEUTILS_INTERACTIVE; + break; + default: + show_usage(); + } + + if (optind + 2 > argc) + show_usage(); + + if (optind + 2 == argc) { + struct stat dest_stat; + int dest_exists = 1; + + if (stat(argv[optind + 1], &dest_stat) < 0) { + if (errno != ENOENT) + perror_msg_and_die("unable to stat `%s'", argv[optind + 1]); + dest_exists = 0; + } + + if (!dest_exists || !S_ISDIR(dest_stat.st_mode)) { + if (move_file(argv[optind], argv[optind + 1]) < 0) + status = 1; + return status; + } + } + + for (i = optind; i < argc - 1; i++) { + char *dest = concat_path_file(argv[argc - 1], + get_last_path_component(argv[i])); + if (move_file(argv[i], dest) < 0) + status = 1; + free(dest); + } + + return status; +} diff --git a/busybox/coreutils/printf.c b/busybox/coreutils/printf.c new file mode 100644 index 000000000..d579a9b4e --- /dev/null +++ b/busybox/coreutils/printf.c @@ -0,0 +1,455 @@ +/* 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 */ + + +// 19990508 Busy Boxed! Dave Cinege + +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +#ifndef S_IFMT +static const int S_IFMT = 0170000; +#endif +#if !defined(S_ISBLK) && defined(S_IFBLK) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +#define IN_CTYPE_DOMAIN(c) 1 + +#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) +#define ISDIGIT(c) (((unsigned char) (c)) - '0' <= 9) + +#define isodigit(c) ((c) >= '0' && (c) <= '7') +#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0') +#define octtobin(c) ((c) - '0') + +static double xstrtod __P((char *s)); +static int print_esc __P((char *escstart)); +static int print_formatted __P((char *format, int argc, char **argv)); +static long xstrtol __P((char *s)); +static unsigned long xstrtoul __P((char *s)); +static void print_direc +__P( + + (char *start, size_t length, int field_width, int precision, + char *argument)); +static void print_esc_char __P((int c)); +static void print_esc_string __P((char *str)); +static void verify __P((char *s, char *end)); + +/* The value to return to the calling program. */ +static int exit_status; + +int printf_main(int argc, char **argv) +{ + char *format; + int args_used; + + exit_status = 0; + if (argc <= 1 || **(argv + 1) == '-') { + 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_status); +} + +/* 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 '\\': + f += print_esc(f); + break; + + default: + putchar(*f); + } + } + + return save_argc - argc; +} + +/* Print a \ escape sequence starting at ESCSTART. + Return the number of characters in the escape sequence + besides the backslash. */ + +static int print_esc(char *escstart) +{ + register char *p = escstart + 1; + int esc_value = 0; /* Value of \nnn escape. */ + int esc_length; /* Length of \nnn escape. */ + + /* \0ooo and \xhhh escapes have maximum length of 3 chars. */ + if (*p == 'x') { + for (esc_length = 0, ++p; + esc_length < 3 && ISXDIGIT(*p); ++esc_length, ++p) + esc_value = esc_value * 16 + hextobin(*p); +/* if (esc_length == 0) + fprintf(stderr, "missing hex in esc"); +*/ + putchar(esc_value); + } else if (*p == '0') { + for (esc_length = 0, ++p; + esc_length < 3 && isodigit(*p); ++esc_length, ++p) + esc_value = esc_value * 8 + octtobin(*p); + putchar(esc_value); + } else if (strchr("\"\\abcfnrtv", *p)) + print_esc_char(*p++); +/* else + fprintf(stderr, "\\%c: invalid esc", *p); +*/ + return p - escstart - 1; +} + +/* Output a single-character \ escape. */ + +static void print_esc_char(int c) +{ + switch (c) { + case 'a': /* Alert. */ + putchar(7); + break; + case 'b': /* Backspace. */ + putchar(8); + break; + case 'c': /* Cancel the rest of the output. */ + exit(0); + break; + case 'f': /* Form feed. */ + putchar(12); + break; + case 'n': /* New line. */ + putchar(10); + break; + case 'r': /* Carriage return. */ + putchar(13); + break; + case 't': /* Horizontal tab. */ + putchar(9); + break; + case 'v': /* Vertical tab. */ + putchar(11); + break; + default: + putchar(c); + break; + } +} + +/* Print string STR, evaluating \ escapes. */ + +static void print_esc_string(char *str) +{ + for (; *str; str++) + if (*str == '\\') + str += print_esc(str); + else + putchar(*str); +} + +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 *s) +{ + char *end; + unsigned long val; + + errno = 0; + val = strtoul(s, &end, 0); + verify(s, end); + return val; +} + +static long xstrtol(char *s) +{ + char *end; + long val; + + errno = 0; + val = strtol(s, &end, 0); + verify(s, end); + return val; +} + +static double xstrtod(char *s) +{ + char *end; + double val; + + errno = 0; + val = strtod(s, &end); + verify(s, end); + return val; +} + +static void verify(char *s, char *end) +{ + if (errno) { + fprintf(stderr, "%s", s); + exit_status = 1; + } else if (*end) { + /* + if (s == end) + fprintf(stderr, "%s: expected numeric", s); + else + fprintf(stderr, "%s: not completely converted", s); + */ + exit_status = 1; + } +} diff --git a/busybox/coreutils/pwd.c b/busybox/coreutils/pwd.c new file mode 100644 index 000000000..f6a00bf1e --- /dev/null +++ b/busybox/coreutils/pwd.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini pwd implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "busybox.h" + +extern int pwd_main(int argc, char **argv) +{ + static char *buf; + + buf = xgetcwd(buf); + + if (buf != NULL) { + puts(buf); + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} diff --git a/busybox/coreutils/rm.c b/busybox/coreutils/rm.c new file mode 100644 index 000000000..51c9f4ceb --- /dev/null +++ b/busybox/coreutils/rm.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rm implementation for busybox + * + * + * Copyright (C) 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int rm_main(int argc, char **argv) +{ + int status = 0; + int opt; + int flags = 0; + int i; + + while ((opt = getopt(argc, argv, "fiRr")) != -1) { + switch (opt) { + case 'f': + flags &= ~FILEUTILS_INTERACTIVE; + flags |= FILEUTILS_FORCE; + break; + case 'i': + flags &= ~FILEUTILS_FORCE; + flags |= FILEUTILS_INTERACTIVE; + break; + case 'R': + case 'r': + flags |= FILEUTILS_RECUR; + break; + } + } + + if (!(flags & FILEUTILS_FORCE) && optind == argc) + show_usage(); + + for (i = optind; i < argc; i++) { + char *base = get_last_path_component(argv[i]); + + if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0) { + error_msg("cannot remove `.' or `..'"); + status = 1; + continue; + } + + if (remove_file(argv[i], flags) < 0) + status = 1; + } + + return status; +} diff --git a/busybox/coreutils/rmdir.c b/busybox/coreutils/rmdir.c new file mode 100644 index 000000000..2c280376f --- /dev/null +++ b/busybox/coreutils/rmdir.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rmdir implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include "busybox.h" + +extern int rmdir_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + + if (argc == 1 || **(argv + 1) == '-') + show_usage(); + + while (--argc > 0) { + if (rmdir(*(++argv)) == -1) { + perror_msg("%s", *argv); + status = EXIT_FAILURE; + } + } + return status; +} diff --git a/busybox/coreutils/sleep.c b/busybox/coreutils/sleep.c new file mode 100644 index 000000000..3bcab88ee --- /dev/null +++ b/busybox/coreutils/sleep.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sleep implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int sleep_main(int argc, char **argv) +{ + if ((argc < 2) || (**(argv + 1) == '-')) { + show_usage(); + } + + if (sleep(atoi(*(++argv))) != 0) + perror_msg_and_die("sleep"); + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/sort.c b/busybox/coreutils/sort.c new file mode 100644 index 000000000..4f4979cc5 --- /dev/null +++ b/busybox/coreutils/sort.c @@ -0,0 +1,106 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sort implementation for busybox + * + * + * Copyright (C) 2000 by 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 +#include +#include +#include "busybox.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, opt, nlines = 0; + int (*compare)(const void *, const void *) = compare_ascii; +#ifdef BB_FEATURE_SORT_REVERSE + int reverse = FALSE; +#endif +#ifdef BB_FEATURE_SORT_UNIQUE + int unique = FALSE; +#endif + + while ((opt = getopt(argc, argv, "nru")) != -1) { + switch (opt) { + case 'n': + compare = compare_numeric; + break; +#ifdef BB_FEATURE_SORT_REVERSE + case 'r': + reverse = TRUE; + break; +#endif +#ifdef BB_FEATURE_SORT_UNIQUE + case 'u': + unique = TRUE; + break; +#endif + default: + show_usage(); + } + } + + /* read the input */ + for (i = optind; i == optind || i < argc; i++) { + if (argv[i] == NULL) + fp = stdin; + else + fp = xfopen(argv[i], "r"); + + while ((line = get_line_from_file(fp)) != NULL) { + lines = xrealloc(lines, sizeof(char *) * (nlines + 1)); + chomp(line); + lines[nlines++] = line; + } + } + + /* sort it */ + qsort(lines, nlines, sizeof(char *), compare); + + /* print it */ +#ifdef BB_FEATURE_SORT_REVERSE + if (reverse) { + for (i = --nlines; 0 <= i; i--) +#ifdef BB_FEATURE_SORT_UNIQUE + if((!unique) || (i == nlines) || (strcmp(lines[i + 1], lines[i]))) +#endif + puts(lines[i]); + } else +#endif + for (i = 0; i < nlines; i++) +#ifdef BB_FEATURE_SORT_UNIQUE + if((!unique) || (!i) || (strcmp(lines[i - 1], lines[i]))) +#endif + puts(lines[i]); + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/stty.c b/busybox/coreutils/stty.c new file mode 100644 index 000000000..2e00a496d --- /dev/null +++ b/busybox/coreutils/stty.c @@ -0,0 +1,1376 @@ +/* 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 + + Special for busybox ported by Vladimir Oleynik 2001 + + */ + +//#define TEST + +#include +#include +#include + +#include +#include + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#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 +}; + +/* What to output and how. */ +enum output_type { + changed, all, recoverable /* Default, -a, -g. */ +}; + +/* Which member(s) of `struct termios' a mode uses. */ +enum mode_type { + 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; /* Which structure element to change. */ + char flags; /* Setting and display options. */ + unsigned long bits; /* Bits to set for this mode. */ + unsigned long mask; /* Other bits to turn off for this mode. */ +}; + +static const struct mode_info mode_info[] = { + {"parenb", control, REV, PARENB, 0 }, + {"parodd", control, REV, PARODD, 0 }, + {"cs5", control, 0, CS5, CSIZE}, + {"cs6", control, 0, CS6, CSIZE}, + {"cs7", control, 0, CS7, CSIZE}, + {"cs8", control, 0, CS8, CSIZE}, + {"hupcl", control, REV, HUPCL, 0 }, + {"hup", control, REV | OMIT, HUPCL, 0 }, + {"cstopb", control, REV, CSTOPB, 0 }, + {"cread", control, SANE_SET | REV, CREAD, 0 }, + {"clocal", control, REV, CLOCAL, 0 }, +#ifdef CRTSCTS + {"crtscts", control, REV, CRTSCTS, 0 }, +#endif + {"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 }, + {"brkint", input, SANE_SET | REV, BRKINT, 0 }, + {"ignpar", input, REV, IGNPAR, 0 }, + {"parmrk", input, REV, PARMRK, 0 }, + {"inpck", input, REV, INPCK, 0 }, + {"istrip", input, REV, ISTRIP, 0 }, + {"inlcr", input, SANE_UNSET | REV, INLCR, 0 }, + {"igncr", input, SANE_UNSET | REV, IGNCR, 0 }, + {"icrnl", input, SANE_SET | REV, ICRNL, 0 }, + {"ixon", input, REV, IXON, 0 }, + {"ixoff", input, SANE_UNSET | REV, IXOFF, 0 }, + {"tandem", input, REV | OMIT, IXOFF, 0 }, +#ifdef IUCLC + {"iuclc", input, SANE_UNSET | REV, IUCLC, 0 }, +#endif +#ifdef IXANY + {"ixany", input, SANE_UNSET | REV, IXANY, 0 }, +#endif +#ifdef IMAXBEL + {"imaxbel", input, SANE_SET | REV, IMAXBEL, 0 }, +#endif + {"opost", output, SANE_SET | REV, OPOST, 0 }, +#ifdef OLCUC + {"olcuc", output, SANE_UNSET | REV, OLCUC, 0 }, +#endif +#ifdef OCRNL + {"ocrnl", output, SANE_UNSET | REV, OCRNL, 0 }, +#endif +#ifdef ONLCR + {"onlcr", output, SANE_SET | REV, ONLCR, 0 }, +#endif +#ifdef ONOCR + {"onocr", output, SANE_UNSET | REV, ONOCR, 0 }, +#endif +#ifdef ONLRET + {"onlret", output, SANE_UNSET | REV, ONLRET, 0 }, +#endif +#ifdef OFILL + {"ofill", output, SANE_UNSET | REV, OFILL, 0 }, +#endif +#ifdef OFDEL + {"ofdel", output, SANE_UNSET | REV, OFDEL, 0 }, +#endif +#ifdef NLDLY + {"nl1", output, SANE_UNSET, NL1, NLDLY}, + {"nl0", output, SANE_SET, NL0, NLDLY}, +#endif +#ifdef CRDLY + {"cr3", output, SANE_UNSET, CR3, CRDLY}, + {"cr2", output, SANE_UNSET, CR2, CRDLY}, + {"cr1", output, SANE_UNSET, CR1, CRDLY}, + {"cr0", output, SANE_SET, CR0, CRDLY}, +#endif + +#ifdef TABDLY + {"tab3", output, SANE_UNSET, TAB3, TABDLY}, + {"tab2", output, SANE_UNSET, TAB2, TABDLY}, + {"tab1", output, SANE_UNSET, TAB1, TABDLY}, + {"tab0", output, SANE_SET, TAB0, TABDLY}, +#else +# ifdef OXTABS + {"tab3", output, SANE_UNSET, OXTABS, 0 }, +# endif +#endif + +#ifdef BSDLY + {"bs1", output, SANE_UNSET, BS1, BSDLY}, + {"bs0", output, SANE_SET, BS0, BSDLY}, +#endif +#ifdef VTDLY + {"vt1", output, SANE_UNSET, VT1, VTDLY}, + {"vt0", output, SANE_SET, VT0, VTDLY}, +#endif +#ifdef FFDLY + {"ff1", output, SANE_UNSET, FF1, FFDLY}, + {"ff0", output, SANE_SET, FF0, FFDLY}, +#endif + {"isig", local, SANE_SET | REV, ISIG, 0 }, + {"icanon", local, SANE_SET | REV, ICANON, 0 }, +#ifdef IEXTEN + {"iexten", local, SANE_SET | REV, IEXTEN, 0 }, +#endif + {"echo", local, SANE_SET | REV, ECHO, 0 }, + {"echoe", local, SANE_SET | REV, ECHOE, 0 }, + {"crterase", local, REV | OMIT, ECHOE, 0 }, + {"echok", local, SANE_SET | REV, ECHOK, 0 }, + {"echonl", local, SANE_UNSET | REV, ECHONL, 0 }, + {"noflsh", local, SANE_UNSET | REV, NOFLSH, 0 }, +#ifdef XCASE + {"xcase", local, SANE_UNSET | REV, XCASE, 0 }, +#endif +#ifdef TOSTOP + {"tostop", local, SANE_UNSET | REV, TOSTOP, 0 }, +#endif +#ifdef ECHOPRT + {"echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 }, + {"prterase", local, REV | OMIT, ECHOPRT, 0 }, +#endif +#ifdef ECHOCTL + {"echoctl", local, SANE_SET | REV, ECHOCTL, 0 }, + {"ctlecho", local, REV | OMIT, ECHOCTL, 0 }, +#endif +#ifdef ECHOKE + {"echoke", local, SANE_SET | REV, ECHOKE, 0 }, + {"crtkill", local, REV | OMIT, ECHOKE, 0 }, +#endif + {evenp, combination, REV | OMIT, 0, 0 }, + {parity, combination, REV | OMIT, 0, 0 }, + {stty_oddp, combination, REV | OMIT, 0, 0 }, + {stty_nl, combination, REV | OMIT, 0, 0 }, + {stty_ek, combination, OMIT, 0, 0 }, + {stty_sane, combination, OMIT, 0, 0 }, + {cooked, combination, REV | OMIT, 0, 0 }, + {raw, combination, REV | OMIT, 0, 0 }, + {stty_pass8, combination, REV | OMIT, 0, 0 }, + {litout, combination, REV | OMIT, 0, 0 }, + {cbreak, combination, REV | OMIT, 0, 0 }, +#ifdef IXANY + {decctlq, combination, REV | OMIT, 0, 0 }, +#endif +#if defined (TABDLY) || defined (OXTABS) + {stty_tabs, combination, REV | OMIT, 0, 0 }, +#endif +#if defined(XCASE) && defined(IUCLC) && defined(OLCUC) + {stty_lcase, combination, REV | OMIT, 0, 0 }, + {stty_LCASE, combination, REV | OMIT, 0, 0 }, +#endif + {stty_crt, combination, OMIT, 0, 0 }, + {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'. */ + int 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 unsigned long baud_to_value(speed_t speed); +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, + const char *device_name); +static void display_changed(struct termios *mode); +static void display_recoverable(struct termios *mode); +static void display_settings(enum output_type output_type, + struct termios *mode, int fd, + const char *device_name); +static void display_speed(struct termios *mode, int fancy); +static void display_window_size(int fancy, int fd, + const char *device_name); +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, + const char *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; + enum output_type output_type; + 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; + const char *device_name; + + output_type = 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_type = all; + break; + + case 'g': + recoverable_output = 1; + output_type = recoverable; + break; + + case 'F': + if (file_name) + 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) + 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)) + 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 = open(device_name, O_RDONLY | O_NONBLOCK); + if (fd < 0) + perror_msg_and_die("%s", device_name); + if ((fdflags = fcntl(fd, F_GETFL)) == -1 + || fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) + perror_msg_and_die("%s: couldn't reset non-blocking mode", + device_name); + } else { + fd = 0; + device_name = "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_msg_and_die("%s", device_name); + + if (verbose_output || recoverable_output || noargs) { + max_col = screen_columns(); + current_col = 0; + display_settings(output_type, &mode, fd, device_name); + return EXIT_SUCCESS; + } + + speed_was_set = 0; + require_set_attr = 0; + k = optind; + while (k < argc) { + int match_found = 0; + int reversed = 0; + int i; + + if (argv[k][0] == '-') { + ++argv[k]; + 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) + 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) + 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) + 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) + 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) + error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + set_window_size((int) parse_number(argv[k], stty_suffixes), + -1, fd, device_name); + } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) { + if (k == argc - 1) + error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + set_window_size(-1, + (int) parse_number(argv[k], stty_suffixes), + fd, device_name); + } else if (STREQ(argv[k], "size")) { + max_col = screen_columns(); + current_col = 0; + display_window_size(0, fd, device_name); + } +#endif +#ifdef HAVE_C_LINE + else if (STREQ(argv[k], "line")) { + if (k == argc - 1) + error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + mode.c_line = parse_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 + error_msg_and_die("invalid argument `%s'", argv[k]); + } + k++; + } + + if (require_set_attr) { + struct termios new_mode; + + if (tcsetattr(fd, TCSADRAIN, &mode)) + perror_msg_and_die("%s", device_name); + + /* 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_msg_and_die("%s", device_name); + + /* 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 + error_msg_and_die ("%s: unable to perform all requested operations", + device_name); + } + } + + 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 & ~info->mask & ~info->bits; + else + *bitsp = (*bitsp & ~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 = parse_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 = parse_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 == input_speed || type == both_speeds) + cfsetispeed(mode, baud); + if (type == output_speed || type == both_speeds) + 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, const char *device_name) +{ + struct winsize win; + + if (get_win_size(fd, &win)) { + if (errno != EINVAL) + perror_msg_and_die("%s", device_name); + memset(&win, 0, sizeof(win)); + } + + if (rows >= 0) + win.ws_row = rows; + if (cols >= 0) + win.ws_col = cols; + +# ifdef TIOCSSIZE + /* Alexander Dupuy 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 = 1; + win.ws_col = 1; + + if (ioctl(fd, TIOCSWINSZ, (char *) &win)) + perror_msg_and_die("%s", device_name); + + if (ioctl(fd, TIOCSSIZE, (char *) &ttysz)) + perror_msg_and_die("%s", device_name); + return; + } +# endif + + if (ioctl(fd, TIOCSWINSZ, (char *) &win)) + perror_msg_and_die("%s", device_name); +} + +static void display_window_size(int fancy, int fd, const char *device_name) +{ + struct winsize win; + + if (get_win_size(fd, &win)) { + if (errno != EINVAL) + perror_msg_and_die("%s", device_name); + if (!fancy) + perror_msg_and_die("%s: no size information for this device", + device_name); + } 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) +{ +#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 + + if (getenv("COLUMNS")) + return atoi(getenv("COLUMNS")); + return 80; +} + +static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode) +{ + switch (type) { + case control: + return &mode->c_cflag; + + case input: + return &mode->c_iflag; + + case output: + return &mode->c_oflag; + + case local: + return &mode->c_lflag; + + default: /* combination: */ + return NULL; + } +} + +static void +display_settings(enum output_type output_type, struct termios *mode, + int fd, const char *device_name) +{ + switch (output_type) { + case changed: + display_changed(mode); + break; + + case all: + display_all(mode, fd, device_name); + break; + + case recoverable: + display_recoverable(mode); + break; + } +} + +static void display_changed(struct termios *mode) +{ + 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, const char *device_name) +{ + 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, device_name); +#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) +{ + if (cfgetispeed(mode) == 0 || cfgetispeed(mode) == cfgetospeed(mode)) + wrapf(fancy ? "speed %lu baud;" : "%lu\n", + baud_to_value(cfgetospeed(mode))); + else + wrapf(fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n", + baud_to_value(cfgetispeed(mode)), + baud_to_value(cfgetospeed(mode))); + if (!fancy) + current_col = 0; +} + +static void display_recoverable(struct termios *mode) +{ + 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; +} + +struct speed_map { + speed_t speed; /* Internal form. */ + unsigned long value; /* Numeric 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}, + {B19200, 19200}, + {B38400, 38400}, +#ifdef B57600 + {B57600, 57600}, +#endif +#ifdef B115200 + {B115200, 115200}, +#endif +#ifdef B230400 + {B230400, 230400}, +#endif +#ifdef B460800 + {B460800, 460800}, +#endif +}; + +static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map)); + +static speed_t string_to_baud(const char *arg) +{ + int i; + + for (i = 0; i < NUM_SPEEDS; ++i) + if (parse_number(arg, 0) == speeds[i].value) + return speeds[i].speed; + return (speed_t) - 1; +} + +static unsigned long baud_to_value(speed_t speed) +{ + int i; + + for (i = 0; i < NUM_SPEEDS; ++i) + if (speed == speeds[i].speed) + return speeds[i].value; + return 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 & ~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 & ~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 ""; + + if (ch >= 32) { + if (ch < 127) + *bpout++ = ch; + else if (ch == 127) { + *bpout++ = '^'; + *bpout++ = '?'; + } else { + *bpout++ = 'M', *bpout++ = '-'; + if (ch >= 128 + 32) { + if (ch < 128 + 127) + *bpout++ = ch - 128; + else { + *bpout++ = '^'; + *bpout++ = '?'; + } + } else { + *bpout++ = '^'; + *bpout++ = ch - 128 + 64; + } + } + } else { + *bpout++ = '^'; + *bpout++ = ch + 64; + } + *bpout = '\0'; + return (const char *) buf; +} + +#ifdef TEST + +const char *applet_name = "stty"; + +#endif + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/sync.c b/busybox/coreutils/sync.c new file mode 100644 index 000000000..ee22ae109 --- /dev/null +++ b/busybox/coreutils/sync.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sync implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int sync_main(int argc, char **argv) +{ + if (argc > 1 && **(argv + 1) == '-') + show_usage(); + sync(); + return(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/tail.c b/busybox/coreutils/tail.c new file mode 100644 index 000000000..90cc2a6ef --- /dev/null +++ b/busybox/coreutils/tail.c @@ -0,0 +1,251 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tail implementation for busybox + * + * + * Copyright (C) 2001 by 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 +#include +#include +#include +#include +#include +#include "busybox.h" + +static const struct suffix_mult tail_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1048576 }, + { NULL, 0 } +}; + +static const int BYTES = 0; +static const int LINES = 1; + +static char *tailbuf; +static int taillen; +static int newline; + +static void tailbuf_append(char *buf, int len) +{ + tailbuf = xrealloc(tailbuf, taillen + len); + memcpy(tailbuf + taillen, buf, len); + taillen += len; +} + +static void tailbuf_trunc() +{ + char *s; + s = memchr(tailbuf, '\n', taillen); + memmove(tailbuf, s + 1, taillen - ((s + 1) - tailbuf)); + taillen -= (s + 1) - tailbuf; + newline = 0; +} + +int tail_main(int argc, char **argv) +{ + int from_top = 0, units = LINES, count = 10, sleep_period = 1; + int show_headers = 0, hide_headers = 0, follow = 0; + int *fds, nfiles = 0, status = EXIT_SUCCESS, nread, nwrite, seen = 0; + char *s, *start, *end, buf[BUFSIZ]; + int i, opt; + + while ((opt = getopt(argc, argv, "c:fhn:q:s:v")) > 0) { + switch (opt) { + case 'f': + follow = 1; + break; +#ifdef BB_FEATURE_FANCY_TAIL + case 'c': + units = BYTES; + /* FALLS THROUGH */ +#endif + case 'n': + count = parse_number(optarg, tail_suffixes); + if (count < 0) + count = -count; + if (optarg[0] == '+') + from_top = 1; + break; +#ifdef BB_FEATURE_FANCY_TAIL + case 'q': + hide_headers = 1; + break; + case 's': + sleep_period = parse_number(optarg, 0); + break; + case 'v': + show_headers = 1; + break; +#endif + default: + show_usage(); + } + } + + /* open all the files */ + fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1)); + if (argc == optind) { + fds[nfiles++] = STDIN_FILENO; + argv[optind] = "standard input"; + } else { + for (i = optind; i < argc; i++) { + if (strcmp(argv[i], "-") == 0) { + fds[nfiles++] = STDIN_FILENO; + argv[i] = "standard input"; + } else if ((fds[nfiles++] = open(argv[i], O_RDONLY)) < 0) { + perror_msg("%s", argv[i]); + status = EXIT_FAILURE; + } + } + } + +#ifdef BB_FEATURE_FANCY_TAIL + /* tail the files */ + if (!from_top && units == BYTES) + tailbuf = xmalloc(count); +#endif + + for (i = 0; i < nfiles; i++) { + if (fds[i] == -1) + continue; + if (!count) { + lseek(fds[i], 0, SEEK_END); + continue; + } + seen = 0; + if (show_headers || (!hide_headers && nfiles > 1)) + printf("%s==> %s <==\n", i == 0 ? "" : "\n", argv[optind + i]); + while ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0) { + if (from_top) { +#ifdef BB_FEATURE_FANCY_TAIL + if (units == BYTES) { + if (count - 1 <= seen) + nwrite = nread; + else if (count - 1 <= seen + nread) + nwrite = nread + seen - (count - 1); + else + nwrite = 0; + seen += nread; + } else { +#else + { +#endif + if (count - 1 <= seen) + nwrite = nread; + else { + nwrite = 0; + for (s = memchr(buf, '\n', nread); s != NULL; + s = memchr(s+1, '\n', nread - (s + 1 - buf))) { + if (count - 1 <= ++seen) { + nwrite = nread - (s + 1 - buf); + break; + } + } + } + } + if (full_write(STDOUT_FILENO, buf + nread - nwrite, + nwrite) < 0) { + perror_msg("write"); + status = EXIT_FAILURE; + break; + } + } else { +#ifdef BB_FEATURE_FANCY_TAIL + if (units == BYTES) { + if (nread < count) { + memmove(tailbuf, tailbuf + nread, count - nread); + memcpy(tailbuf + count - nread, buf, nread); + } else { + memcpy(tailbuf, buf + nread - count, count); + } + seen += nread; + } else { +#else + { +#endif + for (start = buf, end = memchr(buf, '\n', nread); + end != NULL; start = end+1, + end = memchr(start, '\n', nread - (start - buf))) { + if (newline && count <= seen) + tailbuf_trunc(); + tailbuf_append(start, end - start + 1); + seen++; + newline = 1; + } + if (newline && count <= seen && nread - (start - buf) > 0) + tailbuf_trunc(); + tailbuf_append(start, nread - (start - buf)); + } + } + } + + if (nread < 0) { + perror_msg("read"); + status = EXIT_FAILURE; + } + +#ifdef BB_FEATURE_FANCY_TAIL + if (!from_top && units == BYTES) { + if (count < seen) + seen = count; + if (full_write(STDOUT_FILENO, tailbuf + count - seen, seen) < 0) { + perror_msg("write"); + status = EXIT_FAILURE; + } + } +#endif + + if (!from_top && units == LINES) { + if (full_write(STDOUT_FILENO, tailbuf, taillen) < 0) { + perror_msg("write"); + status = EXIT_FAILURE; + } + } + + taillen = 0; + } + + while (follow) { + sleep(sleep_period); + + for (i = 0; i < nfiles; i++) { + if (fds[i] == -1) + continue; + + if ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0) { + if (show_headers || (!hide_headers && nfiles > 1)) + printf("\n==> %s <==\n", argv[optind + i]); + + do { + full_write(STDOUT_FILENO, buf, nread); + } while ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0); + } + + if (nread < 0) { + perror_msg("read"); + status = EXIT_FAILURE; + } + } + } + + return status; +} diff --git a/busybox/coreutils/tee.c b/busybox/coreutils/tee.c new file mode 100644 index 000000000..439cf7dc5 --- /dev/null +++ b/busybox/coreutils/tee.c @@ -0,0 +1,68 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tee implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 "busybox.h" +#include +#include + +int +tee_main(int argc, char **argv) +{ + char *mode = "w"; + int c, i, status = 0, nfiles = 0; + FILE **files; + + while ((c = getopt(argc, argv, "a")) != EOF) { + switch (c) { + case 'a': + mode = "a"; + break; + default: + show_usage(); + } + } + + files = (FILE **)xmalloc(sizeof(FILE *) * (argc - optind + 1)); + files[nfiles++] = stdout; + while (optind < argc) { + if ((files[nfiles++] = fopen(argv[optind++], mode)) == NULL) { + nfiles--; + perror_msg("%s", argv[optind-1]); + status = 1; + } + } + + while ((c = getchar()) != EOF) + for (i = 0; i < nfiles; i++) + putc(c, files[i]); + + return status; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/test.c b/busybox/coreutils/test.c new file mode 100644 index 000000000..9c66cbb87 --- /dev/null +++ b/busybox/coreutils/test.c @@ -0,0 +1,579 @@ +/* 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 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 +#include +#include +#include +#include +#include +#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 ::= +*/ + +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} +}; + +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(); +static int oexpr(); +static int aexpr(); +static int nexpr(); +static int binop(); +static int primary(); +static int filstat(); +static int getn(); +static int newerf(); +static int olderf(); +static int equalf(); +static void syntax(); +static int test_eaccess(); +static int is_a_group_member(); +static void initialize_group_array(); + +extern int +test_main(int argc, char** argv) +{ + int res; + + if (strcmp(applet_name, "[") == 0) { + if (strcmp(argv[--argc], "]")) + 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(op, msg) + char *op; + char *msg; +{ + if (op && *op) + error_msg_and_die("%s: %s", op, msg); + else + error_msg_and_die("%s", msg); +} + +static int +oexpr(n) + enum token n; +{ + int res; + + res = aexpr(n); + if (t_lex(*++t_wp) == BOR) + return oexpr(t_lex(*++t_wp)) || res; + t_wp--; + return res; +} + +static int +aexpr(n) + enum token n; +{ + int res; + + res = nexpr(n); + if (t_lex(*++t_wp) == BAND) + return aexpr(t_lex(*++t_wp)) && res; + t_wp--; + return res; +} + +static int +nexpr(n) + enum token n; /* token */ +{ + if (n == UNOT) + return !nexpr(t_lex(*++t_wp)); + return primary(n); +} + +static int +primary(n) + enum token n; +{ + int 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(nm, mode) + 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(s) + 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 int +getn(s) + char *s; +{ + char *p; + long r; + + errno = 0; + r = strtol(s, &p, 10); + + if (errno != 0) + error_msg_and_die("%s: out of range", s); + + while (isspace(*p)) + p++; + + if (*p) + error_msg_and_die("%s: bad number", s); + + return (int) r; +} + +static int +newerf (f1, f2) +char *f1, *f2; +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_mtime > b2.st_mtime); +} + +static int +olderf (f1, f2) +char *f1, *f2; +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_mtime < b2.st_mtime); +} + +static int +equalf (f1, f2) +char *f1, *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 (path, mode) +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) +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 index 000000000..1718da71e --- /dev/null +++ b/busybox/coreutils/touch.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini touch implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int touch_main(int argc, char **argv) +{ + int fd; + int create = TRUE; + + /* Parse options */ + while (--argc > 0 && **(++argv) == '-') { + while (*(++(*argv))) { + switch (**argv) { + case 'c': + create = FALSE; + break; + default: + show_usage(); + } + } + } + + if (argc < 1) { + show_usage(); + } + + while (argc > 0) { + fd = open(*argv, (create == FALSE) ? O_RDWR : O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd < 0) { + if (create == FALSE && errno == ENOENT) + return EXIT_SUCCESS; + else { + perror_msg_and_die("%s", *argv); + } + } + close(fd); + if (utime(*argv, NULL)) { + perror_msg_and_die("%s", *argv); + } + argc--; + argv++; + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/tr.c b/busybox/coreutils/tr.c new file mode 100644 index 000000000..5b7b8d091 --- /dev/null +++ b/busybox/coreutils/tr.c @@ -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 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 +#include +#include +#include +#include +#include "busybox.h" + +/* This must be a #define, since when DODEBUG 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() +{ + 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) + error_msg("%s", 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) + error_msg_and_die("%s", 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++ = 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_BB_BUFFER(output, BUFSIZ); + RESERVE_BB_BUFFER(input, BUFSIZ); + RESERVE_BB_UBUFFER(vector, ASCII+1); + RESERVE_BB_BUFFER(invec, ASCII+1); + RESERVE_BB_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: + 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') + 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[(int)input[i]] = TRUE; + for (i = 0; i < output_length; i++) + outvec[(int)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/tty.c b/busybox/coreutils/tty.c new file mode 100644 index 000000000..4510c2996 --- /dev/null +++ b/busybox/coreutils/tty.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tty implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +extern int tty_main(int argc, char **argv) +{ + char *tty; + + if (argc > 1) { + if (argv[1][0] != '-' || argv[1][1] != 's') + show_usage(); + } else { + tty = ttyname(0); + if (tty) + puts(tty); + else + puts("not a tty"); + } + return(isatty(0) ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/busybox/coreutils/uname.c b/busybox/coreutils/uname.c new file mode 100644 index 000000000..f7e2291a8 --- /dev/null +++ b/busybox/coreutils/uname.c @@ -0,0 +1,156 @@ +/* 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. */ + +/* 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 */ + +/* Busyboxed by Erik Andersen */ + +#include +#include +#include +#include +#include + +#if defined (HAVE_SYSINFO) && defined (HAVE_SYS_SYSTEMINFO_H) +# include +#endif +#include "busybox.h" + +static void print_element(unsigned int mask, char *element); + +/* Values that are bitwise or'd into `toprint'. */ +/* Operating system name. */ +static const int PRINT_SYSNAME = 1; + +/* Node name on a communications network. */ +static const int PRINT_NODENAME = 2; + +/* Operating system release. */ +static const int PRINT_RELEASE = 4; + +/* Operating system version. */ +static const int PRINT_VERSION = 8; + +/* Machine hardware name. */ +static const int PRINT_MACHINE = 16; + + /* Host processor type. */ +static const int PRINT_PROCESSOR = 32; + +/* Mask indicating which elements of the name to print. */ +static unsigned char toprint; + + +int uname_main(int argc, char **argv) +{ + struct utsname name; + char processor[256]; + +#if defined(__sparc__) && defined(__linux__) + char *fake_sparc = getenv("FAKE_SPARC"); +#endif + + toprint = 0; + + /* Parse any options */ + //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv); + while (--argc > 0 && **(++argv) == '-') { + while (*(++(*argv))) { + switch (**argv) { + case 's': + toprint |= PRINT_SYSNAME; + break; + case 'n': + toprint |= PRINT_NODENAME; + break; + case 'r': + toprint |= PRINT_RELEASE; + break; + case 'v': + toprint |= PRINT_VERSION; + break; + case 'm': + toprint |= PRINT_MACHINE; + break; + case 'p': + toprint |= PRINT_PROCESSOR; + break; + case 'a': + toprint = (PRINT_SYSNAME | PRINT_NODENAME | PRINT_RELEASE | + PRINT_PROCESSOR | PRINT_VERSION | + PRINT_MACHINE); + break; + default: + show_usage(); + } + } + } + + if (toprint == 0) + toprint = PRINT_SYSNAME; + + if (uname(&name) == -1) + perror_msg("cannot get system name"); + +#if defined (HAVE_SYSINFO) && defined (SI_ARCHITECTURE) + if (sysinfo(SI_ARCHITECTURE, processor, sizeof(processor)) == -1) + perror_msg("cannot get processor type"); +} + +#else + strcpy(processor, "unknown"); +#endif + +#if defined(__sparc__) && defined(__linux__) + if (fake_sparc != NULL + && (fake_sparc[0] == 'y' + || fake_sparc[0] == 'Y')) strcpy(name.machine, "sparc"); +#endif + + print_element(PRINT_SYSNAME, name.sysname); + print_element(PRINT_NODENAME, name.nodename); + print_element(PRINT_RELEASE, name.release); + print_element(PRINT_VERSION, name.version); + print_element(PRINT_MACHINE, name.machine); + print_element(PRINT_PROCESSOR, processor); + + return EXIT_SUCCESS; +} + +/* If the name element set in MASK is selected for printing in `toprint', + print ELEMENT; then print a space unless it is the last element to + be printed, in which case print a newline. */ + +static void print_element(unsigned int mask, char *element) +{ + if (toprint & mask) { + toprint &= ~mask; + printf("%s%c", element, toprint ? ' ' : '\n'); + } +} diff --git a/busybox/coreutils/uniq.c b/busybox/coreutils/uniq.c new file mode 100644 index 000000000..53e3c64f2 --- /dev/null +++ b/busybox/coreutils/uniq.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini uniq implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by John Beppu + * Rewritten by 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 +#include +#include +#include +#include +#include "busybox.h" + +static int print_count; +static int print_uniq = 1; +static int print_duplicates = 1; + +static void print_line(char *line, int count, FILE *fp) +{ + if ((print_duplicates && count > 1) || (print_uniq && count == 1)) { + if (print_count) + fprintf(fp, "%7d\t%s", count, line); + else + fputs(line, fp); + } +} + +int uniq_main(int argc, char **argv) +{ + FILE *in = stdin, *out = stdout; + char *lastline = NULL, *input; + int opt, count = 0; + + /* parse argv[] */ + while ((opt = getopt(argc, argv, "cdu")) > 0) { + switch (opt) { + case 'c': + print_count = 1; + break; + case 'd': + print_duplicates = 1; + print_uniq = 0; + break; + case 'u': + print_duplicates = 0; + print_uniq = 1; + break; + } + } + + if (argv[optind] != NULL) { + in = xfopen(argv[optind], "r"); + if (argv[optind+1] != NULL) + out = xfopen(argv[optind+1], "w"); + } + + while ((input = get_line_from_file(in)) != NULL) { + if (lastline == NULL || strcmp(input, lastline) != 0) { + print_line(lastline, count, out); + free(lastline); + lastline = input; + count = 0; + } + count++; + } + print_line(lastline, count, out); + free(lastline); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/usleep.c b/busybox/coreutils/usleep.c new file mode 100644 index 000000000..6023bf430 --- /dev/null +++ b/busybox/coreutils/usleep.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini usleep implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "busybox.h" + +extern int usleep_main(int argc, char **argv) +{ + if ((argc < 2) || (**(argv + 1) == '-')) { + show_usage(); + } + + usleep(atoi(*(++argv))); /* return void */ + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/uudecode.c b/busybox/coreutils/uudecode.c new file mode 100644 index 000000000..a4059ddfe --- /dev/null +++ b/busybox/coreutils/uudecode.c @@ -0,0 +1,353 @@ +/* uudecode.c -- uudecode utility. + * Copyright (C) 1994, 1995 Free Software Foundation, Inc. + * + * This product is free software; you can redistribute 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 product is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this product; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Reworked to GNU style by Ian Lance Taylor, ian@airs.com, August 93. + * + * Original copyright notice is retained at the end of this file. + */ + + + +#include +#include +#include +#include +#include +#include "busybox.h" +#include "pwd_grp/pwd.h" +#include "pwd_grp/grp.h" + +/*struct passwd *getpwnam();*/ + +/* Single character decode. */ +#define DEC(Char) (((Char) - ' ') & 077) + +static int read_stduu (const char *inname) +{ + char buf[2 * BUFSIZ]; + + while (1) { + int n; + char *p; + + if (fgets (buf, sizeof(buf), stdin) == NULL) { + error_msg("%s: Short file", inname); + return FALSE; + } + p = buf; + + /* N is used to avoid writing out all the characters at the end of + the file. */ + n = DEC (*p); + if (n <= 0) + break; + for (++p; n > 0; p += 4, n -= 3) { + char ch; + + if (n >= 3) { + ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4; + putchar (ch); + ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2; + putchar (ch); + ch = DEC (p[2]) << 6 | DEC (p[3]); + putchar (ch); + } else { + if (n >= 1) { + ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4; + putchar (ch); + } + if (n >= 2) { + ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2; + putchar (ch); + } + } + } + } + + if (fgets (buf, sizeof(buf), stdin) == NULL + || strcmp (buf, "end\n")) { + error_msg("%s: No `end' line", inname); + return FALSE; + } + + return TRUE; +} + +static int read_base64 (const char *inname) +{ + static const char b64_tab[256] = { + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*000-007*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*010-017*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*020-027*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*030-037*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*040-047*/ + '\177', '\177', '\177', '\76', '\177', '\177', '\177', '\77', /*050-057*/ + '\64', '\65', '\66', '\67', '\70', '\71', '\72', '\73', /*060-067*/ + '\74', '\75', '\177', '\177', '\177', '\100', '\177', '\177', /*070-077*/ + '\177', '\0', '\1', '\2', '\3', '\4', '\5', '\6', /*100-107*/ + '\7', '\10', '\11', '\12', '\13', '\14', '\15', '\16', /*110-117*/ + '\17', '\20', '\21', '\22', '\23', '\24', '\25', '\26', /*120-127*/ + '\27', '\30', '\31', '\177', '\177', '\177', '\177', '\177', /*130-137*/ + '\177', '\32', '\33', '\34', '\35', '\36', '\37', '\40', /*140-147*/ + '\41', '\42', '\43', '\44', '\45', '\46', '\47', '\50', /*150-157*/ + '\51', '\52', '\53', '\54', '\55', '\56', '\57', '\60', /*160-167*/ + '\61', '\62', '\63', '\177', '\177', '\177', '\177', '\177', /*170-177*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*200-207*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*210-217*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*220-227*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*230-237*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*240-247*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*250-257*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*260-267*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*270-277*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*300-307*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*310-317*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*320-327*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*330-337*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*340-347*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*350-357*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*360-367*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*370-377*/ + }; + unsigned char buf[2 * BUFSIZ]; + + while (1) { + int last_data = 0; + unsigned char *p; + + if (fgets (buf, sizeof(buf), stdin) == NULL) { + error_msg("%s: Short file", inname); + return FALSE; + } + p = buf; + + if (memcmp (buf, "====", 4) == 0) + break; + if (last_data != 0) { + error_msg("%s: data following `=' padding character", inname); + return FALSE; + } + + /* The following implementation of the base64 decoding might look + a bit clumsy but I only try to follow the POSIX standard: + ``All line breaks or other characters not found in the table + [with base64 characters] shall be ignored by decoding + software.'' */ + while (*p != '\n') { + char c1, c2, c3; + + while ((b64_tab[*p] & '\100') != 0) + if (*p == '\n' || *p++ == '=') + break; + if (*p == '\n') + /* This leaves the loop. */ + continue; + c1 = b64_tab[*p++]; + + while ((b64_tab[*p] & '\100') != 0) + if (*p == '\n' || *p++ == '=') { + error_msg("%s: illegal line", inname); + return FALSE; + } + c2 = b64_tab[*p++]; + + while (b64_tab[*p] == '\177') + if (*p++ == '\n') { + error_msg("%s: illegal line", inname); + return FALSE; + } + if (*p == '=') { + putchar (c1 << 2 | c2 >> 4); + last_data = 1; + break; + } + c3 = b64_tab[*p++]; + + while (b64_tab[*p] == '\177') + if (*p++ == '\n') { + error_msg("%s: illegal line", inname); + return FALSE; + } + putchar (c1 << 2 | c2 >> 4); + putchar (c2 << 4 | c3 >> 2); + if (*p == '=') { + last_data = 1; + break; + } + else + putchar (c3 << 6 | b64_tab[*p++]); + } + } + + return TRUE; +} + +static int decode (const char *inname, + const char *forced_outname) +{ + struct passwd *pw; + register char *p; + int mode; + char buf[2 * BUFSIZ]; + char *outname; + int do_base64 = 0; + int res; + int dofre; + + /* Search for header line. */ + + while (1) { + if (fgets (buf, sizeof (buf), stdin) == NULL) { + error_msg("%s: No `begin' line", inname); + return FALSE; + } + + if (strncmp (buf, "begin", 5) == 0) { + if (sscanf (buf, "begin-base64 %o %s", &mode, buf) == 2) { + do_base64 = 1; + break; + } else if (sscanf (buf, "begin %o %s", &mode, buf) == 2) + break; + } + } + + /* If the output file name is given on the command line this rules. */ + dofre = FALSE; + if (forced_outname != NULL) + outname = (char *) forced_outname; + else { + /* Handle ~user/file format. */ + if (buf[0] != '~') + outname = buf; + else { + p = buf + 1; + while (*p != '/') + ++p; + if (*p == '\0') { + error_msg("%s: Illegal ~user", inname); + return FALSE; + } + *p++ = '\0'; + pw = getpwnam (buf + 1); + if (pw == NULL) { + error_msg("%s: No user `%s'", inname, buf + 1); + return FALSE; + } + outname = concat_path_file(pw->pw_dir, p); + dofre = TRUE; + } + } + + /* Create output file and set mode. */ + if (strcmp (outname, "/dev/stdout") != 0 && strcmp (outname, "-") != 0 + && (freopen (outname, "w", stdout) == NULL + || chmod (outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) + )) { + perror_msg("%s", outname); /* */ + if (dofre) + free(outname); + return FALSE; + } + + /* We differenciate decoding standard UU encoding and base64. A + common function would only slow down the program. */ + + /* For each input line: */ + if (do_base64) + res = read_base64 (inname); + else + res = read_stduu (inname); + if (dofre) + free(outname); + return res; +} + +int uudecode_main (int argc, + char **argv) +{ + int opt; + int exit_status; + const char *outname; + outname = NULL; + + while ((opt = getopt(argc, argv, "o:")) != EOF) { + switch (opt) { + case 0: + break; + + case 'o': + outname = optarg; + break; + + default: + show_usage(); + } + } + + if (optind == argc) + exit_status = decode ("stdin", outname) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + else { + exit_status = EXIT_SUCCESS; + do { + if (freopen (argv[optind], "r", stdin) != NULL) { + if (decode (argv[optind], outname) != 0) + exit_status = FALSE; + } else { + perror_msg("%s", argv[optind]); + exit_status = EXIT_FAILURE; + } + optind++; + } + while (optind < argc); + } + return(exit_status); +} + +/* Copyright (c) 1983 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. + * + * 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/uuencode.c b/busybox/coreutils/uuencode.c new file mode 100644 index 000000000..fc037403a --- /dev/null +++ b/busybox/coreutils/uuencode.c @@ -0,0 +1,180 @@ +/* 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 +#include +#include +#include +#include +#include +#include "busybox.h" + +/* Conversion table. for base 64 */ +static char tbl_base64[64] = { + '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', '+', '/' +}; + +static char tbl_std[64] = { + '`', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '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', '[', '\\', ']', '^', '_' +}; + +/* + * 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 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) = '='; + } + else if (i == length + 2) { + *(p - 1) = *(p - 2) = '='; + } + /* ...and zero-terminate it. */ + *p = '\0'; +} + +int uuencode_main(int argc, char **argv) +{ + const int src_buf_size = 60; // This *MUST* be a multiple of 3 + const int dst_buf_size = 4 * ((src_buf_size + 2) / 3); + RESERVE_BB_BUFFER(src_buf, src_buf_size + 1); + RESERVE_BB_BUFFER(dst_buf, dst_buf_size + 1); + struct stat stat_buf; + FILE *src_stream = stdin; + char *tbl = tbl_std; + size_t size; + mode_t mode; + int opt; + int column = 0; + int write_size = 0; + int remaining; + int buffer_offset = 0; + + while ((opt = getopt(argc, argv, "m")) != -1) { + switch (opt) { + case 'm': + tbl = tbl_base64; + break; + default: + show_usage(); + } + } + + switch (argc - optind) { + case 2: + src_stream = xfopen(argv[optind], "r"); + stat(argv[optind], &stat_buf); + mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + if (src_stream == stdout) { + printf("NULL\n"); + } + break; + case 1: + mode = 0666 & ~umask(0666); + break; + default: + show_usage(); + } + + 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) { + /* Encode the buffer we just read in */ + uuencode(src_buf, dst_buf, size, tbl); + + /* Write the buffer to stdout, wrapping at 60 chars. + * This looks overly complex, but it gets tricky as + * the line has to continue to wrap correctly if we + * have to refill the buffer + * + * Improvments most welcome + */ + + /* Initialise values for the new buffer */ + remaining = 4 * ((size + 2) / 3); + buffer_offset = 0; + + /* Write the buffer to stdout, wrapping at 60 chars + * starting from the column the last buffer ran out + */ + do { + if (remaining > (60 - column)) { + write_size = 60 - column; + } + else if (remaining < 60) { + write_size = remaining; + } else { + write_size = 60; + } + + /* Setup a new row if required */ + if (column == 0) { + putchar('\n'); + if (tbl == tbl_std) { + putchar('M'); + } + } + /* Write to the 60th column */ + if (fwrite(&dst_buf[buffer_offset], 1, write_size, stdout) != write_size) { + perror("Couldnt finish writing"); + } + /* Update variables based on last write */ + buffer_offset += write_size; + remaining -= write_size; + column += write_size; + if (column % 60 == 0) { + column = 0; + } + } while (remaining > 0); + } + printf(tbl == tbl_std ? "\n`\nend\n" : "\n====\n"); + + return(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/wc.c b/busybox/coreutils/wc.c new file mode 100644 index 000000000..695e7e7d4 --- /dev/null +++ b/busybox/coreutils/wc.c @@ -0,0 +1,156 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini wc implementation for busybox + * + * Copyright (C) 2000 Edward Betts + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +static int total_lines, total_words, total_chars, max_length; +static int print_lines, print_words, print_chars, print_length; + +static void print_counts(int lines, int words, int chars, int length, + const char *name) +{ + char const *space = ""; + + if (print_lines) { + printf("%7d", lines); + space = " "; + } + if (print_words) { + printf("%s%7d", space, words); + space = " "; + } + if (print_chars) { + printf("%s%7d", space, chars); + space = " "; + } + if (print_length) + printf("%s%7d", space, length); + if (*name) + printf(" %s", name); + putchar('\n'); +} + +static void wc_file(FILE * file, const char *name) +{ + int lines, words, chars, length; + int in_word = 0, linepos = 0; + int c; + + lines = words = chars = length = 0; + while ((c = getc(file)) != EOF) { + chars++; + switch (c) { + case '\n': + lines++; + case '\r': + case '\f': + if (linepos > length) + length = linepos; + linepos = 0; + goto word_separator; + case '\t': + linepos += 8 - (linepos % 8); + goto word_separator; + case ' ': + linepos++; + case '\v': + word_separator: + if (in_word) { + in_word = 0; + words++; + } + break; + default: + linepos++; + in_word = 1; + break; + } + } + if (linepos > length) + length = linepos; + if (in_word) + words++; + print_counts(lines, words, chars, length, name); + total_lines += lines; + total_words += words; + total_chars += chars; + if (length > max_length) + max_length = length; + fclose(file); + fflush(stdout); +} + +int wc_main(int argc, char **argv) +{ + FILE *file; + unsigned int num_files_counted = 0; + int opt, status = EXIT_SUCCESS; + + total_lines = total_words = total_chars = max_length = 0; + print_lines = print_words = print_chars = print_length = 0; + + while ((opt = getopt(argc, argv, "clLw")) > 0) { + switch (opt) { + case 'c': + print_chars = 1; + break; + case 'l': + print_lines = 1; + break; + case 'L': + print_length = 1; + break; + case 'w': + print_words = 1; + break; + default: + show_usage(); + } + } + + if (!print_lines && !print_words && !print_chars && !print_length) + print_lines = print_words = print_chars = 1; + + if (argv[optind] == NULL || strcmp(argv[optind], "-") == 0) { + wc_file(stdin, ""); + return EXIT_SUCCESS; + } else { + while (optind < argc) { + if ((file = wfopen(argv[optind], "r")) != NULL) + wc_file(file, argv[optind]); + else + status = EXIT_FAILURE; + num_files_counted++; + optind++; + } + } + + if (num_files_counted > 1) + print_counts(total_lines, total_words, total_chars, + max_length, "total"); + + return status; +} diff --git a/busybox/coreutils/whoami.c b/busybox/coreutils/whoami.c new file mode 100644 index 000000000..c3b1140e6 --- /dev/null +++ b/busybox/coreutils/whoami.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini whoami implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int whoami_main(int argc, char **argv) +{ + char user[9]; + uid_t uid = geteuid(); + + if (argc > 1) + show_usage(); + + my_getpwuid(user, uid); + if (*user) { + puts(user); + return EXIT_SUCCESS; + } + error_msg_and_die("cannot find username for UID %u", (unsigned) uid); +} diff --git a/busybox/coreutils/yes.c b/busybox/coreutils/yes.c new file mode 100644 index 000000000..7d9596d0b --- /dev/null +++ b/busybox/coreutils/yes.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini yes implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "busybox.h" + +extern int yes_main(int argc, char **argv) +{ + int i; + + if (argc >= 2 && *argv[1] == '-') + show_usage(); + + if (argc == 1) { + while (1) + if (puts("y") == EOF) { + perror("yes"); + return EXIT_FAILURE; + } + } + + while (1) + for (i = 1; i < argc; i++) + if (fputs(argv[i], stdout) == EOF + || putchar(i == argc - 1 ? '\n' : ' ') == EOF) { + perror("yes"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/busybox/cp.c b/busybox/cp.c new file mode 100644 index 000000000..82d43adff --- /dev/null +++ b/busybox/cp.c @@ -0,0 +1,114 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cp implementation for busybox + * + * + * Copyright (C) 2000 by 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 +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +extern int cp_main(int argc, char **argv) +{ + int status = 0; + int opt; + int flags = 0; + int i; + + while ((opt = getopt(argc, argv, "adfipR")) != -1) + switch (opt) { + case 'a': + flags |= FILEUTILS_PRESERVE_STATUS | FILEUTILS_RECUR; + /* fallthrough */ + case 'd': + flags |= FILEUTILS_PRESERVE_SYMLINKS; + break; + case 'f': + flags |= FILEUTILS_FORCE; + break; + case 'i': + flags |= FILEUTILS_INTERACTIVE; + break; + case 'p': + flags |= FILEUTILS_PRESERVE_STATUS; + break; + case 'R': + flags |= FILEUTILS_RECUR; + break; + default: + show_usage(); + } + + if (optind + 2 > argc) + show_usage(); + + /* If there are only two arguments and... */ + if (optind + 2 == argc) { + struct stat source_stat; + struct stat dest_stat; + int source_exists = 1; + int dest_exists = 1; + + if (((flags & FILEUTILS_PRESERVE_SYMLINKS) && + lstat(argv[optind], &source_stat) < 0) || + (!(flags & FILEUTILS_PRESERVE_SYMLINKS) && + stat(argv[optind], &source_stat))) { + if (errno != ENOENT) + perror_msg_and_die("unable to stat `%s'", argv[optind]); + source_exists = 0; + } + + if (stat(argv[optind + 1], &dest_stat) < 0) { + if (errno != ENOENT) + perror_msg_and_die("unable to stat `%s'", argv[optind + 1]); + dest_exists = 0; + } + + /* ...if neither is a directory or... */ + if (((!source_exists || !S_ISDIR(source_stat.st_mode)) && + (!dest_exists || !S_ISDIR(dest_stat.st_mode))) || + /* ...recursing, the first is a directory, and the + * second doesn't exist, then... */ + ((flags & FILEUTILS_RECUR) && S_ISDIR(source_stat.st_mode) && + !dest_exists)) { + /* ...do a simple copy. */ + if (copy_file(argv[optind], argv[optind + 1], flags) < 0) + status = 1; + return status; + } + } + + for (i = optind; i < argc - 1; i++) { + char *dest = concat_path_file(argv[argc - 1], + get_last_path_component(argv[i])); + if (copy_file(argv[i], dest, flags) < 0) + status = 1; + free(dest); + } + + return status; +} diff --git a/busybox/cpio.c b/busybox/cpio.c new file mode 100644 index 000000000..7f68715eb --- /dev/null +++ b/busybox/cpio.c @@ -0,0 +1,95 @@ +/* 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 +#include +#include +#include +#include +#include "busybox.h" + +extern int cpio_main(int argc, char **argv) +{ + FILE *src_stream = stdin; + char **extract_names = NULL; + int extract_function = 0; + int num_of_entries = 0; + int opt = 0; + mode_t oldmask = 0; + + while ((opt = getopt(argc, argv, "idmuvtF:")) != -1) { + switch (opt) { + case 'i': // extract + extract_function |= extract_all_to_fs; + break; + case 'd': // create _leading_ directories + extract_function |= extract_create_leading_dirs; + oldmask = umask(077); /* Make make_directory act like GNU cpio */ + break; + case 'm': // preserve modification time + extract_function |= extract_preserve_date; + break; + case 'v': // verbosly list files + extract_function |= extract_verbose_list; + break; + case 'u': // unconditional + extract_function |= extract_unconditional; + break; + case 't': // list files + extract_function |= extract_list; + break; + case 'F': + src_stream = xfopen(optarg, "r"); + break; + default: + show_usage(); + } + } + + if ((extract_function & extract_all_to_fs) && (extract_function & extract_list)) { + extract_function ^= extract_all_to_fs; /* If specify t, don't extract*/ + } + + if ((extract_function & extract_all_to_fs) && (extract_function & extract_verbose_list)) { + /* The meaning of v changes on extract */ + extract_function ^= extract_verbose_list; + extract_function |= extract_list; + } + + while (optind < argc) { + extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); + extract_names[num_of_entries] = xstrdup(argv[optind]); + num_of_entries++; + extract_names[num_of_entries] = NULL; + optind++; + } + + unarchive(src_stream, stdout, &get_header_cpio, extract_function, "./", extract_names); + if (oldmask) { + umask(oldmask); /* Restore umask if we changed it */ + } + return EXIT_SUCCESS; +} + diff --git a/busybox/cut.c b/busybox/cut.c new file mode 100644 index 000000000..3ed264870 --- /dev/null +++ b/busybox/cut.c @@ -0,0 +1,356 @@ +/* + * cut.c - minimalist version of cut + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include /* getopt */ +#include +#include +#include "busybox.h" + + +/* globals from other files */ +extern int optind; +extern char *optarg; + + +/* option vars */ +static char part = 0; /* (b)yte, (c)har, (f)ields */ +static unsigned int supress_non_delimited_lines = 0; +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 seperated 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 seperated 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(<ok, "-"); + 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) + 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(<ok, "-"); + if (ntok == NULL) { + e = NON_RANGE; + } else if (strlen(ntok) == 0) { + e = EOL; + } else { + e = strtoul(ntok, &junk, 10); + if(*junk != '\0' || e < 0) + 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) + 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) + 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 = get_line_from_file(file)) != NULL) { + chomp(line); + + /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ + if (part == 'c' || part == 'b') + cut_line_by_chars(line); + + /* cut based on fields */ + else if (part == 'f') { + 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) +{ + int opt; + + while ((opt = getopt(argc, argv, "b:c:d:f:ns")) > 0) { + switch (opt) { + case 'b': + case 'c': + case 'f': + /* make sure they didn't ask for two types of lists */ + if (part != 0) { + error_msg_and_die("only one type of list may be specified"); + } + part = (char)opt; + parse_lists(optarg); + break; + case 'd': + if (strlen(optarg) > 1) { + error_msg_and_die("the delimiter must be a single character"); + } + delim = optarg[0]; + break; + case 'n': + /* no-op */ + break; + case 's': + supress_non_delimited_lines++; + break; + } + } + + if (part == 0) { + error_msg_and_die("you must specify a list of bytes, characters, or fields"); + } + + /* non-field (char or byte) cutting has some special handling */ + if (part != 'f') { + if (supress_non_delimited_lines) { + error_msg_and_die("suppressing non-delimited lines makes sense" + " only when operating on fields"); + } + if (delim != '\t' && part != 'f') { + 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 = wfopen(argv[i], "r"); + if(file) { + cut_file(file); + fclose(file); + } + } + } + + return EXIT_SUCCESS; +} diff --git a/busybox/date.c b/busybox/date.c new file mode 100644 index 000000000..6db3e2838 --- /dev/null +++ b/busybox/date.c @@ -0,0 +1,247 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini date implementation for busybox + * + * by Matthew Grant + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#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 suprising some people */ + +static struct tm *date_conv_time(struct tm *tm_time, const char *t_string) +{ + int nr; + + 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) { + error_msg_and_die(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) { + + t.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + + } 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) { + + 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:%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 { + error_msg_and_die(invalid_date, t_string); + } + *tm_time = t; + return (tm_time); +} + + +int date_main(int argc, char **argv) +{ + char *date_str = NULL; + char *date_fmt = NULL; + char *t_buff; + int c; + int set_time = 0; + int rfc822 = 0; + int utc = 0; + int use_arg = 0; + time_t tm; + struct tm tm_time; + + /* Interpret command line args */ + while ((c = getopt(argc, argv, "Rs:ud:")) != EOF) { + switch (c) { + case 'R': + rfc822 = 1; + break; + case 's': + set_time = 1; + if ((date_str != NULL) || ((date_str = optarg) == NULL)) { + show_usage(); + } + break; + case 'u': + utc = 1; + if (putenv("TZ=UTC0") != 0) + error_msg_and_die(memory_exhausted); + break; + case 'd': + use_arg = 1; + if ((date_str != NULL) || ((date_str = optarg) == NULL)) + show_usage(); + break; + default: + show_usage(); + } + } + + 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]; + } +#if 0 + else { + error_msg("date_str='%s' date_fmt='%s'\n", date_str, date_fmt); + show_usage(); + } +#endif + + /* Now we have parsed all the information except the date format + which depends on whether the clock is being set or read */ + + 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 (date_str != NULL) { + + 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 = mktime(&tm_time); + if (tm < 0) + error_msg_and_die(invalid_date, date_str); + if ( utc ) { + if (putenv("TZ=UTC0") != 0) + error_msg_and_die(memory_exhausted); + } + + /* if setting time, set it */ + if (set_time) { + if (stime(&tm) < 0) { + perror_msg("cannot set date"); + } + } + } + + /* Display output */ + + /* Deal with format string */ + if (date_fmt == NULL) { + date_fmt = (rfc822 + ? (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"); + + } 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/dc.c b/busybox/dc.c new file mode 100644 index 000000000..8d7a92a28 --- /dev/null +++ b/busybox/dc.c @@ -0,0 +1,182 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */ + +static double stack[100]; +static unsigned int pointer; + +static void push(double a) +{ + if (pointer >= (sizeof(stack) / sizeof(*stack))) + error_msg_and_die("stack overflow"); + stack[pointer++] = a; +} + +static double pop() +{ + if (pointer == 0) + error_msg_and_die("stack underflow"); + return stack[--pointer]; +} + +static void add() +{ + push(pop() + pop()); +} + +static void sub() +{ + double subtrahend = pop(); + + push(pop() - subtrahend); +} + +static void mul() +{ + push(pop() * pop()); +} + +static void divide() +{ + double divisor = pop(); + + push(pop() / divisor); +} + +static void and() +{ + push((unsigned int) pop() & (unsigned int) pop()); +} + +static void or() +{ + push((unsigned int) pop() | (unsigned int) pop()); +} + +static void eor() +{ + push((unsigned int) pop() ^ (unsigned int) pop()); +} + +static void not() +{ + push(~(unsigned int) pop()); +} + +static void print() +{ + printf("%g\n", pop()); +} + +struct op { + const char *name; + void (*function) (); +}; + +static const struct op operators[] = { + {"+", add}, + {"add", add}, + {"-", sub}, + {"sub", sub}, + {"*", mul}, + {"mul", mul}, + {"/", divide}, + {"div", divide}, + {"and", and}, + {"or", or}, + {"not", not}, + {"eor", eor}, + {0, 0} +}; + +static void stack_machine(const char *argument) +{ + char *endPointer = 0; + double d; + const struct op *o = operators; + + if (argument == 0) { + print(); + 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++; + } + 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 = get_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]=='-') + show_usage(); + while (argc >= 2) { + stack_machine(argv[1]); + argv++; + argc--; + } + } + stack_machine(0); + return EXIT_SUCCESS; +} diff --git a/busybox/dd.c b/busybox/dd.c new file mode 100644 index 000000000..d46db82a0 --- /dev/null +++ b/busybox/dd.c @@ -0,0 +1,154 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dd implementation for busybox + * + * + * Copyright (C) 2000 by 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 +#include +#include +#include +#include +#include +#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) +{ + int i, ifd, ofd, oflag, sync_flag = FALSE, trunc = TRUE; + size_t in_full = 0, in_part = 0, out_full = 0, out_part = 0; + size_t bs = 512, count = -1; + ssize_t n; + off_t seek = 0, skip = 0; + char *infile = NULL, *outfile = NULL, *buf; + + for (i = 1; i < argc; i++) { + if (strncmp("bs=", argv[i], 3) == 0) + bs = parse_number(argv[i]+3, dd_suffixes); + else if (strncmp("count=", argv[i], 6) == 0) + count = parse_number(argv[i]+6, dd_suffixes); + else if (strncmp("seek=", argv[i], 5) == 0) + seek = parse_number(argv[i]+5, dd_suffixes); + else if (strncmp("skip=", argv[i], 5) == 0) + skip = parse_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 = FALSE; + buf += 7; + } else if (strncmp("sync", buf, 4) == 0) { + sync_flag = TRUE; + buf += 4; + } else { + error_msg_and_die("invalid conversion `%s'", argv[i]+5); + } + if (buf[0] == '\0') + break; + if (buf[0] == ',') + buf++; + } + } else + show_usage(); + } + + buf = xmalloc(bs); + + if (infile != NULL) { + if ((ifd = open(infile, O_RDONLY)) < 0) + perror_msg_and_die("%s", infile); + } else { + ifd = STDIN_FILENO; + infile = "standard input"; + } + + if (outfile != NULL) { + oflag = O_WRONLY | O_CREAT; + + if (!seek && trunc) + oflag |= O_TRUNC; + + if ((ofd = open(outfile, oflag, 0666)) < 0) + perror_msg_and_die("%s", outfile); + + if (seek && trunc) { + if (ftruncate(ofd, seek * bs) < 0) + perror_msg_and_die("%s", outfile); + } + } else { + ofd = STDOUT_FILENO; + outfile = "standard output"; + } + + if (skip) { + if (lseek(ifd, skip * bs, SEEK_CUR) < 0) + perror_msg_and_die("%s", infile); + } + + if (seek) { + if (lseek(ofd, seek * bs, SEEK_CUR) < 0) + perror_msg_and_die("%s", outfile); + } + + while (in_full + in_part != count) { + n = safe_read(ifd, buf, bs); + if (n < 0) + 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 = full_write(ofd, buf, n); + if (n < 0) + perror_msg_and_die("%s", outfile); + if (n == bs) + out_full++; + else + out_part++; + } + + fprintf(stderr, "%ld+%ld records in\n", (long)in_full, (long)in_part); + fprintf(stderr, "%ld+%ld records out\n", (long)out_full, (long)out_part); + + return EXIT_SUCCESS; +} diff --git a/busybox/deallocvt.c b/busybox/deallocvt.c new file mode 100644 index 000000000..15cd0c9b9 --- /dev/null +++ b/busybox/deallocvt.c @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * disalloc.c - aeb - 940501 - Disallocate virtual terminal(s) + * Renamed deallocvt. + */ +#include +#include +#include +#include +#include +#include "busybox.h" + +/* From */ +static const int VT_DISALLOCATE = 0x5608; /* free memory associated to vt */ + +int deallocvt_main(int argc, char *argv[]) +{ + int fd, num, i; + + //if ((argc > 2) || ((argv == 2) && (**(argv + 1) == '-'))) + if (argc > 2) + show_usage(); + + fd = get_console_fd("/dev/console"); + + if (argc == 1) { + /* deallocate all unused consoles */ + if (ioctl(fd, VT_DISALLOCATE, 0)) + perror_msg_and_die("VT_DISALLOCATE"); + } else { + for (i = 1; i < argc; i++) { + num = atoi(argv[i]); + if (num == 0) + error_msg("0: illegal VT number"); + else if (num == 1) + error_msg("VT 1 cannot be deallocated"); + else if (ioctl(fd, VT_DISALLOCATE, num)) + perror_msg_and_die("VT_DISALLOCATE"); + } + } + + return EXIT_SUCCESS; +} diff --git a/busybox/debian/Config.h-deb b/busybox/debian/Config.h-deb new file mode 100644 index 000000000..892ce1303 --- /dev/null +++ b/busybox/debian/Config.h-deb @@ -0,0 +1,473 @@ +/* vi: set sw=4 ts=4: */ +// This file defines the feature set to be compiled into busybox. +// When you turn things off here, they won't be compiled in at all. +// +//// This file is parsed by sed. You MUST use single line comments. +// i.e., //#define BB_BLAH +// +// +// BusyBox Applications +//#define BB_ADJTIMEX +#define BB_AR +//#define BB_ASH +#define BB_BASENAME +#define BB_CAT +#define BB_CHGRP +#define BB_CHMOD +#define BB_CHOWN +#define BB_CHROOT +#define BB_CHVT +#define BB_CLEAR +//#define BB_CMP +#define BB_CP +//#define BB_CPIO +#define BB_CUT +#define BB_DATE +//#define BB_DC +#define BB_DD +//#define BB_DEALLOCVT +#define BB_DF +#define BB_DIRNAME +#define BB_DMESG +//#define BB_DOS2UNIX +//#define BB_DPKG +//#define BB_DPKG_DEB +//#define BB_DUTMP +#define BB_DU +//#define BB_DUMPKMAP +#define BB_ECHO +#define BB_ENV +#define BB_EXPR +//#define BB_FBSET +//#define BB_FDFLUSH +#define BB_FIND +#define BB_FREE +//#define BB_FREERAMDISK +//#define BB_FSCK_MINIX +//#define BB_GETOPT +#define BB_GREP +#define BB_GUNZIP +#define BB_GZIP +#define BB_HALT +#define BB_HEAD +//#define BB_HOSTID +//#define BB_HOSTNAME +//#define BB_HUSH +#define BB_ID +//#define BB_IFCONFIG +#define BB_INIT +//#define BB_INSMOD +#define BB_KILL +#define BB_KILLALL +#define BB_KLOGD +//#define BB_LASH +//#define BB_LENGTH +#define BB_LN +//#define BB_LOADACM +//#define BB_LOADFONT +#define BB_LOADKMAP +#define BB_LOGGER +//#define BB_LOGNAME +#define BB_LS +#define BB_LSMOD +//#define BB_MAKEDEVS +#define BB_MD5SUM +#define BB_MKDIR +//#define BB_MKFIFO +//#define BB_MKFS_MINIX +#define BB_MKNOD +#define BB_MKSWAP +//#define BB_MKTEMP +//#define BB_MODPROBE +#define BB_MORE +#define BB_MOUNT +//#define BB_MSH +//#define BB_MT +#define BB_MV +//#define BB_NC +//#define BB_NSLOOKUP +//#define BB_PIDOF +#define BB_PING +//#define BB_PIVOT_ROOT +#define BB_POWEROFF +//#define BB_PRINTF +#define BB_PS +#define BB_PWD +//#define BB_RDATE +//#define BB_READLINK +#define BB_REBOOT +//#define BB_RENICE +#define BB_RESET +#define BB_RM +#define BB_RMDIR +//#define BB_RMMOD +//#define BB_ROUTE +//#define BB_RPM2CPIO +#define BB_SED +//#define BB_SETKEYCODES +#define BB_SLEEP +#define BB_SORT +//#define BB_STTY +#define BB_SWAPONOFF +#define BB_SYNC +#define BB_SYSLOGD +#define BB_TAIL +#define BB_TAR +//#define BB_TEE +//#define BB_TEST +#define BB_TELNET +//#define BB_TFTP +#define BB_TOUCH +#define BB_TR +//#define BB_TRACEROUTE +#define BB_TRUE_FALSE +#define BB_TTY +//#define BB_UNIX2DOS +//#define BB_UUENCODE +//#define BB_UUDECODE +#define BB_UMOUNT +#define BB_UNIQ +#define BB_UNAME +//#define BB_UPDATE +#define BB_UPTIME +//#define BB_USLEEP +#define BB_VI +//#define BB_WATCHDOG +#define BB_WC +#define BB_WGET +#define BB_WHICH +#define BB_WHOAMI +#define BB_XARGS +#define BB_YES +// End of Applications List +// +// +// +// --------------------------------------------------------- +// This is where feature definitions go. Generally speaking, +// turning this stuff off makes things a bit smaller (and less +// pretty/useful). +// +// +// If you enabled one or more of the shells, you may select which one +// should be run when sh is invoked: +//#define BB_FEATURE_SH_IS_ASH +//#define BB_FEATURE_SH_IS_HUSH +//#define BB_FEATURE_SH_IS_LASH +#define BB_FEATURE_SH_IS_MSH +// +// BusyBox will, by default, malloc space for its buffers. This costs code +// size for the call to xmalloc. You can use the following feature to have +// them put on the stack. For some very small machines with limited stack +// space, this can be deadly. For most folks, this works just fine... +//#define BB_FEATURE_BUFFERS_GO_ON_STACK +// The third alternative for buffer allocation is to use 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. +//#define BB_FEATURE_BUFFERS_GO_IN_BSS +// +// Turn this on to use Erik's very cool devps, and devmtab kernel drivers, +// thereby eliminating the need for the /proc filesystem and thereby saving +// lots and lots memory for more important things. NOTE: If you enable this +// feature, you _must_ have patched the kernel to include the devps patch that +// is included in the busybox/kernel-patches directory. You will also need to +// create some device special files in /dev on your embedded system: +// mknod /dev/mtab c 10 22 +// mknod /dev/ps c 10 21 +// I emailed Linus and this patch will not be going into the stock kernel. +//#define BB_FEATURE_USE_DEVPS_PATCH +// +// show verbose usage messages +//#define BB_FEATURE_VERBOSE_USAGE +// +// Use termios to manipulate the screen ('more' is prettier with this on) +#define BB_FEATURE_USE_TERMIOS +// +// calculate terminal & column widths (for more and ls) +#define BB_FEATURE_AUTOWIDTH +// +// show username/groupnames for ls +#define BB_FEATURE_LS_USERNAME +// +// show file timestamps in ls +#define BB_FEATURE_LS_TIMESTAMPS +// +// enable ls -p and -F +#define BB_FEATURE_LS_FILETYPES +// +// sort the file names +#define BB_FEATURE_LS_SORTFILES +// +// enable ls -R +#define BB_FEATURE_LS_RECURSIVE +// +// enable ls -L +#define BB_FEATURE_LS_FOLLOWLINKS +// +// Disable for a smaller (but less functional) ping +#define BB_FEATURE_FANCY_PING +// +// Make init use a simplified /etc/inittab file (recommended). +#define BB_FEATURE_USE_INITTAB +// +//Enable init being called as /linuxrc +#define BB_FEATURE_LINUXRC +// +//Have init enable core dumping for child processes (for debugging only) +//#define BB_FEATURE_INIT_COREDUMPS +// +//Make sure nothing is printed to the console on boot +//#define BB_FEATURE_EXTRA_QUIET +// +// enable syslogd -R remotehost +#define BB_FEATURE_REMOTE_LOG +// +// enable syslogd -C +//#define BB_FEATURE_IPC_SYSLOG +// +//Disable for a simple tail implementation (2.34k vs 3k for the full one). +//Both provide 'tail -f', but this cuts out -c, -q, -s, and -v. +#define BB_FEATURE_FANCY_TAIL +// +// Enable support for loop devices in mount +#define BB_FEATURE_MOUNT_LOOP +// +// Enable support for a real /etc/mtab file instead of /proc/mounts +//#define BB_FEATURE_MTAB_SUPPORT +// +// Enable support for mounting remote NFS volumes. +// You may need to mount with "-o nolock" if you are +// not running a local portmapper daemon... +#define BB_FEATURE_NFSMOUNT +// +// Enable support forced filesystem unmounting +// (i.e., in case of an unreachable NFS system). +#define BB_FEATURE_MOUNT_FORCE +// +// Enable support for creation of tar files. +#define BB_FEATURE_TAR_CREATE +// +// Enable support for "--exclude" and "-X" for excluding files +#define BB_FEATURE_TAR_EXCLUDE +// +// Enable support for tar -z option (currently only works for inflating) +#define BB_FEATURE_TAR_GZIP +// +// Enable reverse sort +#define BB_FEATURE_SORT_REVERSE +// +// Enable uniqe sort +#define BB_FEATURE_SORT_UNIQUE +// +// Enable command line editing in the shell. +// Only relevant if a shell is enabled. On by default. +#define BB_FEATURE_COMMAND_EDITING +// +// Enable tab completion in the shell. This is now working quite nicely. +// This feature adds a bit over 4k. Only relevant if a shell is enabled. +#define BB_FEATURE_COMMAND_TAB_COMPLETION +// +// Attempts to match usernames in a ~-prefixed path +//#define BB_FEATURE_COMMAND_USERNAME_COMPLETION +// +//Allow the shell to invoke all the compiled in BusyBox applets as if they +//were shell builtins. Nice for staticly linking an emergency rescue shell, +//among other things. Off by default. +// Only relevant if a shell is enabled. +//#define BB_FEATURE_SH_STANDALONE_SHELL +// +//When this is enabled, busybox shell applets can be called using full path +//names. This causes applets (i.e., most busybox commands) to override +//real commands on the filesystem. For example, if you run run /bin/cat, it +//will use BusyBox cat even if /bin/cat exists on the filesystem and is _not_ +//busybox. Some systems want this, others do not. Choose wisely. :-) This +//only has meaning when BB_FEATURE_SH_STANDALONE_SHELL is enabled. +// Only relevant if a shell is enabled. Off by default. +//#define BB_FEATURE_SH_APPLETS_ALWAYS_WIN +// +// Uncomment this option for a fancy shell prompt that includes the +// current username and hostname. On systems that don't have usernames +// or hostnames, this can look hideous. +// Only relevant if a shell is enabled. +//#define BB_FEATURE_SH_FANCY_PROMPT +// +//Turn on extra fbset options +//#define BB_FEATURE_FBSET_FANCY +// +//Turn on fbset readmode support +//#define BB_FEATURE_FBSET_READMODE +// +// Support insmod/lsmod/rmmod for post 2.1 kernels +//#define BB_FEATURE_NEW_MODULE_INTERFACE +// +// Support insmod/lsmod/rmmod for pre 2.1 kernels +//#define BB_FEATURE_OLD_MODULE_INTERFACE +// +// Support module version checking +//#define BB_FEATURE_INSMOD_VERSION_CHECKING +// +// Support for uClinux memory usage optimization, which will load the image +// directly into the kernel memory. This divides memory requrements by three. +// If you are not running uClinux (i.e., your CPU has an MMU) leave this +// disabled... +//#define BB_FEATURE_INSMOD_LOADINKMEM +// +// Support for Minix filesystem, version 2 +//#define BB_FEATURE_MINIX2 +// +// Enable ifconfig status reporting output -- this feature adds 12k. +#define BB_FEATURE_IFCONFIG_STATUS +// +// Enable ifconfig slip-specific options "keepalive" and "outfill" +//#define BB_FEATURE_IFCONFIG_SLIP +// +// Enable ifconfig options "mem_start", "io_addr", and "irq". +//#define BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ +// +// Enable ifconfig option "hw". Currently works for only with "ether". +#define BB_FEATURE_IFCONFIG_HW +// +// Enable busybox --install [-s] +// to create links (or symlinks) for all the commands that are +// compiled into the binary. (needs /proc filesystem) +#define BB_FEATURE_INSTALLER +// +// Enable a nifty progress meter in wget (adds just under 2k) +#define BB_FEATURE_WGET_STATUSBAR +// +// Enable HTTP authentication in wget +#define BB_FEATURE_WGET_AUTHENTICATION +// +// Clean up all memory before exiting -- usually not needed +// as the OS can clean up... Don't enable this unless you +// have a really good reason for cleaning things up manually. +//#define BB_FEATURE_CLEAN_UP +// +// Support for human readable output by ls, du, etc.(example 13k, 23M, 235G) +#define BB_FEATURE_HUMAN_READABLE +// +// Support for the find -type option. +#define BB_FEATURE_FIND_TYPE +// +// Support for the find -perm option. +#define BB_FEATURE_FIND_PERM +// +// Support for the find -mtine option. +#define BB_FEATURE_FIND_MTIME +// +// Support for the -A -B and -C context flags in grep +//#define BB_FEATURE_GREP_CONTEXT +// +// Support for the EGREP applet (alias to the grep applet) +//#define BB_FEATURE_GREP_EGREP_ALIAS +// +// Tell tftp what commands that should be supported. +#define BB_FEATURE_TFTP_PUT +#define BB_FEATURE_TFTP_GET +// +// features for vi +#define BB_FEATURE_VI_COLON // ":" colon commands, no "ex" mode +#define BB_FEATURE_VI_YANKMARK // Yank/Put commands and Mark cmds +#define BB_FEATURE_VI_SEARCH // search and replace cmds +#define BB_FEATURE_VI_USE_SIGNALS // catch signals +#define BB_FEATURE_VI_DOT_CMD // remember previous cmd and "." cmd +#define BB_FEATURE_VI_READONLY // vi -R and "view" mode +#define BB_FEATURE_VI_SETOPTS // set-able options, ai ic showmatch +#define BB_FEATURE_VI_SET // :set +#define BB_FEATURE_VI_WIN_RESIZE // handle window resize +// +// Enable a if you system have setuped locale +//#define BB_LOCALE_SUPPORT +// +// Support for TELNET to pass TERM type to remote host. Adds 384 bytes. +#define BB_FEATURE_TELNET_TTYPE +// +// End of Features List +// +// +// +// +// +// +//--------------------------------------------------- +// Nothing beyond this point should ever be touched by +// mere mortals so leave this stuff alone. +// +#include +#if defined __UCLIBC__ && ! defined __UCLIBC_HAS_MMU__ + #undef BB_RPM2CPIO /* Uses gz_open(), which uses fork() */ + #undef BB_DPKG_DEB /* Uses gz_open(), which uses fork() */ + #undef BB_ASH /* Uses fork() */ + #undef BB_HUSH /* Uses fork() */ + #undef BB_LASH /* Uses fork() */ + #undef BB_INIT /* Uses fork() */ + #undef BB_FEATURE_TAR_GZIP /* Uses fork() */ + #undef BB_SYSLOGD /* Uses daemon() */ + #undef BB_KLOGD /* Uses daemon() */ + #undef BB_UPDATE /* Uses daemon() */ +#endif +#if defined BB_ASH || defined BB_HUSH || defined BB_LASH || defined BB_MSH + #if defined BB_FEATURE_COMMAND_EDITING + #define BB_CMDEDIT + #else + #undef BB_FEATURE_COMMAND_EDITING + #undef BB_FEATURE_COMMAND_TAB_COMPLETION + #undef BB_FEATURE_COMMAND_USERNAME_COMPLETION + #undef BB_FEATURE_SH_FANCY_PROMPT + #endif +#else + #undef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + #undef BB_FEATURE_SH_STANDALONE_SHELL + #undef BB_FEATURE_SH_FANCY_PROMPT +#endif +// +#ifdef BB_KILLALL + #ifndef BB_KILL + #define BB_KILL + #endif +#endif +// +#ifndef BB_INIT + #undef BB_FEATURE_LINUXRC +#endif +// +#if defined BB_MOUNT && defined BB_FEATURE_NFSMOUNT + #define BB_NFSMOUNT +#endif +// +#if defined BB_FEATURE_AUTOWIDTH + #ifndef BB_FEATURE_USE_TERMIOS + #define BB_FEATURE_USE_TERMIOS + #endif +#endif +// +#if defined BB_INSMOD || defined BB_LSMOD + #if ! defined BB_FEATURE_NEW_MODULE_INTERFACE && ! defined BB_FEATURE_OLD_MODULE_INTERFACE + #define BB_FEATURE_NEW_MODULE_INTERFACE + #endif +#endif +// +#ifdef BB_UNIX2DOS + #define BB_DOS2UNIX +#endif +// +#ifdef BB_SYSLOGD + #if defined BB_FEATURE_IPC_SYSLOG + #define BB_LOGREAD + #endif +#endif +// +#if defined BB_ASH && defined BB_FEATURE_SH_IS_ASH +# define BB_SH +# define shell_main ash_main +#elif defined BB_HUSH && defined BB_FEATURE_SH_IS_HUSH +# define BB_SH +# define shell_main hush_main +#elif defined BB_LASH && defined BB_FEATURE_SH_IS_LASH +# define BB_SH +# define shell_main lash_main +#elif defined BB_MSH && defined BB_FEATURE_SH_IS_MSH +# define BB_SH +# define shell_main msh_main +#endif diff --git a/busybox/debian/Config.h-static b/busybox/debian/Config.h-static new file mode 100644 index 000000000..71f6df0d0 --- /dev/null +++ b/busybox/debian/Config.h-static @@ -0,0 +1,473 @@ +/* vi: set sw=4 ts=4: */ +// This file defines the feature set to be compiled into busybox. +// When you turn things off here, they won't be compiled in at all. +// +//// This file is parsed by sed. You MUST use single line comments. +// i.e., //#define BB_BLAH +// +// +// BusyBox Applications +//#define BB_ADJTIMEX +#define BB_AR +#define BB_ASH +#define BB_BASENAME +#define BB_CAT +#define BB_CHGRP +#define BB_CHMOD +#define BB_CHOWN +#define BB_CHROOT +#define BB_CHVT +#define BB_CLEAR +#define BB_CMP +#define BB_CP +#define BB_CPIO +#define BB_CUT +#define BB_DATE +#define BB_DC +#define BB_DD +#define BB_DEALLOCVT +#define BB_DF +#define BB_DIRNAME +#define BB_DMESG +#define BB_DOS2UNIX +#define BB_DPKG +#define BB_DPKG_DEB +#define BB_DUTMP +#define BB_DU +#define BB_DUMPKMAP +#define BB_ECHO +#define BB_ENV +#define BB_EXPR +#define BB_FBSET +#define BB_FDFLUSH +#define BB_FIND +#define BB_FREE +#define BB_FREERAMDISK +#define BB_FSCK_MINIX +#define BB_GETOPT +#define BB_GREP +#define BB_GUNZIP +#define BB_GZIP +#define BB_HALT +#define BB_HEAD +#define BB_HOSTID +#define BB_HOSTNAME +//#define BB_HUSH +#define BB_ID +#define BB_IFCONFIG +#define BB_INIT +//#define BB_INSMOD +#define BB_KILL +#define BB_KILLALL +#define BB_KLOGD +//#define BB_LASH +#define BB_LENGTH +#define BB_LN +#define BB_LOADACM +#define BB_LOADFONT +#define BB_LOADKMAP +#define BB_LOGGER +#define BB_LOGNAME +#define BB_LS +#define BB_LSMOD +#define BB_MAKEDEVS +#define BB_MD5SUM +#define BB_MKDIR +#define BB_MKFIFO +#define BB_MKFS_MINIX +#define BB_MKNOD +#define BB_MKSWAP +#define BB_MKTEMP +//#define BB_MODPROBE +#define BB_MORE +#define BB_MOUNT +//#define BB_MSH +#define BB_MT +#define BB_MV +#define BB_NC +#define BB_NSLOOKUP +#define BB_PIDOF +#define BB_PING +#define BB_PIVOT_ROOT +#define BB_POWEROFF +#define BB_PRINTF +#define BB_PS +#define BB_PWD +#define BB_RDATE +#define BB_READLINK +#define BB_REBOOT +#define BB_RENICE +#define BB_RESET +#define BB_RM +#define BB_RMDIR +#define BB_RMMOD +#define BB_ROUTE +#define BB_RPM2CPIO +#define BB_SED +#define BB_SETKEYCODES +#define BB_SLEEP +#define BB_SORT +#define BB_STTY +#define BB_SWAPONOFF +#define BB_SYNC +#define BB_SYSLOGD +#define BB_TAIL +#define BB_TAR +#define BB_TEE +#define BB_TEST +#define BB_TELNET +#define BB_TFTP +#define BB_TOUCH +#define BB_TR +#define BB_TRACEROUTE +#define BB_TRUE_FALSE +#define BB_TTY +#define BB_UNIX2DOS +#define BB_UUENCODE +#define BB_UUDECODE +#define BB_UMOUNT +#define BB_UNIQ +#define BB_UNAME +#define BB_UPDATE +#define BB_UPTIME +#define BB_USLEEP +#define BB_VI +#define BB_WATCHDOG +#define BB_WC +#define BB_WGET +#define BB_WHICH +#define BB_WHOAMI +#define BB_XARGS +#define BB_YES +// End of Applications List +// +// +// +// --------------------------------------------------------- +// This is where feature definitions go. Generally speaking, +// turning this stuff off makes things a bit smaller (and less +// pretty/useful). +// +// +// If you enabled one or more of the shells, you may select which one +// should be run when sh is invoked: +#define BB_FEATURE_SH_IS_ASH +//#define BB_FEATURE_SH_IS_HUSH +//#define BB_FEATURE_SH_IS_LASH +//#define BB_FEATURE_SH_IS_MSH +// +// BusyBox will, by default, malloc space for its buffers. This costs code +// size for the call to xmalloc. You can use the following feature to have +// them put on the stack. For some very small machines with limited stack +// space, this can be deadly. For most folks, this works just fine... +//#define BB_FEATURE_BUFFERS_GO_ON_STACK +// The third alternative for buffer allocation is to use 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. +//#define BB_FEATURE_BUFFERS_GO_IN_BSS +// +// Turn this on to use Erik's very cool devps, and devmtab kernel drivers, +// thereby eliminating the need for the /proc filesystem and thereby saving +// lots and lots memory for more important things. NOTE: If you enable this +// feature, you _must_ have patched the kernel to include the devps patch that +// is included in the busybox/kernel-patches directory. You will also need to +// create some device special files in /dev on your embedded system: +// mknod /dev/mtab c 10 22 +// mknod /dev/ps c 10 21 +// I emailed Linus and this patch will not be going into the stock kernel. +//#define BB_FEATURE_USE_DEVPS_PATCH +// +// show verbose usage messages +#define BB_FEATURE_VERBOSE_USAGE +// +// Use termios to manipulate the screen ('more' is prettier with this on) +#define BB_FEATURE_USE_TERMIOS +// +// calculate terminal & column widths (for more and ls) +#define BB_FEATURE_AUTOWIDTH +// +// show username/groupnames for ls +#define BB_FEATURE_LS_USERNAME +// +// show file timestamps in ls +#define BB_FEATURE_LS_TIMESTAMPS +// +// enable ls -p and -F +#define BB_FEATURE_LS_FILETYPES +// +// sort the file names +#define BB_FEATURE_LS_SORTFILES +// +// enable ls -R +#define BB_FEATURE_LS_RECURSIVE +// +// enable ls -L +#define BB_FEATURE_LS_FOLLOWLINKS +// +// Disable for a smaller (but less functional) ping +#define BB_FEATURE_FANCY_PING +// +// Make init use a simplified /etc/inittab file (recommended). +#define BB_FEATURE_USE_INITTAB +// +//Enable init being called as /linuxrc +#define BB_FEATURE_LINUXRC +// +//Have init enable core dumping for child processes (for debugging only) +//#define BB_FEATURE_INIT_COREDUMPS +// +//Make sure nothing is printed to the console on boot +//#define BB_FEATURE_EXTRA_QUIET +// +// enable syslogd -R remotehost +#define BB_FEATURE_REMOTE_LOG +// +// enable syslogd -C +//#define BB_FEATURE_IPC_SYSLOG +// +//Disable for a simple tail implementation (2.34k vs 3k for the full one). +//Both provide 'tail -f', but this cuts out -c, -q, -s, and -v. +#define BB_FEATURE_FANCY_TAIL +// +// Enable support for loop devices in mount +#define BB_FEATURE_MOUNT_LOOP +// +// Enable support for a real /etc/mtab file instead of /proc/mounts +//#define BB_FEATURE_MTAB_SUPPORT +// +// Enable support for mounting remote NFS volumes. +// You may need to mount with "-o nolock" if you are +// not running a local portmapper daemon... +#define BB_FEATURE_NFSMOUNT +// +// Enable support forced filesystem unmounting +// (i.e., in case of an unreachable NFS system). +#define BB_FEATURE_MOUNT_FORCE +// +// Enable support for creation of tar files. +#define BB_FEATURE_TAR_CREATE +// +// Enable support for "--exclude" and "-X" for excluding files +#define BB_FEATURE_TAR_EXCLUDE +// +// Enable support for tar -z option (currently only works for inflating) +#define BB_FEATURE_TAR_GZIP +// +// Enable reverse sort +#define BB_FEATURE_SORT_REVERSE +// +// Enable uniqe sort +#define BB_FEATURE_SORT_UNIQUE +// +// Enable command line editing in the shell. +// Only relevant if a shell is enabled. On by default. +#define BB_FEATURE_COMMAND_EDITING +// +// Enable tab completion in the shell. This is now working quite nicely. +// This feature adds a bit over 4k. Only relevant if a shell is enabled. +#define BB_FEATURE_COMMAND_TAB_COMPLETION +// +// Attempts to match usernames in a ~-prefixed path +//#define BB_FEATURE_COMMAND_USERNAME_COMPLETION +// +//Allow the shell to invoke all the compiled in BusyBox applets as if they +//were shell builtins. Nice for staticly linking an emergency rescue shell, +//among other things. Off by default. +// Only relevant if a shell is enabled. +#define BB_FEATURE_SH_STANDALONE_SHELL +// +//When this is enabled, busybox shell applets can be called using full path +//names. This causes applets (i.e., most busybox commands) to override +//real commands on the filesystem. For example, if you run run /bin/cat, it +//will use BusyBox cat even if /bin/cat exists on the filesystem and is _not_ +//busybox. Some systems want this, others do not. Choose wisely. :-) This +//only has meaning when BB_FEATURE_SH_STANDALONE_SHELL is enabled. +// Only relevant if a shell is enabled. Off by default. +#define BB_FEATURE_SH_APPLETS_ALWAYS_WIN +// +// Uncomment this option for a fancy shell prompt that includes the +// current username and hostname. On systems that don't have usernames +// or hostnames, this can look hideous. +// Only relevant if a shell is enabled. +#define BB_FEATURE_SH_FANCY_PROMPT +// +//Turn on extra fbset options +//#define BB_FEATURE_FBSET_FANCY +// +//Turn on fbset readmode support +//#define BB_FEATURE_FBSET_READMODE +// +// Support insmod/lsmod/rmmod for post 2.1 kernels +#define BB_FEATURE_NEW_MODULE_INTERFACE +// +// Support insmod/lsmod/rmmod for pre 2.1 kernels +//#define BB_FEATURE_OLD_MODULE_INTERFACE +// +// Support module version checking +//#define BB_FEATURE_INSMOD_VERSION_CHECKING +// +// Support for uClinux memory usage optimization, which will load the image +// directly into the kernel memory. This divides memory requrements by three. +// If you are not running uClinux (i.e., your CPU has an MMU) leave this +// disabled... +//#define BB_FEATURE_INSMOD_LOADINKMEM +// +// Support for Minix filesystem, version 2 +//#define BB_FEATURE_MINIX2 +// +// Enable ifconfig status reporting output -- this feature adds 12k. +#define BB_FEATURE_IFCONFIG_STATUS +// +// Enable ifconfig slip-specific options "keepalive" and "outfill" +//#define BB_FEATURE_IFCONFIG_SLIP +// +// Enable ifconfig options "mem_start", "io_addr", and "irq". +//#define BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ +// +// Enable ifconfig option "hw". Currently works for only with "ether". +#define BB_FEATURE_IFCONFIG_HW +// +// Enable busybox --install [-s] +// to create links (or symlinks) for all the commands that are +// compiled into the binary. (needs /proc filesystem) +#define BB_FEATURE_INSTALLER +// +// Enable a nifty progress meter in wget (adds just under 2k) +#define BB_FEATURE_WGET_STATUSBAR +// +// Enable HTTP authentication in wget +#define BB_FEATURE_WGET_AUTHENTICATION +// +// Clean up all memory before exiting -- usually not needed +// as the OS can clean up... Don't enable this unless you +// have a really good reason for cleaning things up manually. +//#define BB_FEATURE_CLEAN_UP +// +// Support for human readable output by ls, du, etc.(example 13k, 23M, 235G) +#define BB_FEATURE_HUMAN_READABLE +// +// Support for the find -type option. +#define BB_FEATURE_FIND_TYPE +// +// Support for the find -perm option. +#define BB_FEATURE_FIND_PERM +// +// Support for the find -mtine option. +#define BB_FEATURE_FIND_MTIME +// +// Support for the -A -B and -C context flags in grep +//#define BB_FEATURE_GREP_CONTEXT +// +// Support for the EGREP applet (alias to the grep applet) +//#define BB_FEATURE_GREP_EGREP_ALIAS +// +// Tell tftp what commands that should be supported. +#define BB_FEATURE_TFTP_PUT +#define BB_FEATURE_TFTP_GET +// +// features for vi +#define BB_FEATURE_VI_COLON // ":" colon commands, no "ex" mode +#define BB_FEATURE_VI_YANKMARK // Yank/Put commands and Mark cmds +#define BB_FEATURE_VI_SEARCH // search and replace cmds +#define BB_FEATURE_VI_USE_SIGNALS // catch signals +#define BB_FEATURE_VI_DOT_CMD // remember previous cmd and "." cmd +#define BB_FEATURE_VI_READONLY // vi -R and "view" mode +#define BB_FEATURE_VI_SETOPTS // set-able options, ai ic showmatch +#define BB_FEATURE_VI_SET // :set +#define BB_FEATURE_VI_WIN_RESIZE // handle window resize +// +// Enable a if you system have setuped locale +//#define BB_LOCALE_SUPPORT +// +// Support for TELNET to pass TERM type to remote host. Adds 384 bytes. +#define BB_FEATURE_TELNET_TTYPE +// +// End of Features List +// +// +// +// +// +// +//--------------------------------------------------- +// Nothing beyond this point should ever be touched by +// mere mortals so leave this stuff alone. +// +#include +#if defined __UCLIBC__ && ! defined __UCLIBC_HAS_MMU__ + #undef BB_RPM2CPIO /* Uses gz_open(), which uses fork() */ + #undef BB_DPKG_DEB /* Uses gz_open(), which uses fork() */ + #undef BB_ASH /* Uses fork() */ + #undef BB_HUSH /* Uses fork() */ + #undef BB_LASH /* Uses fork() */ + #undef BB_INIT /* Uses fork() */ + #undef BB_FEATURE_TAR_GZIP /* Uses fork() */ + #undef BB_SYSLOGD /* Uses daemon() */ + #undef BB_KLOGD /* Uses daemon() */ + #undef BB_UPDATE /* Uses daemon() */ +#endif +#if defined BB_ASH || defined BB_HUSH || defined BB_LASH || defined BB_MSH + #if defined BB_FEATURE_COMMAND_EDITING + #define BB_CMDEDIT + #else + #undef BB_FEATURE_COMMAND_EDITING + #undef BB_FEATURE_COMMAND_TAB_COMPLETION + #undef BB_FEATURE_COMMAND_USERNAME_COMPLETION + #undef BB_FEATURE_SH_FANCY_PROMPT + #endif +#else + #undef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + #undef BB_FEATURE_SH_STANDALONE_SHELL + #undef BB_FEATURE_SH_FANCY_PROMPT +#endif +// +#ifdef BB_KILLALL + #ifndef BB_KILL + #define BB_KILL + #endif +#endif +// +#ifndef BB_INIT + #undef BB_FEATURE_LINUXRC +#endif +// +#if defined BB_MOUNT && defined BB_FEATURE_NFSMOUNT + #define BB_NFSMOUNT +#endif +// +#if defined BB_FEATURE_AUTOWIDTH + #ifndef BB_FEATURE_USE_TERMIOS + #define BB_FEATURE_USE_TERMIOS + #endif +#endif +// +#if defined BB_INSMOD || defined BB_LSMOD + #if ! defined BB_FEATURE_NEW_MODULE_INTERFACE && ! defined BB_FEATURE_OLD_MODULE_INTERFACE + #define BB_FEATURE_NEW_MODULE_INTERFACE + #endif +#endif +// +#ifdef BB_UNIX2DOS + #define BB_DOS2UNIX +#endif +// +#ifdef BB_SYSLOGD + #if defined BB_FEATURE_IPC_SYSLOG + #define BB_LOGREAD + #endif +#endif +// +#if defined BB_ASH && defined BB_FEATURE_SH_IS_ASH +# define BB_SH +# define shell_main ash_main +#elif defined BB_HUSH && defined BB_FEATURE_SH_IS_HUSH +# define BB_SH +# define shell_main hush_main +#elif defined BB_LASH && defined BB_FEATURE_SH_IS_LASH +# define BB_SH +# define shell_main lash_main +#elif defined BB_MSH && defined BB_FEATURE_SH_IS_MSH +# define BB_SH +# define shell_main msh_main +#endif diff --git a/busybox/debian/Config.h-udeb b/busybox/debian/Config.h-udeb new file mode 100644 index 000000000..51446ff89 --- /dev/null +++ b/busybox/debian/Config.h-udeb @@ -0,0 +1,473 @@ +/* vi: set sw=4 ts=4: */ +// This file defines the feature set to be compiled into busybox. +// When you turn things off here, they won't be compiled in at all. +// +//// This file is parsed by sed. You MUST use single line comments. +// i.e., //#define BB_BLAH +// +// +// BusyBox Applications +//#define BB_ADJTIMEX +//#define BB_AR +//#define BB_ASH +#define BB_BASENAME +#define BB_CAT +#define BB_CHGRP +#define BB_CHMOD +#define BB_CHOWN +#define BB_CHROOT +#define BB_CHVT +#define BB_CLEAR +//#define BB_CMP +#define BB_CP +//#define BB_CPIO +#define BB_CUT +#define BB_DATE +//#define BB_DC +#define BB_DD +//#define BB_DEALLOCVT +#define BB_DF +#define BB_DIRNAME +#define BB_DMESG +//#define BB_DOS2UNIX +//#define BB_DPKG +//#define BB_DPKG_DEB +//#define BB_DUTMP +#define BB_DU +//#define BB_DUMPKMAP +#define BB_ECHO +#define BB_ENV +#define BB_EXPR +//#define BB_FBSET +//#define BB_FDFLUSH +#define BB_FIND +#define BB_FREE +#define BB_FREERAMDISK +//#define BB_FSCK_MINIX +//#define BB_GETOPT +#define BB_GREP +#define BB_GUNZIP +#define BB_GZIP +#define BB_HALT +#define BB_HEAD +//#define BB_HOSTID +//#define BB_HOSTNAME +//#define BB_HUSH +#define BB_ID +//#define BB_IFCONFIG +#define BB_INIT +//#define BB_INSMOD +#define BB_KILL +#define BB_KILLALL +#define BB_KLOGD +//#define BB_LASH +//#define BB_LENGTH +#define BB_LN +//#define BB_LOADACM +//#define BB_LOADFONT +#define BB_LOADKMAP +#define BB_LOGGER +//#define BB_LOGNAME +#define BB_LS +#define BB_LSMOD +//#define BB_MAKEDEVS +#define BB_MD5SUM +#define BB_MKDIR +//#define BB_MKFIFO +//#define BB_MKFS_MINIX +#define BB_MKNOD +#define BB_MKSWAP +//#define BB_MKTEMP +//#define BB_MODPROBE +#define BB_MORE +#define BB_MOUNT +//#define BB_MSH +//#define BB_MT +#define BB_MV +//#define BB_NC +//#define BB_NSLOOKUP +//#define BB_PIDOF +#define BB_PING +#define BB_PIVOT_ROOT +#define BB_POWEROFF +//#define BB_PRINTF +#define BB_PS +#define BB_PWD +//#define BB_RDATE +//#define BB_READLINK +#define BB_REBOOT +//#define BB_RENICE +#define BB_RESET +#define BB_RM +#define BB_RMDIR +//#define BB_RMMOD +//#define BB_ROUTE +//#define BB_RPM2CPIO +#define BB_SED +//#define BB_SETKEYCODES +#define BB_SLEEP +#define BB_SORT +//#define BB_STTY +#define BB_SWAPONOFF +#define BB_SYNC +#define BB_SYSLOGD +#define BB_TAIL +#define BB_TAR +//#define BB_TEE +//#define BB_TEST +#define BB_TELNET +//#define BB_TFTP +#define BB_TOUCH +#define BB_TR +//#define BB_TRACEROUTE +#define BB_TRUE_FALSE +#define BB_TTY +//#define BB_UNIX2DOS +//#define BB_UUENCODE +//#define BB_UUDECODE +#define BB_UMOUNT +#define BB_UNIQ +#define BB_UNAME +//#define BB_UPDATE +#define BB_UPTIME +//#define BB_USLEEP +#define BB_VI +//#define BB_WATCHDOG +#define BB_WC +#define BB_WGET +#define BB_WHICH +#define BB_WHOAMI +#define BB_XARGS +#define BB_YES +// End of Applications List +// +// +// +// --------------------------------------------------------- +// This is where feature definitions go. Generally speaking, +// turning this stuff off makes things a bit smaller (and less +// pretty/useful). +// +// +// If you enabled one or more of the shells, you may select which one +// should be run when sh is invoked: +//#define BB_FEATURE_SH_IS_ASH +//#define BB_FEATURE_SH_IS_HUSH +//#define BB_FEATURE_SH_IS_LASH +#define BB_FEATURE_SH_IS_MSH +// +// BusyBox will, by default, malloc space for its buffers. This costs code +// size for the call to xmalloc. You can use the following feature to have +// them put on the stack. For some very small machines with limited stack +// space, this can be deadly. For most folks, this works just fine... +//#define BB_FEATURE_BUFFERS_GO_ON_STACK +// The third alternative for buffer allocation is to use 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. +//#define BB_FEATURE_BUFFERS_GO_IN_BSS +// +// Turn this on to use Erik's very cool devps, and devmtab kernel drivers, +// thereby eliminating the need for the /proc filesystem and thereby saving +// lots and lots memory for more important things. NOTE: If you enable this +// feature, you _must_ have patched the kernel to include the devps patch that +// is included in the busybox/kernel-patches directory. You will also need to +// create some device special files in /dev on your embedded system: +// mknod /dev/mtab c 10 22 +// mknod /dev/ps c 10 21 +// I emailed Linus and this patch will not be going into the stock kernel. +//#define BB_FEATURE_USE_DEVPS_PATCH +// +// show verbose usage messages +//#define BB_FEATURE_VERBOSE_USAGE +// +// Use termios to manipulate the screen ('more' is prettier with this on) +#define BB_FEATURE_USE_TERMIOS +// +// calculate terminal & column widths (for more and ls) +#define BB_FEATURE_AUTOWIDTH +// +// show username/groupnames for ls +#define BB_FEATURE_LS_USERNAME +// +// show file timestamps in ls +#define BB_FEATURE_LS_TIMESTAMPS +// +// enable ls -p and -F +#define BB_FEATURE_LS_FILETYPES +// +// sort the file names +#define BB_FEATURE_LS_SORTFILES +// +// enable ls -R +#define BB_FEATURE_LS_RECURSIVE +// +// enable ls -L +#define BB_FEATURE_LS_FOLLOWLINKS +// +// Disable for a smaller (but less functional) ping +#define BB_FEATURE_FANCY_PING +// +// Make init use a simplified /etc/inittab file (recommended). +#define BB_FEATURE_USE_INITTAB +// +//Enable init being called as /linuxrc +#define BB_FEATURE_LINUXRC +// +//Have init enable core dumping for child processes (for debugging only) +//#define BB_FEATURE_INIT_COREDUMPS +// +//Make sure nothing is printed to the console on boot +//#define BB_FEATURE_EXTRA_QUIET +// +// enable syslogd -R remotehost +#define BB_FEATURE_REMOTE_LOG +// +// enable syslogd -C +//#define BB_FEATURE_IPC_SYSLOG +// +//Disable for a simple tail implementation (2.34k vs 3k for the full one). +//Both provide 'tail -f', but this cuts out -c, -q, -s, and -v. +#define BB_FEATURE_FANCY_TAIL +// +// Enable support for loop devices in mount +#define BB_FEATURE_MOUNT_LOOP +// +// Enable support for a real /etc/mtab file instead of /proc/mounts +//#define BB_FEATURE_MTAB_SUPPORT +// +// Enable support for mounting remote NFS volumes. +// You may need to mount with "-o nolock" if you are +// not running a local portmapper daemon... +#define BB_FEATURE_NFSMOUNT +// +// Enable support forced filesystem unmounting +// (i.e., in case of an unreachable NFS system). +#define BB_FEATURE_MOUNT_FORCE +// +// Enable support for creation of tar files. +#define BB_FEATURE_TAR_CREATE +// +// Enable support for "--exclude" and "-X" for excluding files +#define BB_FEATURE_TAR_EXCLUDE +// +// Enable support for tar -z option (currently only works for inflating) +#define BB_FEATURE_TAR_GZIP +// +// Enable reverse sort +#define BB_FEATURE_SORT_REVERSE +// +// Enable uniqe sort +#define BB_FEATURE_SORT_UNIQUE +// +// Enable command line editing in the shell. +// Only relevant if a shell is enabled. On by default. +#define BB_FEATURE_COMMAND_EDITING +// +// Enable tab completion in the shell. This is now working quite nicely. +// This feature adds a bit over 4k. Only relevant if a shell is enabled. +#define BB_FEATURE_COMMAND_TAB_COMPLETION +// +// Attempts to match usernames in a ~-prefixed path +//#define BB_FEATURE_COMMAND_USERNAME_COMPLETION +// +//Allow the shell to invoke all the compiled in BusyBox applets as if they +//were shell builtins. Nice for staticly linking an emergency rescue shell, +//among other things. Off by default. +// Only relevant if a shell is enabled. +//#define BB_FEATURE_SH_STANDALONE_SHELL +// +//When this is enabled, busybox shell applets can be called using full path +//names. This causes applets (i.e., most busybox commands) to override +//real commands on the filesystem. For example, if you run run /bin/cat, it +//will use BusyBox cat even if /bin/cat exists on the filesystem and is _not_ +//busybox. Some systems want this, others do not. Choose wisely. :-) This +//only has meaning when BB_FEATURE_SH_STANDALONE_SHELL is enabled. +// Only relevant if a shell is enabled. Off by default. +//#define BB_FEATURE_SH_APPLETS_ALWAYS_WIN +// +// Uncomment this option for a fancy shell prompt that includes the +// current username and hostname. On systems that don't have usernames +// or hostnames, this can look hideous. +// Only relevant if a shell is enabled. +//#define BB_FEATURE_SH_FANCY_PROMPT +// +//Turn on extra fbset options +//#define BB_FEATURE_FBSET_FANCY +// +//Turn on fbset readmode support +//#define BB_FEATURE_FBSET_READMODE +// +// Support insmod/lsmod/rmmod for post 2.1 kernels +//#define BB_FEATURE_NEW_MODULE_INTERFACE +// +// Support insmod/lsmod/rmmod for pre 2.1 kernels +//#define BB_FEATURE_OLD_MODULE_INTERFACE +// +// Support module version checking +//#define BB_FEATURE_INSMOD_VERSION_CHECKING +// +// Support for uClinux memory usage optimization, which will load the image +// directly into the kernel memory. This divides memory requrements by three. +// If you are not running uClinux (i.e., your CPU has an MMU) leave this +// disabled... +//#define BB_FEATURE_INSMOD_LOADINKMEM +// +// Support for Minix filesystem, version 2 +//#define BB_FEATURE_MINIX2 +// +// Enable ifconfig status reporting output -- this feature adds 12k. +#define BB_FEATURE_IFCONFIG_STATUS +// +// Enable ifconfig slip-specific options "keepalive" and "outfill" +//#define BB_FEATURE_IFCONFIG_SLIP +// +// Enable ifconfig options "mem_start", "io_addr", and "irq". +//#define BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ +// +// Enable ifconfig option "hw". Currently works for only with "ether". +#define BB_FEATURE_IFCONFIG_HW +// +// Enable busybox --install [-s] +// to create links (or symlinks) for all the commands that are +// compiled into the binary. (needs /proc filesystem) +#define BB_FEATURE_INSTALLER +// +// Enable a nifty progress meter in wget (adds just under 2k) +#define BB_FEATURE_WGET_STATUSBAR +// +// Enable HTTP authentication in wget +#define BB_FEATURE_WGET_AUTHENTICATION +// +// Clean up all memory before exiting -- usually not needed +// as the OS can clean up... Don't enable this unless you +// have a really good reason for cleaning things up manually. +//#define BB_FEATURE_CLEAN_UP +// +// Support for human readable output by ls, du, etc.(example 13k, 23M, 235G) +#define BB_FEATURE_HUMAN_READABLE +// +// Support for the find -type option. +#define BB_FEATURE_FIND_TYPE +// +// Support for the find -perm option. +#define BB_FEATURE_FIND_PERM +// +// Support for the find -mtine option. +#define BB_FEATURE_FIND_MTIME +// +// Support for the -A -B and -C context flags in grep +//#define BB_FEATURE_GREP_CONTEXT +// +// Support for the EGREP applet (alias to the grep applet) +//#define BB_FEATURE_GREP_EGREP_ALIAS +// +// Tell tftp what commands that should be supported. +#define BB_FEATURE_TFTP_PUT +#define BB_FEATURE_TFTP_GET +// +// features for vi +#define BB_FEATURE_VI_COLON // ":" colon commands, no "ex" mode +#define BB_FEATURE_VI_YANKMARK // Yank/Put commands and Mark cmds +#define BB_FEATURE_VI_SEARCH // search and replace cmds +#define BB_FEATURE_VI_USE_SIGNALS // catch signals +#define BB_FEATURE_VI_DOT_CMD // remember previous cmd and "." cmd +#define BB_FEATURE_VI_READONLY // vi -R and "view" mode +#define BB_FEATURE_VI_SETOPTS // set-able options, ai ic showmatch +#define BB_FEATURE_VI_SET // :set +#define BB_FEATURE_VI_WIN_RESIZE // handle window resize +// +// Enable a if you system have setuped locale +//#define BB_LOCALE_SUPPORT +// +// Support for TELNET to pass TERM type to remote host. Adds 384 bytes. +#define BB_FEATURE_TELNET_TTYPE +// +// End of Features List +// +// +// +// +// +// +//--------------------------------------------------- +// Nothing beyond this point should ever be touched by +// mere mortals so leave this stuff alone. +// +#include +#if defined __UCLIBC__ && ! defined __UCLIBC_HAS_MMU__ + #undef BB_RPM2CPIO /* Uses gz_open(), which uses fork() */ + #undef BB_DPKG_DEB /* Uses gz_open(), which uses fork() */ + #undef BB_ASH /* Uses fork() */ + #undef BB_HUSH /* Uses fork() */ + #undef BB_LASH /* Uses fork() */ + #undef BB_INIT /* Uses fork() */ + #undef BB_FEATURE_TAR_GZIP /* Uses fork() */ + #undef BB_SYSLOGD /* Uses daemon() */ + #undef BB_KLOGD /* Uses daemon() */ + #undef BB_UPDATE /* Uses daemon() */ +#endif +#if defined BB_ASH || defined BB_HUSH || defined BB_LASH || defined BB_MSH + #if defined BB_FEATURE_COMMAND_EDITING + #define BB_CMDEDIT + #else + #undef BB_FEATURE_COMMAND_EDITING + #undef BB_FEATURE_COMMAND_TAB_COMPLETION + #undef BB_FEATURE_COMMAND_USERNAME_COMPLETION + #undef BB_FEATURE_SH_FANCY_PROMPT + #endif +#else + #undef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + #undef BB_FEATURE_SH_STANDALONE_SHELL + #undef BB_FEATURE_SH_FANCY_PROMPT +#endif +// +#ifdef BB_KILLALL + #ifndef BB_KILL + #define BB_KILL + #endif +#endif +// +#ifndef BB_INIT + #undef BB_FEATURE_LINUXRC +#endif +// +#if defined BB_MOUNT && defined BB_FEATURE_NFSMOUNT + #define BB_NFSMOUNT +#endif +// +#if defined BB_FEATURE_AUTOWIDTH + #ifndef BB_FEATURE_USE_TERMIOS + #define BB_FEATURE_USE_TERMIOS + #endif +#endif +// +#if defined BB_INSMOD || defined BB_LSMOD + #if ! defined BB_FEATURE_NEW_MODULE_INTERFACE && ! defined BB_FEATURE_OLD_MODULE_INTERFACE + #define BB_FEATURE_NEW_MODULE_INTERFACE + #endif +#endif +// +#ifdef BB_UNIX2DOS + #define BB_DOS2UNIX +#endif +// +#ifdef BB_SYSLOGD + #if defined BB_FEATURE_IPC_SYSLOG + #define BB_LOGREAD + #endif +#endif +// +#if defined BB_ASH && defined BB_FEATURE_SH_IS_ASH +# define BB_SH +# define shell_main ash_main +#elif defined BB_HUSH && defined BB_FEATURE_SH_IS_HUSH +# define BB_SH +# define shell_main hush_main +#elif defined BB_LASH && defined BB_FEATURE_SH_IS_LASH +# define BB_SH +# define shell_main lash_main +#elif defined BB_MSH && defined BB_FEATURE_SH_IS_MSH +# define BB_SH +# define shell_main msh_main +#endif diff --git a/busybox/debian/README.debian b/busybox/debian/README.debian new file mode 100644 index 000000000..f210a3e39 --- /dev/null +++ b/busybox/debian/README.debian @@ -0,0 +1,10 @@ +BusyBox for Debian +---------------------- + +BusyBox is being developed and maintained by Erik Andersen +. + +If you have a problem with BusyBox, send email to the Debian bug tracking +system that lives at + +Erik Andersen , Sun, 18 Jun 2000 21:52:00 -0600 diff --git a/busybox/debian/changelog b/busybox/debian/changelog new file mode 100644 index 000000000..41274bdaf --- /dev/null +++ b/busybox/debian/changelog @@ -0,0 +1,175 @@ +busybox (1:0.60.0-1) unstable; urgency=low + + * New version released. See changelog for details. + + -- Erik Andersen 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Wed, 13 Dec 2000 08:36:07 -0700 + +busybox (0.47-1) unstable; urgency=low + + * New version released. See changelog for details. + + -- Erik Andersen Mon, 25 Sep 2000 23:00:56 -0600 + +busybox (0.46-1) unstable; urgency=low + + * New version released. See changelog for details. + + -- Erik Andersen 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 Tue, 27 Jun 2000 12:26:41 -0600 + diff --git a/busybox/debian/control b/busybox/debian/control new file mode 100644 index 000000000..3626718da --- /dev/null +++ b/busybox/debian/control @@ -0,0 +1,66 @@ +Source: busybox +Priority: optional +Maintainer: Erik Andersen +Build-Depends: debhelper (>= 2.1.18), dpkg-dev (>= 1.7.0) +Standards-Version: 3.2.1.0 + +Package: busybox +Architecture: any +Depends: ${shlibs:Depends} +Conflicts: busybox-static +Replaces: 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-static +Architecture: any +Depends: ${shlibs:Depends} +Conflicts: busybox +Replaces: 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-udeb +Architecture: any +Depends: ${shlibs:Depends} +Section: debian-installer +Priority: standard +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. + diff --git a/busybox/debian/copyright b/busybox/debian/copyright new file mode 100644 index 000000000..68a96e68b --- /dev/null +++ b/busybox/debian/copyright @@ -0,0 +1,7 @@ +This package was debianized by Erik Andersen on +Sun, 18 Jun 2000 23:31:02 -0600 + +It was downloaded from ftp://ftp.lineo.com/pub/busybox +HomePage is at: http://busybox.lineo.com/ + +Copyright: GPL diff --git a/busybox/debian/rules b/busybox/debian/rules new file mode 100755 index 000000000..1d7413c35 --- /dev/null +++ b/busybox/debian/rules @@ -0,0 +1,193 @@ +#!/usr/bin/make -f + +# This is a bit unusual, in that I have to completely recompile everything +# for each package I build (obviously static and dynamic builds require +# things to be recompiled...) + +# This is the debhelper compatability version to use. +#export DH_COMPAT=1 + +bbbd=debian/busybox_builddir +bb=debian/tmp +bbsbd=debian/busybox_static_builddir +bbs=debian/busybox-static +bbubd=debian/busybox_udeb_builddir +bbu=debian/busybox-udeb + +clean: + dh_testdir + dh_testroot + rm -f debian/build-stamp-busybox debian/build-stamp-busybox-static debian/build-stamp-busybox-udeb + -$(MAKE) clean + -rm -rf $(bb) $(bbbd) $(bbs) $(bbsbd) $(bbubd) $(bbu) + dh_clean + +build: debian/build-stamp-busybox +debian/build-stamp-busybox: + dh_testdir + mkdir -p $(bbbd) + cp Makefile $(bbbd) + cp debian/Config.h-deb $(bbbd)/Config.h + -(cd $(bbbd); $(MAKE) "BB_SRC_DIR=../../" applet_source_list) + (cd $(bbbd); $(MAKE) USE_SYSTEM_PWD_GRP=false "BB_SRC_DIR=../../") + touch debian/build-stamp-busybox + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + # Do not run 'make install', since we do not want all the symlinks. + # This just installes the busybox binary... + #(cd $(bbbd); $(MAKE) "BB_SRC_DIR=../../" "PREFIX=../../$(bb)" install) + mkdir -p $(bb)/bin/ + cp $(bbbd)/busybox $(bb)/bin/busybox + mkdir -p $(bb)/usr/share/doc/busybox/busybox.lineo.com + cp $(bbbd)/docs/busybox.lineo.com/BusyBox.html $(bb)/usr/share/doc/busybox/busybox.lineo.com/ + mkdir -p $(bb)/usr/share/man/man1 + cp $(bbbd)/docs/BusyBox.1 $(bb)/usr/share/man/man1/busybox.1 + +# Now for the statically linked stuff +build-static: debian/build-stamp-busybox-static +debian/build-stamp-busybox-static: + dh_testdir + mkdir -p $(bbsbd) + cp Makefile $(bbsbd) + cp debian/Config.h-static $(bbsbd)/Config.h + (cd $(bbsbd); $(MAKE) DOSTATIC=true USE_SYSTEM_PWD_GRP=false "BB_SRC_DIR=../../") + touch debian/build-stamp-busybox-static + +install-static: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + # Do not run 'make install', since we do not want all the symlinks. + # This just installes the busybox binary... + #(cd $(bbsbd); $(MAKE) "BB_SRC_DIR=../../" "PREFIX=../../$(bbs)" install) + mkdir -p $(bbs)/bin/ + cp $(bbsbd)/busybox $(bbs)/bin/busybox + mkdir -p $(bbs)/usr/share/doc/busybox-static/busybox.lineo.com + cp $(bbsbd)/docs/busybox.lineo.com/BusyBox.html $(bbs)/usr/share/doc/busybox-static/busybox.lineo.com/ + mkdir -p $(bbs)/usr/share/man/man1/ + cp $(bbsbd)/docs/BusyBox.1 $(bbs)/usr/share/man/man1/busybox.1 + +half_clean: + dh_testdir + dh_testroot + rm -rf $(bbs) debian/build-stamp-busybox-static + -$(MAKE) clean + +do_static: half_clean build-static install-static + + + +# Now for the .udeb stuff +PACKAGE=busybox-udeb +VERSION=$(shell dpkg-parsechangelog | grep ^Version: | cut -d ' ' -f 2 | sed -e s/[0-9]://g) +ARCH=$(shell dpkg --print-architecture) +FILENAME=$(PACKAGE)_$(VERSION)_$(ARCH).udeb + +build-udeb: debian/build-stamp-busybox-udeb +debian/build-stamp-busybox-udeb: + dh_testdir + mkdir -p $(bbubd) + cp Makefile $(bbubd) + cp debian/Config.h-udeb $(bbubd)/Config.h + (cd $(bbubd); $(MAKE) USE_SYSTEM_PWD_GRP=false "BB_SRC_DIR=../../") + touch debian/build-stamp-busybox-udeb + +install-udeb: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + (cd $(bbubd); $(MAKE) "BB_SRC_DIR=../../" "PREFIX=../../$(bbu)" install) + mkdir -p $(bbu)/usr/share/man/man1/ + cp $(bbubd)/docs/BusyBox.1 $(bbu)/usr/share/man/man1/busybox.1 + +three_quarter_clean: + dh_testdir + dh_testroot + rm -rf $(bbu) debian/build-stamp-busybox-udeb + -$(MAKE) clean + +do_udeb: three_quarter_clean build-udeb install-udeb + + + +# Build architecture-independent files here. +binary-indep: +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: busybox busybox-static busybox-udeb + +busybox: install + @echo "--- Building: $@" + dh_testdir + dh_testroot + dh_installdirs + dh_installdocs -p$@ $(bbbd)/docs/BusyBox.txt \ + $(bbbd)/docs/BusyBox.html docs/style-guide.txt \ + docs/busybox.lineo.com AUTHORS README TODO + rm -rf `find $(bb) -name CVS` + rm -f `find $(bb) -name .cvsignore` + dh_installchangelogs -p$@ Changelog + dh_undocumented -p$@ + dh_strip -p$@ + dh_compress -p$@ + dh_fixperms -p$@ + dh_installdeb -p$@ + dh_shlibdeps -p$@ + dh_gencontrol -p$@ + dh_md5sums -p$@ + dh_builddeb -p$@ + + +busybox-static: do_static + @echo "--- Building: $@" + dh_testdir + dh_testroot + dh_installdirs + dh_installdocs -p$@ $(bbsbd)/docs/BusyBox.txt \ + $(bbsbd)/docs/BusyBox.html docs/style-guide.txt \ + docs/busybox.lineo.com AUTHORS README TODO + rm -rf `find $(bbs) -name CVS` + rm -f `find $(bbs) -name .cvsignore` + dh_installchangelogs -p$@ Changelog + dh_undocumented -p$@ + dh_strip -p$@ + dh_compress -p$@ + dh_fixperms -p$@ + dh_installdeb -p$@ + dh_shlibdeps -p$@ + dh_gencontrol -p$@ + dh_md5sums -p$@ + dh_builddeb -p$@ + + +# Note that this builds a .udeb, which is not policy compliant or anything. +# +busybox-udeb: do_udeb + @echo "--- Building: $@" + dh_testdir + dh_testroot + dh_installdirs + dh_strip -p$@ + dh_compress -p$@ + dh_fixperms -p$@ + dh_installdeb -p$@ + dh_shlibdeps -p$@ + #Make _very_ sure there are no docs lurking about. + rm -rf $(bbu)/usr/share/man + #dh_gencontrol -p$@ + # Don't write your stupid guesses to debian/files. + dh_gencontrol -p$@ -- -fdebian/files~ + # Register file manually. + dpkg-distaddfile $(FILENAME) debian-installer standard + dh_md5sums -p$@ + dh_builddeb -p$@ --filename=$(FILENAME) + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/busybox/df.c b/busybox/df.c new file mode 100644 index 000000000..8cb13fa6d --- /dev/null +++ b/busybox/df.c @@ -0,0 +1,158 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini df implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * based on original code by (I think) Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include "busybox.h" + +extern const char mtab_file[]; /* Defined in utility.c */ +#ifdef BB_FEATURE_HUMAN_READABLE +static unsigned long df_disp_hr = KILOBYTE; +#endif + +static int do_df(char *device, const char *mount_point) +{ + struct statfs s; + long blocks_used; + long blocks_percent_used; + + if (statfs(mount_point, &s) != 0) { + perror_msg("%s", mount_point); + return FALSE; + } + + if (s.f_blocks > 0) { + blocks_used = s.f_blocks - s.f_bfree; + if(blocks_used == 0) + blocks_percent_used = 0; + else { + blocks_percent_used = (long) + (blocks_used * 100.0 / (blocks_used + s.f_bavail) + 0.5); + } + if (strcmp(device, "/dev/root") == 0) { + /* Adjusts device to be the real root device, + * or leaves device alone if it can't find it */ + device = find_real_root_device_name(device); + if(device==NULL) + return FALSE; + } +#ifdef BB_FEATURE_HUMAN_READABLE + printf("%-20s %9s ", device, + make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr)); + + printf("%9s ", + make_human_readable_str( (s.f_blocks - s.f_bfree), s.f_bsize, df_disp_hr)); + + printf("%9s %3ld%% %s\n", + make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), + blocks_percent_used, mount_point); +#else + printf("%-20s %9ld %9ld %9ld %3ld%% %s\n", + device, + (long) (s.f_blocks * (s.f_bsize / (double)KILOBYTE)), + (long) ((s.f_blocks - s.f_bfree)*(s.f_bsize/(double)KILOBYTE)), + (long) (s.f_bavail * (s.f_bsize / (double)KILOBYTE)), + blocks_percent_used, mount_point); +#endif + } + + return TRUE; +} + +extern int df_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int opt = 0; + int i = 0; + char disp_units_hdr[80] = "1k-blocks"; /* default display is kilobytes */ + + while ((opt = getopt(argc, argv, "k" +#ifdef BB_FEATURE_HUMAN_READABLE + "hm" +#endif +)) > 0) + { + switch (opt) { +#ifdef BB_FEATURE_HUMAN_READABLE + case 'h': + df_disp_hr = 0; + strcpy(disp_units_hdr, " Size"); + break; + case 'm': + df_disp_hr = MEGABYTE; + strcpy(disp_units_hdr, "1M-blocks"); + break; +#endif + case 'k': + /* default display is kilobytes */ + break; + default: + show_usage(); + } + } + + printf("%-20s %-14s %s %s %s %s\n", "Filesystem", disp_units_hdr, + "Used", "Available", "Use%", "Mounted on"); + + if(optind < argc) { + struct mntent *mount_entry; + for(i = optind; i < argc; i++) + { + if ((mount_entry = find_mount_point(argv[i], mtab_file)) == 0) { + error_msg("%s: can't find mount point.", argv[i]); + status = EXIT_FAILURE; + } else if (!do_df(mount_entry->mnt_fsname, mount_entry->mnt_dir)) + status = EXIT_FAILURE; + } + } else { + FILE *mount_table; + struct mntent *mount_entry; + + mount_table = setmntent(mtab_file, "r"); + if (mount_table == 0) { + perror_msg("%s", mtab_file); + return EXIT_FAILURE; + } + + while ((mount_entry = getmntent(mount_table))) { + if (!do_df(mount_entry->mnt_fsname, mount_entry->mnt_dir)) + status = EXIT_FAILURE; + } + endmntent(mount_table); + } + + return status; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/dirname.c b/busybox/dirname.c new file mode 100644 index 000000000..b534e6950 --- /dev/null +++ b/busybox/dirname.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dirname implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ + +#include +#include +#include +#include "busybox.h" + +extern int dirname_main(int argc, char **argv) +{ + if ((argc < 2) || (**(argv + 1) == '-')) + show_usage(); + argv++; + + puts (dirname (argv[0])); + + return EXIT_SUCCESS; +} diff --git a/busybox/dmesg.c b/busybox/dmesg.c new file mode 100644 index 000000000..73de6d1ae --- /dev/null +++ b/busybox/dmesg.c @@ -0,0 +1,95 @@ +/* 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 + * - added Native Language Support + * + * from util-linux -- adapted for busybox by + * Erik Andersen . I ripped out Native Language + * Support, replaced getopt, added some gotos for redundant stuff. + */ + +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +# ifdef __alpha__ +# define klogctl syslog +# endif +#else +# include +#endif + +#include "busybox.h" + +int dmesg_main(int argc, char **argv) +{ + char *buf; + int c; + int bufsize = 8196; + int i; + int n; + int level = 0; + int lastc; + int cmd = 3; + + while ((c = getopt(argc, argv, "cn:s:")) != EOF) { + switch (c) { + case 'c': + cmd = 4; + break; + case 'n': + cmd = 8; + if (optarg == NULL) + show_usage(); + level = atoi(optarg); + break; + case 's': + if (optarg == NULL) + show_usage(); + bufsize = atoi(optarg); + break; + default: + show_usage(); + } + } + + if (optind < argc) { + show_usage(); + } + + if (cmd == 8) { + if (klogctl(cmd, NULL, level) < 0) + perror_msg_and_die("klogctl"); + return EXIT_SUCCESS; + } + + if (bufsize < 4096) + bufsize = 4096; + buf = (char *) xmalloc(bufsize); + if ((n = klogctl(cmd, buf, bufsize)) < 0) + perror_msg_and_die("klogctl"); + + 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'); + return EXIT_SUCCESS; +} diff --git a/busybox/docs/.cvsignore b/busybox/docs/.cvsignore new file mode 100644 index 000000000..ec9e94b27 --- /dev/null +++ b/busybox/docs/.cvsignore @@ -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 index 000000000..d753300c1 --- /dev/null +++ b/busybox/docs/autodocifier.pl @@ -0,0 +1,287 @@ +#!/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; + + # make options bold + my $trivial = $usage->{trivial}; + $trivial =~ s/(?/sxg; + my @f0 = + map { $_ !~ /^\s/ && s/(?/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" + ; +} + +# FIXME | generate SGML for an applet +sub sgml_for_usage { + my $name = shift; + my $usage = shift; + return + "\n". + " $name\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", + "sgml|s", + "pod|p", + "verbose|v", +); + +if (defined $opt{help}) { + print + "$0 [OPTION]... [FILE]...\n", + "\t--help\n", + "\t--sgml\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; +if (defined $opt{sgml}) { + $generator = \&sgml_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. +Currently, the same content has to be duplicated in 3 places in +slightly different formats -- F, F, and +F. This is tedious, so Perl has come to the rescue. + +This script was based on a script by Erik Andersen +which was in turn based on a script by Mark Whitley + +=head1 OPTIONS + +=over 4 + +=item B<--help> + +This displays the help message. + +=item B<--pod> + +Generate POD (this is the default) + +=item B<--sgml> + +Generate SGML + +=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 + +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 + +=item B + +This should contain descriptions of each option. This will also +be displayed along with the trivial help if BB_FEATURE_TRIVIAL_HELP +is disabled. I + +=item B + +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. I + +=item B + +This should be an example of how the command is acutally used. +This will not be printed when a B<-h> is given to a command -- it +is inteded only for the POD or SGML documentation. I + +=back + +=head1 FILES + +F + +=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 + +=cut + +# $Id: autodocifier.pl,v 1.21 2001/04/17 17:09:34 beppu Exp $ diff --git a/busybox/docs/busybox.net/.cvsignore b/busybox/docs/busybox.net/.cvsignore new file mode 100644 index 000000000..88825af14 --- /dev/null +++ b/busybox/docs/busybox.net/.cvsignore @@ -0,0 +1 @@ +BusyBox.html diff --git a/busybox/docs/busybox.net/busybox-growth.ps b/busybox/docs/busybox.net/busybox-growth.ps new file mode 100644 index 000000000..123f38114 --- /dev/null +++ b/busybox/docs/busybox.net/busybox-growth.ps @@ -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/images/busybox.jpeg b/busybox/docs/busybox.net/images/busybox.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..37edc9614648070b96a748a08368c168eb5195f5 GIT binary patch literal 9023 zcmb7|J2~_Kub+cLrq0XLqh`w)6y|;Ff!27Gw`ypGjj;?2@47G2?#*M z6{H}da$*7kP&H|}>xwWKOjt_ehPslrf-+3$e-H{V7|cM=z{AMMqa-3AqV)f*t04d@ zEd>>YIgmmWK*>r0WTm(o1qc8Dl)!&yPyqg)C@29y5EV5I?LSwW1wa9$1pQx8Qv&}* zDE^HCML=wda4JieKz7mCTxzA-E)FrP9U6q|OHSpWh4W9kaa=I;ziD0y!2i(y5B$GQ zLHUpU&yrvTP*VO|{BJqnf7t(Pv9gH(72%d3c2So=4n*wL27uvz305F0;1=Lh$860ot(6)>&o_oUb071=~4457S=kJ$Q%l;mUVLRK~CA zh#!}h{2C}_HX%rUL}$y8w@>Vp z=)L)YNpOBUV30C1GUJfP2A^;BQI;lKg=Q1w?61)%dztM6ftKz#ZB{;} z2~4=UShqZW`S&Y09gFRULCPcSHnN?dgwZJLy^CsVP~IJ?T} zK_J82?};8G?~&S>d4p9&h^zpEN;XU zic?w_%~L#rZ8)dg;4~wYBSeg|Cbn*HDgB8aHCA%n7^6~>w{Jc+9Le8`_?Hnjo! zqE2HZ?LOjBK+qT;x^!?rQD}WJG0kiqDD<18L_ZN!wH@*Ou!Z~rclE^cnN=2sI_l&~ z=yvVOd+mo=xOFo$m)qGTB7E3P2iyIxO+e;|4SSNM4#(pO<`PJJsJ+h{1jR{@=I>`K zvY=@k&wTr-#7xE-eaS1K*nr`jUu^KB#-`!1jzy}u759i|aa$=al_s?w_bThVF*01` z^=OX<`~eUkm*8OO5vnHe+{;LN*gI^!`wEEL5UjgJBw-Vab1NcXUJo=4? zf`gqU_l50{YCGtQ)CC}`Y}>o!PmlbxMsT;5A6tBP`pAB}zsfZG((bwKBV$MOLm~fH zSd&VMDEnIs+ote}XL~2&*8yw1b;=5F~F{ zamK5c-{xJx%z$!|mw{*Gfhrrpcf}VUGA1l5M}dT$+In$NY_O|`dsL55#mcd7)%Q5Pw_B{!9XSl{*#0y}F964?shS#v>m0+V@q1t_`3YLtH(8$Mq}EMR_F(XNAN$yH zKC)orR_SY+V;RStuZ`fFSyXD%HO!%H$kaZg?yu@GaXxX3jt>0u=s#=5*~9&cK97lm z8a#ieVxqbA6?l8b2Y1gp?KV~9nZ6Z%wLCwhb2k&fh~38vpFW-++V1?Arfx5Hx>E20 zvhWaA?KRWl;=3!zaeU*|AH_?jHfGA*tbt*T_JiG9TTyjKEMg1N3UAJOt^j!p4KbYe zBk%rIOmEY^z^OG9hF(j+dCuNpdB0HIe>;V{y5v4V(0KMQs@fnuj&`?aCxB-ySu;0M zm=NH_^wJ7HqYMeRe?8cUS1V*LGVfmFpymYzJ;pOl%p?GMr##T55u@mEvsBZpw3%LG z2WWR8W$Dy;IE{1vEq>uqi$)tcjV6ik5$88&120%DIn^Cfxw&PVQ|p)M!seEr|!=}p?l--66P?e(!wqN4_1Vee_L{x%%hG-SRhfb~-C2p&1A zc&uA?rYFB-UQb%#98~pK`p^MOg764wZ;6WKmni!U44B> zR-b>F(_h~3K(Sno*WSPyR%}i@bQSodz~+M5Yeit*z8+&vQg2cyzhlT?!Z`9s*kfTh znsJoKOZ*8-wqbiXz4)7Z%r5hL4CMOHFfr@~Q*Y9Tyy#iM=x9Z+qLF9@o)|6OW=Y_m zHM8nrN0s=ws@f}nrLgN0jm$@2FXFJ{XEjF_#f9xn)p%!@lA}L1zuGhF5haYxh;l;% ze61)gzJZ>nuzWus<`@W9aiq2|0{Z6_^Juo%Ylsvra)^_CWJe{#QBGX><%8brj}`!s z6fB10`bq6xg=#>215b9S9Tn$(f>tZ-=?#voda{XIU02|sxOrVf*CCmDi2H@RH$v~@ zX%hk_-J33~FS$W+R1}vB0!mO(kPH{tJ5sIqK){%8Q0S#q7$0Fi4sFlR!2jRTz;Dm0 zhx{1FAI3*|>kkTTn>>dxe&&UD$G^EukfL9~sb3$IW-3J4Q%&`;4R2(n;IY58n0HrI z7j3<|0H5ZIk3lSV^Esj3T(tAA^S(#uQ*?`5S@f3srurlgL9l5s34 zV7OSBoANKQ%hT$gTsph3(u1PMayJ#Lgsw47#b5BNhe3zdop=?+WincuP_?{afq{2u zEm{?JzIO{=0ru@_x*WB`sdgQ&zm{V7)^m}b9r_WjRoIlJ&?vbF!8C<>7$n1faxO7N z-Vd7l^!^HfK5%VZ8Tege&)qwI&crp%3(7%(PR_y`EA`ht4sIg}m4`8^ZE7C{C5Lk)Nbk`2%ws;Et~b z;eMIi&ThDsrqz`%3lEcSVvAYvu&n!1qYdG9lSOq|#pgeJT0YTz_qqxEio2r^p+0VE zJ-EeW-5mQpDW2#7C>i`&h+s6-WG?%VokxB%I>sv8%`FL#xDLs@ndYH9>AV|~UR;)w zWt)dA)Dj0>rz~2W*c9Z*g<4y&WKD2Q&VvqEa!HMT^uXF@W(|!B>@5aJ|2!>+1SnbL zL|j67)@(xm3J`M4>>@D|5*In;`nCnuu&Bc$IT<7+2{V!4qZ6kRHLH~y^skeDYZ*gb zC-@(2oSKa77eguOO9QwLwT8^w#c%5NJNDXy=iYmd6DG?r(QB!T=Mjna;yhBJPm(Rq zTPAps2OZzzQUoG10sEqde~YFt;|6Dx&C1oB?u-Y9?&n!XPoXrw3;8@J7sWw-90^VL z>p5ij(Sxis)kFYr5Mp+$@+V!V--5DQr+~fNXRIb$PpTB9l|Tz>It{_2SnoaiZ@9z< zb}re|y6C&r6+uIeV58UD2Qitv4iu8j8rd~TyiX#WQdzyLu^(3QR1OIoiM`d0(q~O= z(rw}rhWAIa{#vHThsp;?;lNMHIn=XqliF;L(Zqb;}y{`SKj&;9@E&cAJF zJ_;XtF79t{I@}*S8~FP5TkYr@y6}8=v%v>!T_5ii5KanETjHs>c&6MaMj1{K~KC5ST_Cu5~@ zF0f_I7tDHCsvb*dL?%DkHS}J%%nJ_2xHi|J%~J#P{x7L|#~btTZ;m6jU(As|oJt0; z67!|`8Kjt$r4|7xR2Q)}%hw%wO4wyq>53hi36ilKS!wZm#XifEUvC5{*qe5JJGJ2` z%X9R*r{KTPXHThHomYwDYOTYtLH_#unm+~UMr++4{?aE~Tmeqq0Aik76xll*gnWZ? zqPDjqZ9&3b?zAUg&s9Fm*sn6+M-Jtm6^G&T6O0CuN3cyb)0;p%BUlcIK>L>q&TE2I`iMdZ#;YFpR z&%o}qGHkr(rv6Q|_On&f%fIDq3irI9#7$f7jE9|(GB`^Ix;|-}u>E8TeruEov`KD# zzdE{1*kxM`(Yj`uH7*RAZ+iK_NzE}EoY?0@Jb9mHs3u;nd96v;k9dCSOncVLI~`I+ z%YXJb$RPB*019mD2vA`XNZmv#+ zI+@M}gK_H}y6g9BbJoiO7DF7?Plxq#$sW*hL`YNs@RMwbzMLWM_IP2~nsbz+k=C{B z#~W-S_n8=9oj9LRV-qhXx+;LHHU%wPcplEyaw$v{(9Iv?hF zT0F)tw#{89kq#l7wtIh)=#Nx;wtQ-u<-w3c%bZ zc+J;-X@Ei|=OZtDrDg%X^lAHZCcg5Nc1I?c_`fS&g7`)k_zKhLdQTk}?zdS(yU_}n=dQv znp_R4jW8MsCHo*UaFnV#uo+Qezz z5}8L-II)^@k4p~Tvz?l=A}}fiRQ^m+!&rCPaG){@9ZDuwEb~I`+pIi5pN z^2XBZ9Kqf3eBs72(;;RFwx~Y2HcsiHr{#!MPgttm38jC{5qi|{7Q5~^j@mzvUKNB5 zu9)c%X$^kqTep9c_n^=Ukh(g>SGNrjXB2PtO~@FHRkQa&`;K-?g_h3VQxs~IwrM(E z`1+b1r4@^AO^6akzJT#4J??HUa(qfN5*_piG@e@Qa$@&!mHSBxPGhz;In0MsVNunw z$52n)-Y361&x~NZqpQFO@6mOw;>yw9I*=^^1)Js_Z?Aa< z_^n11Ne@$n!yXou&}$KwBNc)-&9Qb9?@AZQ)~BW-4b@m5n3;Y@m@|b^gITNM*ice- zs7Y6LSXQFVSGldCU!ppcP98JA^$}j9*umC{=<%qAZ%kR`T^#mqutJBfOme5DLiAU? zxE)`~d`2;cu1CVM4^{$3m&0Col$*}a0Wm6WU0b%pgcX+HMsX1cMWps(-u*fR3jtDh zhgfW}-eL|RR=`dso(~|CJCB%+eQ>j?0QEC!j6%H@A$NOpcy_Icz=3gbfsp2)PG-F0UFuv9dHM%qZ%J#0AuW`iJap_h>RiV8rl^YP zR}^?JYThgzQIsEdiC;IV_79Hq!k zWBmj|<|DHdPpkJ-4b{Z`h^!RxDW6Qc1y0ECcY1=Uj33Cs(Yl>N6U7r4h0JC?gUn0p z6OPNf52sae>!QqhWqx7FDv|#!n8qAST2Sg3X+_oQC#7fQhS~uH|6t~(By_wE5~$<& zW46GWF(LFdjlrqUceGqmogtO`G@#%&{2kH8&fyJ1CV` zL?XVzOYHCQCN*nMYrJCi6zjJP# z`LCua=4*Oh?i;eflP1;BH1klRE3knfiH)NV97%VA#X1EYEGLq@kSFB!a*G$m*JFD;6{hAWXT42%{a zdDq;BDORjdMXx~rUC&H+`$e2ihS>}G&F|g^wa5J(xjZTHCRy&3oLqX)?Ef*Kxpa^| z=UY&b?Wzxq{3Xeig^c`g78~-`=3zDUP^U2KT;v}~NIOG){7Cln2uUc&7zO=O(D^;> zK-wzm@N3-T#qIv7hyDh5vD_SeIE~yhZ+&O&``SF64!KuVl^{ zL2XNn7z_?uI;u@UOh6OA8GGSOnqE@gM?9?_HrfscCE8tB3#w%6+wJ9yTlRsm8b-H66K8WIr{_Oy%=)go9rZV!mNVKQ>dSE5 zwYLR9@(e9&v8virjW0XqA-q#lB#(K1w-$Vr!Cnmw6kPGv>!b?FX$0DM=^30bG`LW7 zE)MEwlssIKMVaze^5Z$pr}!wJw`)Z&ax%H$4`l{VS%dk4fLw<%FB==t#<~jFJcDnx zJ>Gpgbt&r;_WEOgif=qhtjl!X;cp4Zr1yz5D$!9_2N*$Li1_`tV&iO_D$7?WZ1R^c z+AQKB^nAwQg(Ts#dbx`5Za8aW_AadRYwd1no;~tUang<%kY)bRQ1%i0ui*DI@+XZd z)#8GDBwC1nV#kXT-noG#b@r$2O3$~P)i&K&YpzWg-WgpQLF4|`mY+SQ`#}Ln$};UX z3zRmP(=x=rub_zHN5!C%H>{^(m@U0$?{JrVc9yL>X}+iqHK_`-%*EtN-#Zi518#p$ z{yEbw+9PdWsEQVz7jb8jiXuprmAk^USR4b6&xjAK!haI?zDOf4Ohqz$gj~RoV3X%~ zY4Ni%&YCNL8irP9VxBD4T)o1_m&f0tQ##+sciTAOqPtv2_uWZCl{cKfF;A3z79voP zyn<8)LEhK-_{s5(?M?;@vU~CiyEhCi9`CKEpwbg>`y+6DQw2}F6MAKI#K)&*q+Ywz z@)1)Mo~`uXm3{rYqb$c>!_imvL+Voncr!tDKXh?7;Rz?Df2VDQ(RIXInb(m8ITVyC zdi35Rjtp<4E%JX_r%6hHE_KR}+CHDLLJVRp{K2jYBGQ70=usa^MNK=a(Cs(M1C=Z0 z-g^(Yhy`zrQba^tD9_pQyToK^iiVfEBT16JLCHL{Jh3xvc5QGL>d&Q>H&ax)U2o(A z;Ho#L9AKyy+9aUhQZHRi7*V8yhzi*RVu^#7(D^*my;xbObZ^ z<=^A14t>SAp5-+_ZnNcTfx=hhN-#OLN^ggvq$zZXxwwKDNSBv0o6mh&2z&u^$f#x@ zCr|N;VB_-8#(Y@@Y%`yW{5G(N)rSNvjEu7cX5&FYb`wl@f3&24$O0#~BL;w0qp| zMaZmF=|FTAv16s~T5ff-lYC7cc*fb~66A_@dV1%8jssnNf+q)ITAlBnj9z*<; zas6EH{z_K#jk&%u>cZcgO9&O83OCes-4QM(a=+|0&W$7Z?61!ad&Jbzv$A_D%+ z2@!gS(L3YVk`AQaS91lHizpQ7uK>6ePg)4+jVKc$jLBF&Yrz-4-ddv50Pn+&62?2v zw}%}K<(HJxZZ<+Wf?gkD+t`&0<_k}NDG>^gISDi&dDBT}*b$kkS!Zh>FzDtzZ2eO@ zSmy-8^&IOKOKsHWo^=CkI+6IWV4`{pN%t-xBDuS++#Yta!0~) zcWM4a4AGO(gnD4r+uEW%kXcj-U(iigP?l|x3f0B0a{VNg;Ue05%r=X)Kh)59FLlMn zDLhM}tO=v4*ohc+{rt>}E<&ApZox}Np8sWcvIzn;FKRjKzp8JsSyN*HqBrJid=b~A zE<(A>Sg_QGQ3kq}y@&@Z6&Z?ypA%(k!1s<~qwVJt5J`tMXkC#I&9w_-I5o2S9yLMG ztu0K(HjaW+2ml<1Xu}7jg`G0kj4lu;|6={TBOBE|o?4XM&S(r&mMTE=5V&I*D7@m) zolq*@5MJ>KWGf@%id&?39U(aNFf)qkRVlmz$IF&d{PRO5;n7ZzPzsIOUC)DR_Ya)l}iMMWZoC4HUkUb0CpVz+X2ettvH zn(a33~_<4iaS-B62jILYChu4*Y(_QH@Ifg@_4tD_ zIxERFy8-<~i-FAz&njoW$W%KMp=o=K!uD}Jgq7FSu|SdEhLVB9`1Hd0U~NBKGRm)? zxyi0YB<#DBq63}Y@q&p5c|}DJ^@d<98>a%cGse9{L9csT%NLdG%!;{|A6T~en&B@^ z*YuXh8EXE!uz)d6Q4RUHh!E#hShsj%y}CiKKQIPJBsWA~*lPkqHXePiojys-_}c_vR1DOQrKzdL8Vk(lI~$#0O{gW^(u%Dj~v$2OJMc`y_WWb0WyU{XkI zI{)ceXi9QYV7UU&YSfT}P5AUjt9%W(g-t+fo`DK~6hkv=X5YiY*IgO}e?S~59J!KRqH}eZv>rWBVU|N-D~-PU zw4`x|*k_LQ+s_YxD0 z`z?n!d71?PTym<7tu{WX(*yD0EXhSvTKQRC-}YI&TUXUle^H+~F_x@%mb0utvg3A& z3)3;{yR;DNBLEDq*xVQdH_Rr0WN#!%uZvrYLUY{E#pb@(0MWJ2;KtxsoPvu$izcw zPK^XDn^c@r{X_~_%q2KbN!D-jw`h1Yp>;gNOC>?4?M#e*`&D}_b-Z=-dSA`C*+V(d z$8w?vFuh&@wX8HKL6pY{;N41%g4Ve@#>$o52D zk}5%xS$;vEljD`!q@jj<`LC8bRKwVh#}+&+s+M~A(n%^SK`|pseJJ(Jwu;aMjb?z? zzqAOu;!K71aGDIl_1S2RqmHFss%|G+=uhY&?+p%1M(+inMk;cb&6UIQnx6f+LGlrC qy2SApw64beEjm`KkOK~FU)=&1au^Y%W^|-0<{1ktJtuRo$o~h}A}@LX literal 0 HcmV?d00001 diff --git a/busybox/docs/busybox.net/images/busybox2.jpg b/busybox/docs/busybox.net/images/busybox2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..abf8f0610b6d87a97accc655ff4ff5f9948b3e24 GIT binary patch literal 8204 zcmd6McUV)~(s!^RAgD+$g3>!uLJe?40Ygco1rpjpI+5P{QADH*gbv}-2~~Ovpi~J$ zganW-9YT@b%jdbzd%yRdd*Anu@45ecv-h*t%$nJ=_MYE7v)5Yt>ig9Xz&%ZMusVQ@ zj0~Xh>i}F`0+^n<+gN(qSU>b}@U(wuU~ltK1FZAnY7+1iKtWD^i~J_Vty{M!DJiI^ z>1n8M-==1{Pj{F80V^lR16FqShdg5Z54l9R+1UkT1w|f9NJ~p|^2;m9NrJ?rq$U4! zLPkkRNqw7|nTCd0@)7$Z$^U1%`UIe*Ad@1SyH3UhxJFBMotEsX3&0K_16;fQ=X?BL zBD;3|2Kmif6u-2p_W;+(u3x)x{pL;b8`mlSH2G6Y`$*y@`TZxlmTq)hlCR@$(R1fi zF{tXj{5-@XDAFRK8|8;=B`?yK|OD976OL>j##;=p?`pv&T z0ogU$>o@K{l8}6&dy~uZwW?dy5Z%-Gob9U#0QL1>#|>PdM=3$Vd=jRz;+6g z?pm`CID6E^ofrr~$DZO#2aQDA?Nn*as*!P810h}Box)uwOU}0QUNy%hw@-BQ^dl?` z{CvEO^3!E{|F+n+6;5z8)y=QbE91hB7a7I6PN+PXR!QvJgG1b%2Hg__>URxBJ?Vb( zklLce?B*n#g4V@p)|mEXU->3g-Y*iI7|~zzO&IS!$y@3p`JWf~2(i2AyE`(ZXu+os z*<0^0?@U!SxN%sm{&D>)Sz^|*v$!yGPYlaTC!6-`q2UZ|c_2b_Ny4}nmb)@4zdw;< zGp_Rb3RWenVzYI!#KJc`){V!Dk)6;fzMW?{oN+M`MGoITPYnp2xFIMgQkWimBlVBJ zhVUN}^Yj1W$Xf%}Ou~55uOm8R6~XK`8#IdZo-% zzw#i;iiA`M*4ocflw{9sNU)GvIgk&1Z-Ae#&3=tBdT%yzT#El?A@Z}?Vc%i;MQB&a z=fDB~sh1t^{I?!u-`EXYHWbpD&&)0r81=Dj+?U!)_hIm>=($V#6+7UFozuEBV}hne!8#xSplDLe;)HK809 z!T@iF>ATao&3AIX;Ap`{KY9xeqIi_>Wh#s99xqw`<_*S&G5q#hh>o&iH%$ia=HmB{ z4-~xSfLQC)8uxDG25pe8jt?(Zq%{V@1~;lIkUml`dTbGde}^wL&K#xPOPzBL`rta% z?Y-5QbA3MNzkar&OvmsLlwT=*OAsU!w zE8Er*^6hGhlus8F_y2+QKetceI>^e`Jc4RizKm-S6SR26hOd(#Vl+vlerb4nMq+k( zFn{MHcfj2BcB5+gtZq4bw{b82M&DEI02G}>D4NB3!i ziLWegRZ-YaP;M}9DC)fa(7@lsM8EOaFt$l5w+{9@Zgp~%z0#2QV;hB@U|*4Olbf0r zGFVC_7@s8nI5(FuZE>T%xjL`uRe0oADH_wCIg?v8COsC@f5uA{=VK9sQdbj zI&~T1;kyX?P({WhX5M?$9^HP4wzBsT-~(}`v9CuWe#wV3v|);G=j+3Is;&T2bs92h z^4qT#Ys`hlSQ6I`1G_Cf=OA86F%C{Fm0nmo@GHFW@up%Vsy4bdpqrHaWxYwAbZDdl zY4J{oWf0^WPKh@0_h&KXYhYgLwkxs+NLl}L{Qu<16O97^Hy!!b`qbv?a<XzYbVbk8m_oGXtlLbF$j+Xlb|7aATvvb~ z5osS(Ww~5=n1%~XDms?{CdCal)Td)i7dQj&4w-NPZSAxH! z#A3#%Ijc>#$c#87sikKAptvqB2#rc+GbfK@YzcFgI4TLDd6w4N95uMk-}-L12Z6ic zLrfyk@F4OUdCSTv#JOrI5`fynYTm#cN=D%tajyD=Cyr6T9K$?ut`=4Pj{ymX+V$~u zPvmIqO`cD%P%~>Ju`e z*#mLB$+j43;`SHa4v8uUgHK}n!f%qrh05M^2Wf909#xY+?@QMD%{qzkP;Cy0$m?>z znZNG@%;rL0m zEM!n&Y02i|pxdWWE*>tm~+LWFyDsX_VHte&+Re)&?`p_ON5f_xpN8 zp3zG_8@28x(Uv^PJfX_Md+tFWtF6%boWMYzAkb+`Ikny(6af-zIi4e|h*Ax9%tW_$ znGxRX68BC*bk}?@R#YlgHdZ@BQ^boBW9ODO{O?zO#`JX?mcn$mW9hg8ZATLzli^RW z=lb!>yO#~!Aj_EcQ_qEAn@htyR-mJ$Y9=}`Ce<|n7f*715f_cVaq@0UuqQH%xot>2 z3?l3Yy8{&C2OZC`!!OH;Hak7)grWuX)=cRf48@(1aVoK^9WF+rs3g{x0WG6n5?`fRhPP25Pfm0sC{Cv%U^!maNBIQm?pT>U4*R z$Z6(9#$nyyg-oeAtg?&*Zr4|Bt2Awx|DM&^6Tvkqvp3IFTfVj~l<=IuB0DdbR5i3~ zC)yun9yDKX*vgk(s$BOiM5p-7ZiA?B! zO3oY7`RPc(1iM}Y>IR*QQiRTom=0KQmz_LGnWOdOCF@}K4vbD6RMF#??l7zkwmzq%NnQ+^+1#rSY4}>ez zfrJMad*-}g&lc|T@DL=`g*3FIopeRCuEF&r8$ou>d~V|@krO>RX{);3B{3`uBoq;&Jc0E7iBVI{p?X^6-CA~=QV&=i zYHiP1T}h>K8hu|@me%Eb+x);6m2r%IfmS;DgPhipgV_+RwnsKPbt8hZh1~3wm^YQo zIsETmxICC@|2~I57|ghR=96*?Js|}8iX+d5_C0S0>$tVg>@F z7iSnPB7(D|L4y_}&4W-TQRu=Ipnj4gBb>9SPCrgn(!8TX)W;)P2N^5xXYwNjj)=Qr| zT9iM!mM4zW;xOd95xbEoz?MEZwAAM$DZu%+5m`!|wGqtZMdo&dsfFgH+qnWlLz(|h zo=}|?L|7Ed6W6$#LUWP!Nk9+fCNG9zbF*4;4h}qs&6}c`lOY?P|DW>p&%Qf%H}xix zAm%v;@y;8;|n z(L~FUJH<%G>X_^np=rt31f<%0P^|nw$XzZBt2|Os#p?>74l5@w7PK<%r=G4V7?GPK z>VV5tsy3jZhFRrygi1{`T3F+P^J}3gfx6D{aM|GZEDb?{Wiy4;EwNYS^R4AeqKupn zi+w4#42xz1*Njg6%=AH(fZO|a{PJF=;3klAZ`fFH=|CKCVZJ%>*#CiwPOod49mkTh zywwIuprZ~hwi~H?p(EQPHYEYgO_o`U4KYopU4@2xY3_|!+&y4FZwGB&PN(kd;A15v zA*AK%ZUiAEHuZwqsjn*aC|OApH;?kd*#SFV)=l(btys({>NzCB_kcFO2WuUVHL*+B zC>?IEwuv6o1VN$woU@+L%yd7e)EaS>97$p;XY0_Q?A~YitcJaY^TtqrpQ+C)zqWo8 z$(B<7=H53FxU!uVVozSlCx4J!45bHYRz;T>DI0e!7TV>=hDq#okEv%*i7?Ie%qsaJ zic(+7zcW|n{51G|YKIpGH^hA(enC058(G>5)2y#{(VsIiWNJ|>ILDLQyE<6yAl3ZT zXckWNS|Pvv*qIOXHgwdGF3%(G&Cg^L&$u)Nd8_51H5DunT%dX_zo0@Et)t%H_;}#p zHfiCw(XVm~aC7{B%g$TEhaav0Kg4*K*Fp|Ikz%bjsi=y0+$fYuch{t^069#<-_PPg zi;6sef?j>v_zCmlCmsc^%Qm zFULQ89=(e*#%`Z-a#>UeQX({S4?lo=dvASP+ZIP^JU&iHmRRB33J#iMS8u1BH*mHe z=jWN5&SAZgyw6$L1_T|p=n8UUlo* zUePi5RuzT=L4{pppg-$(ynBFSpZGf?zO$8Ob(1Ya-o1T(8VxyX?@dK@o8>5>^ds)l z{@Y(m-poS(mcV9>+vzwEIv8IWiH~C{%3;y<4-V~6Ox6)~jjT@`d}||fAT;WUSWH`T zD5*}zN63@>@8t=uG1s5Rlmv1e7V>UPL|v>&r;vbj#l%E&KH1RmqEDxKNyzuS=V`WG zzBXaNT9RM?#k)mAyk%QYx8m)RD2aw_=#D3rW7nX#y`zAllHKlr&ujUtGFMcxEAEl2 zRs_$Af~&bZ@q75qhBv{(*rIa`o0!hrVjj9uUfAXo;XqsWF(;f#9(jz`3a~HdhGd2i zZnQspiJF`g>6uOBZca)!Y_4+=>D>ep*pBI4Zj0{=-%MNOuT`u7mo0d~Vi|d~)$ET0-}r<(qWjQy+~}xR*nq zPwbJ^j&?w=q?GKJN2h(uI-`#QJA|!dQKJ@MW$kw|3s&F0-_u9cf6+G{b9w5XLf&3p zryO?vlYB!yfOBP0$@O1oyI65MiL%5TS%dlOyrr>fH(3nKl)gAwk`NPOhO*}B3F|{vWs8`Qt|ZH% ziwWoz02R`-FSQaFaZAjy8Ex{SVGUoFa;W^?dv3>XYW6Vz{^1`P_1`TSjSX=A#vi{r zn4Q4m~xbWJF)AWjjRn^fvt;zFZAk#ECu&=Xd`w^jxQE*lkK zNw9&8s&DT2`ZPMD7b&!^~+fE zVNNa2S2I=CN~@@ii-aTiI)fFenKI75IMo|@DkN^G8@ZwO-s-DYD`M&<`{1;N?l7IT z24Y%`L#Ijmmr7`GmzS18-N9YMXvUEzF)CAev68zG>9GTWqi->6Ji`Re_~fC0-Hx&S z$Mp&0HKHwg?#bwv2tRKXyP?d+a9T{9o2rBho_Aay5)=qw^^&!^&UXyuoJ3wj1f;fm9brNiqkyO`lercZ0 z_ziDpJJ6<7Yg6bkwOPJg;PPN5BNHepq`|>&>A=rszqK#;a(Hw&xvBH?!_xYJ(60>$ z){cIEhKVza%IWW6N}8e#)>s}PP#Ui1x%)O;Xi+LG9!SUN4rduGU_aQ9N(*`ELsKO0 zcgOGe{m9Xy^((-WN!r@AH*CR=>>j*F4$z6b(d+3pO&^h*I7We`)xNL|X^4O)4rkNdOsKwpB&kqwQ7$x zttCqpMZ7L!qqib}>!#WS>ept2vh@!G6*lu*Pp2&sEWT~rF^cDorPDK)Qpjt$8|{~w ztpKc6rmS{~S@CRi6fA@DFoTN>c!v#Tx@WB;Jhg8JpJ|6VA-1k;k zrAJNeT2hw0^ghB-uQJA#)H#(Rl3U7FehbFfDA-(2ASSPcgUU7Q(NYNhSI(aULFl#htKXy(_@sQ?2^!$3(XklFYM)Ro&Ht z&%!J{RPse4zenk{IGt--uINyC@kcuws_Xf0Xy@+se{x?ly9_gxMtn>zXwY?>hWq|p zG*Xs$vc#X9W?pXe(z7~FQSN;;E}E@&1a(yY>>K46 z%P&)0eQJYJjp;Ln99IDD*#aAr$r?9fqb>IiO< zKI8b&Lq^~$%&gzq0NnUzl>e1icEe|8P;CXma7sWQ3`5PHvSS5rwxxxT?vPAy90me> zI47Nn=+r*Z0J(?eA&k7goy%5aMV~yLO+GhG-~LgeuH}i;>V#tIgsR^@5GtK9c}~z# z@-#0}&ho(d*C}kV@e5O|CoQ_)HEdRhrBm4k^EbRko0u!{jDba$9NW3s4$j}fl(|Jk zn*FqRWQ-)If4AeXsu9OYH@jTs1G9V{^<4pO_fcYx=-Ar@wfsM@1`B|=#$KqKaKUjq zymjBBm+B*(c^q(vc>dUl6Gu3{065jDFa#B*a3uQM610Qg`t0rrssp!p@>DZP;EmjB zf0Vpcv>7&NoM0|xQ#Fq}H4@XdYu?LxUapv0WKJ6#e9OV^FfcRk2)E_BmA93p6E3!m zW@9)CVAVukS}%-<{FFU)B-O@gxBkl{=o z9;y=$hQBVh7cB=pX0z&y6_F%KRY33?XKyF#gd@h95@t)x1kdA#5ZjZ&%mOBe;rxW& z7W!=B1bLY=>`dYhz;FMG*1yXy8ykwe)D23yH6@K5(thJ)TY(q_N-mA&>721Rrpu+H z@^*MDb51pP2g44q#isiDZtnE&(qcBUNn!OWNh9_+F^UWNJbjenZJH~?j z8gXgyB1+FaBB#qxUJDOo4(NoEO&30SNx^*FqB{JKMRod>IlIKOf52f#Y^H0Jl~g}b z4F~m#=o9ObFKAKFQhwgn7@6cC1U}pf7!8c*bN~vYOE!Zy(C=!B@ZztkNsrOlvOcu# zYq6D|*+#M4krtpAHJCUS5l&d`k12&-6QqLlY-D6#b8cR>*0(W^6qc>3{*rdxy$rG6 zS{oC5Qm9ahJ;EZZqD^$6FHklLdL?5!*u}NaZE{v|#hqfN)kg@TTu?#1QcSCym2+yw z!~gwj_3t?RnzV?E3d|w~XKLwlfbY^W$#(8IER~#J+tPn)BMir4c(+fa`sZ{sJl z&!#tH+V9n6wGRB+wFSOQmn+G(C0cru5N4WL@-F1nmVKS=5+l8MOj)LnUgYczHG^^< zjDF_iB}3xsAt$IZozp3@Ieh579V_##P)TJeuo-i}DUkC?uAGRndeV9E z{76(8?<_QL6%V(QO+{ht2+mhN7S{~vp3+Q|R_ literal 0 HcmV?d00001 diff --git a/busybox/docs/busybox.net/images/fm.mini.png b/busybox/docs/busybox.net/images/fm.mini.png new file mode 100644 index 0000000000000000000000000000000000000000..c0883cd34c35133634e52808ba6dcbf0a047361f GIT binary patch literal 7708 zcmWle2{e@78^>QlQ^X`crpQu?$u=Wpgk%aCB$LP_(qtP=lI(kpWG{`K$u=Yn*~yk| zM%L`h#K@FMjBP^p|NPH;?t9OD&wI}Mp6A~0_ul98MBcn%bmZ`f!vFvrF}{8c&FwMV zCJ*A}-e1mq{>kn5ob}E00iZNqaL0k4J3jQ}x{W6Q2#Wr{@eE7`?{FU;d}eHBaIl}B z7mQH8aoM6408SPeU(>hr%`o%#`-ry=nsPS_xV+uH@!ijN!)&zAeVKC}!s`UfG5dJ< z_^~Vt3>24UrHH-@bCc4ikteErqXh(sy2NFE%X2 zIe&NVb6b$1Wht{m)(0*Ju-7uz(i@i|Ue#53(@TS!>`Nl53{)iD@sCKCyhM)=KCbS| zxHS0KOUydfFxrAe-CD!7RzJeUK%ro04{{rJZJb=878*@0EwBim2`w})AaV>a^AW9cXXf0 zgPsS6JXv3`5v%GAS~0P&VOLe!Eq!_L9#a$}a6qoBb}ltDQ`h1tRr!q)_9kS+b~G*_ zo2gEKvB>B&a17i6g-N<6R=af2;7wgI&CeKAiLX;O>D}Z*bd`bRP=#Mr$VBj|Uk^p1 z&^VhUu@iF7!2=kjGcj<&Dda0WJ+~humqipe&c! zZf@n{@565|O;nDy!an}H(Df8F-S3j&i0yoyV|ujec==Ivq9_%FG@ZB;#n zx!L&h)H@b=Nse;Ypavkhm}Us{noSbf(FWxa1E;3IyCDJq86lfj@ePBGfu4pO1Mo5D zqJ|*G&{l}DWB|zo1ob1a=6-%WB$r1-LFI_(&MGLNNC_v}K6FicYIjZr1ZS?i@_ZcQ zA!UN1aq8ubi|y(jTB$W?lYA@DYHEAdS5m;}Ld&R}-_Ir{6b~4>sr>iE zF5lHEDIA_vsV<-#trh1+7e|gku7Xk_5^`-Eol!416{7^rleyz?=Yk=vlsYDx$3tXk zL$qVyG!FRh-Zpo1C0vuMH@hlWkC7A`E zrk!7Ef^Qpbb0dSuJ}ItF{9)vZd_dPe6admjw$V{Y>(l2 zC&P{?v>Y}Do?m$d#EqG9SDA9ubM%mlrTbL9Ym6l(@A%aY*=sNX?CXpCEuzO4fFH1fS}{p5sn=t&A^; zwXpCwKUdmitx@Fi^1s;V_@G~I?{=ciA3uKV9iV-t@40M7X6Edv#3)y$Y>YxO+)%Rk z2pE?V4!R&HD5&SR7-5h~Z~!Y1zz=Ok+C0XD$wIC@%3Ylt9kW}|Gh_jW*iUT`Nh~Ol zfCKQMlGCBEvjp|?QCldEEU4mWs8Gvt`Y6Xv2#J$w3`0@(tc3 zI`(R@>Jk2be|L5srusTNM{rs=^|&52zqI_qfiqrGZUL+$_y^r#(?PkLwuf5Pg6BB# zd%L+JdOOQ~q>$p_IoXfl-8ElYEk@U&jvZx@JkbA`&lSmM8}FCx+#l2R+G-xGNX}I8Ko>Ri3#2(5opwJa)&eCFiU5c@amU_Bh{;t{RIJWK zTdUbi_x1EtVpK-G!WB6>)ogJx0sWzJw*F=(tm{4!B`q0?U>U2c`h|g4b zPIA;1`N+;PYHd%g5fqXO(nr3}=@<}GY)7Tq#iM!e-kk@flF3@C?=M=qL9ZPG-0z2d zQ^V3EvFh>g&S*<$HW5w*0Nw{&b!TY%s7Ae%P`fvivwUFGgOZ~d(dy#kqbJmAm^=Hq z*s@;ntxjJ!*X^KQN#e{EMC7gBGsw2G`tel$Giy^*Ve=Ej2de61`Ibv3L})9aPBv7n zQA5Li(3SJ_pq(Q7v!QzneR>}IyBkYOi}xCRSK8k-_-?bS^|lv0-|a3q>#e@o_oYv) zu!6UCW=DEPOKgh>d)kYBqp9Z$i0UDm%LS@-H9Ly|jIaFr(-D_H!J3FpE9>i4<>Pm3 zZ4pm@zGC^_rQelMwRO3e?lb8(8k?>u9rU-~yDn(;=hv$d!e-`X_Me~5NiqWKF%(lX zGe*Et!`i3D+MxOD2;wSN2-s*3*9ngT9qHOI#I=5|GaBerv;HR^*SM7Ndf^WX*dVqz2%1_ z#}#?~xYM1hsm>&x(B0i#r}8j3FD%w+EmEOzJzPy^b)& zv9)_M_Qu`?FKF-oWgnFGozALDmXB-W#JEE|+5TbdaXKgLL!7jwZV{YC)WJYf{c|UM&fGi8jQQZ2nyR_dd61;4*&yGJ^ z`;{i`-%HOlROg6J+20&GVm<7p(x zn>ajUVtylMcsRFdPmSTjaR1&^Q&ZM78Zt+=u5xGJH{}WSysT9P!P=HwQ10S=p)W~& z1YTXCH3$J=3-!vOn z9!eF6=>A<%ApLVvd9<*wfOlUSneS1uF5dfX)E7rvSz%UZ>V^C$Tnbs@{?J~h63bbe zLyOP!og>?ekpowpoBr)>1Ya-Am50OzOlG&+j#BOB(^`#NvDL-1E%hTP~8R2~4k26BXMe1G!c!tNg>iR9#Do+mc% zCo>GG%Se%aO|gnH#W&=v+{uQZ$KF~dzg)zW$T2IN3|8tixdNX9wL+N) zbKS6c$*(V{PBLCCpgs4Um{k4&JWb zXSWL`$LdpNhh+AC$asltiCq3i^T`Y-Cr-HV0fU+2T&9JE|0W$^CFh4T`R3>EwmRq+p4Zj2r}z)!j)n4mDbU*;J$Ue7gs_UVZq);!#{HnC z%?`%p)k5>#Uut%Hn*%eez3Z2Og~ew~54dJ^jsNel?(Xi{*(9uW{)E-EGfDEH-y;pR z!^VZ)Qc5P#xkjTp)tHB{6HZnC)x_b2LbDaz^(Ju_o<{{?chPq8B=c0WYlUUXxx0H= zb|p%V6KrqMKKVJ=9~xDb5(XdP9)H>0lGuuoFnf#641~vO#~ZA}o$cm9M$?Aw{)|sB zi$S4KCVdz|iKw+k#y>B*w@McefP~1!h<-Z}Sb~I|zSGmAsv*ht?@4`k_d8t|IN+Ml z*QfG4%%^EycYc0e=3mdo-HRVA&jHJcC4ssp&SbnG!hXk-L+9qSXvG4-?E6hJu?at5 zf26M^W??8+$w@ea)Ms@8#+L@p-4gvT{59_=9zQ$Zu+x>OnNvMEJw24H zmRWI36+T+sBq3&fyPv?afxCgVWWkM=MD~Qjz zKEK^Y+vhzEww1BF*9w~AEN&BSs?{&I95WA$RoBthW(GK%aml>w#X9M1t~ozk23x7= zI3M;#L!jZ`Otj%(Ympnrvj~lYdVmoE66PUG0p@|smBg!_=gVWS7SSM%#zeDIg0)Nb zyYp9-Ok!pvHH&!%B-1syX0d}>-Vs!@ZSr7N9aAuo+o)?T8XbjtdfEXOXy#H&B1R$m zi6!;x6XBAY?!viysYKZ5p!4D#ZSTMz-6sqF#MXvy<`zqehvyFowmM}zOjfn8 zl1g%7PEAkudiq={PG^z9)RJ%YkpF(U__)uTxqs&Tn>fnw0U3ns|1#p4-q>qhvYu zm^F!V-B@pDL@$tHv!R?m)beMeaepX~WmrB=%Cy;9iMRViK^0ldZ5ZuNfv`IZ<|Ou! zDnPKWI@jYJ_#^Rz(Oh$Ig{Rvy?Cw@}Vx>GggtZ^!uJK0_+nX8iHlb!RZFke8hIlA|Ra(X)cQwq4e%K|*gXHK=AUCo+HS~}oKlQrP z8`)*0>?Qw;a?~#bRbDE@OR7NGgyL0AHyg01#AK34aXB53^7{Hm^!3?UClZTd6^%X& z*d(=kSc;t!YPBl*#ST`mSC#4hNc!y10^91Tf7bM{(ji#z)u9%PUcxm|uuLPqa=&q zQ^_90yGi9WHKvLgP0KC!VYYYDJ`CdwNSma;^?)4m6%R~*x~Hd4htn$9=x#onsKIv& z`6@vG+o=`LM3~(=lU_^%pjSY|ehhOe%N#49akq2k=BU&s6$aQGlWHG)jj>%2Y4UT5N z^mNrY*OeeEc-je0dl0vDn^*72Nj>fch#=ji4C9j6;}>UI^1BZQO2vvO&)zWW`I-8q zCaKrXu*UtU3^EmfSMSvHA zO-tklOPyKi-6NsN&6j#kIoY_9PDjD0NTu6iMD&Y({U)PQZz)>U_cAd*_2v33ajRC; zWLdon(Q-#;`6}J}^T7u1^6Gl^!Fea5ma+Q9X<%AM%?~Qs1B=7b*7Lr^j3ZN&`TM;1 zM>CR*kqC(@HtvXx)%EP{hV1rp!>_{~*4iT9-Wp828sQ8$gHrF(4?2+JK7r9Lrz@=d zEPj&DMJY#}>JzNwMMYXI-=n!lBKQ0#bsZ&}2g-2M(uJ&G*K*X-LZ zA}lP>`r<}`qm5O`Xp!Z)HU9?XHW!UQ-gU0kk`*}MzD*KqD5!cS;J@fM&G*0KhA@=5 zx!#2*5Lf;<=2%saRyBLe@}T=;8*0iYG&3Wf_VvfH<&9x- zjaDx`i}Svg#fU0_TFSoVI(&bm0e&rSyuL2{vj0ZNCw9#cM@dpr(lI-Rn)C~9NKlMc zzm56HMMbr60qi+G$N>V7>_1}!r}~j-9)A!c@?328`w5~A5>I2U`0D()Y&)vbm4LpK zjW1Vzi%fQe5`ew~p*nrKM%#_}b!vaC}_r+eJfj5;;yrNuJNxZ#t3^p5LcVuH1O z(3;xi&4OV5N3gNR>bSAM*X7jfYdRS!exe1X9}lq_civ2F3|jEaYtl zQZj{GU!z~bSH4FrZKH90rGpjMnL0vO$=y#Njy9#AAVla6XQe3oECCV<7vQQ;4hRK{ z8R#pXi+Ug>vc0?aec`#73+xqW{R@nIM(!c*LuJi(VdTPg6|>gi4xJ&{#(d_t`E7mO zJB_;J%c5`Z9)KE189AmNOhnz}u?w|?Im9Ro#K`!@wF-e$+J16ezSa(P$^r!7^cN7g zg*aA>rN^K#NsdroJ8`tq7l?*0$-2-IS_!G*n$YwnDp}l<^U*XxS;!6aB#mm5bQC## zq#A#g%bMUA$wRMB5so6?KJS2B0l{7-BvJYfAK;WTfxMj6=UF``C`duTn=>e$`M%njrOI= z+8>8nKBP;)0RiL(hs+ydFv2&24=)e8vrPHkXy#7XTY8RS%*Yk!^o|yVvInHKVKgU3nO#zUGFL_drmXxdppLoHv6ceX zmF3I++RTF&x@hQOr+5bZJg38FqY6(i8BU9HgBvPG_jW(SP+pFFU@XmsC$gv;A27*E zN<|9hq+oWXb<;m`AEl&3Mz=~`YE4UGUb7@ZK|2vvq4?K7XE62B?~AshM_NA(&earW zva}TWL7in>;B5?5Fcx%#m7chX%aNDFp3NsfhdV?2&-NT6Q<*Ehy*HW|_Lz^F>D-zG ztB>KkwxcvZFG*q{;&T53$tjX;Zv;jd`j$C6#tayu+7D%?z)rs*2_hCGgii@Ih zOhgj?E_3DgpJAoULVi^Fje^ShGT+sq$X-*jVg%8FAws8U8D?TAZyd+V`z~*+cB$PJ zpWE0zqo!CoGV0D}S7+15auQDzrM^x~OsrdK-2UBv71Qcga2BA!b? zoTP=h-&OT`rE>JfTeu>nFacADbWzP#)K|*V_&fg<;s0Zk<(vOl&x7ib;WEaUs`dNT z8NRmW<}>BzoNM!+j`=`#81E-OWk4bncn#d7{PN?UI8+ProDyZv`u;VH+^n6a@(01B~lwap_#-uSGzU6P~C z?Awb+$d)?NX7z`V*I%i}F_p~|(Z=d~w>whWPOI<(2$KE7aWyyj6tRISe6j%#2u=mb zAwlh0;2A&CxlMVIQv?|G?4^!8nTMxF8Z07j9R>u#9Bn#Nxc*24LXRH+RQPiKTAznt zZIbo_z2peuyx_wvH$fIo#{r#x3L!`zZ*PGY&3W1d7>byI9NBcRLb*$)kE6rPFwZ&7 z3e5Rt`p^ORhB<+iqRA0VWsf1h{LDSdyZUk!1bG4PF5{Nsl=TbMy8!G>l;W9S{Ox%~xkNGaAsq6^Pjk1OSs$;FL3o%roao2$S>@8F{_}Fi5 zbQ8GzaA9%rtnt8yVLY9brvhQZgt)8+Lm8Q!o&A|=eB6R!B^hf-KB20j0;f|UG>`9W zZ=+lbnxs2$s#)*9y*7W>t!V$ke R+)6HBY;faRG4lTN{{e4=7E=HK literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..d583140348966d345f223c002abb7efb899f071e GIT binary patch literal 3955 zcmV-(4~+1MP)~Z4)94tK~#90?V3MmBujqBKU&w_*5Zb(2OV@E@C1Xeg9#?sU;+gb zC{VD$7%ottU=16LL4kq|6udye2Ex2Rm<=}AK*0tAZ=hhoGANip!2|-&!5I1u1&tZF zen<0Ot!b>%yLvkgs=K;pG?Er`?+kaZK$`BJ>Ri91n=>A4;{_w0n{V;~Pwe|U5JkV)oXt!E_Ri+jI|$ru=8qCQT#MOSAYm1zi8gW@+jrq{aq!`TDY2yD2fbS zfDnpSJ0=XH1(2(8AJ~;~mUXrk@?4Wl$E0bBHQKMGu#%!M2*87!=h+u6^uEPCGz1!B z?+5(0Tm#7Rlk)g}!&nOfgGG{SgA@{hCeMCHp8bv>{ClD(@=UJ4eu&kzUdHP;n`Pu# zLZLH^1#1kI>RF~SrUbg$LjTvTeV3)(cZ;A{5-jrvpyDv}? z6d3}L@|WnsP|PM66B9)dQc8p{2q72bt{Q$F7K%cXXBm0+Ta3NNnv&swHGf>t=~45< zS@z5R9#W)IHp|j`nYRjHHw@MqhQr~4JSVF@UY6aISA8d?L>nhV3W*et5Ui2U5~kA$ zvsp$_xLs|G`>SMEfAuxcT9YIb(yK|izH2H9Tfo{!Sbjlae^KjIDc59WmSvou|J%X> zc6R>p)`C1ZRs(7Q5P;EW#QFJ%!^1ntpRH&j3Li8w#X1-4QuCAyUSWr zx{W_#?T@7nipt%2*U^l?Z{8fSv-5&ZCtg^@+1VNA=OaM#%{OnEPA5zz|3RLAVt04Q zU@#yI6;Y(v+#C?cF-J#7?Ccnlg`wiZhg0G>rqhWRcI2Ybs2iF0%&S-Xbh}*uwzs#vF)EiJ#2S;y#BZTI*Z$$1 zPM6AzDLqA0=`L$KZ=j9Q$Z9utHSn_B~dKzSpzS}~nYmk%FK$Y#GSeQv||+W*1YwMI~! zHyFEC;{24rvsnT9Hlu}r!^1=N_xB0Ih%C$KcDrnEZ*zWrRth(4ZuZ&VcLsR>{)D}~ zJ@PzbI2>ZFlQEf0h@uEC`{>li$W8I5rK+$*6VZr@xLgFYruHlYwV2!*MhM(2+E>UN-%Q_5-Dp>;)p0+ z!J@TGuxGQyJo=%OZ!(#XWm)M`H1FRZv$wam;P0D^yK6Kvl`jc&Ns=%cjTjDxXstOt zJ@xm^W*J&*+U*woUWfkXb7Y{rE6lQ#^Yam{R?9PgGMR9BIp)KMQ#zerF&zFA0B0Zn zl{EcNtSM^^bAu_aG4=-R+_4-OaU@bD5VGj=?!R%ap~P{|F#;Odb>E^*XhJ; zZuS8eoHjSN{QZ?XS`M_$`|IaP5At0(Y*R)UDt`TI=Qbvj2>{QZ4;Tyvbh}-olw4h< zTu!E>Y2uA@b(PR+#V*~`wR;p%w3xmk>d zOY87HdU;XH052B@P4-nFG_#ath9mDGJTe(Gf-A$RmU#&ogvUlzKiY{q#-gpRZj4 z0wC7V#Wlu&k}fTjQkFW@{gXROj|$xCv&b@L7bA3%cp$<~OnWd`%)u{4%r3?}+uvW3 z*es(L8N3ScCdjL@3Tq8oYtPKf%Sq|}zI8#b%!l(_b8&G-YrRElU6Ljv^1J|GmZr4Y zG5vm@>C`d)<;x*KAel}k=&B`QZ?N28%pAcSW7j-#*3L17L6mN&0@=9^S?*R-2gCbD zIvR0#cu0FNp!0Hw3>8 zkBcHhN{I?2GH?Zh&NWH#8-#p{;0J{GjJ1y#ouAOveZr{4*I)1Bs=kb5I$=6-?!?$f zE`_smu*Rowmd6NgFcyrR6HtTP{99mjom6XVqr$V+kX?+qJUpbgzfZK$^9yYc21LCc zK^WEd3y`6rUHVU*BxK_;?VarfnTw#;_gV`k&%Cj22jj)&l2R`4TwSGPSynE}u}KCl zAW5kxjAOm9Amn3Y*?A}m!+1O*kO4vl?wZcXa*Zi_7FvJj^|h=yzyVR}Z;HCicTBFH zTQ#XlR@cliWml~R!03$e;US$DFNijJx3+Q+MJ)T_>APd}Rl?@Gw_Kf`VzfqnJR?sM zgn;$u15~@ksv!hiy+3isN|9fVF}X%6#rpFtTF(bdK@_^(u4_$}JEyr&=RW3W4H$zI z&P;`vBUp2~n;Qx~VePf=ya5>dh(dpd)}IK1r&uJ`YOF1blClIZ%r!yqqy(BJMmN@0 zD}CE<&d$dGo}tVBa&|HD{(F6+=b317P1UQ=NrFxi5ANjf5E&};Rq9yTZqa$Qk5tMl zm5s;9P|@1jLMTO%rd+*0K_>~_SNjWK$8k)n)k2qz{bgpEBEuA)uwsr8kC3)(nN~g) zS5aGxFQ;t@@+>!~phU|U4|3%;a%~W-l?HaZM@otGeUiHMy1?k=<2*@NZ@7yJxaqsM z9;j-_P_e!ddGNqEz?7o9zmICSn0-8BdVGuw72Ul(EO2#v%;nenY`l5vpDmT5dvHKd z8qW3%K|5x8bmWW{$6m)EP;6}UnNBA@2nNC9lFg9K{u@(VBjj4?C;tX3-NOB5v~@{s z%#Y~e6Iu2>DpW-4e}}b@%~J0+z>pUPW3CB;ugYgCQ~*IB5mGI5IeiK4P`@4hg@AmT z+%7hRr2Tvg6~~xdGd(^o1sU{Jf@;V9p7`Yp&*o@ri|O$(t>**6Ue6olVt3EqCxk@B zaSd|i63SkL(fXF2?RLw7nq_|R)*7-bqtM@BF<4U;Z{{(={0E|VC6j~5XCB@+3-Bd)wV>tE8Vs0S?2}!Lh&K8N z0V7G@J8*u@bN_a!B1RBMqDT>i5lShr#s@^KA=d_7XoPr7 z5J>;$!ER(4RsY&rxjK)o6bsuiQLo44H%D}|ruDpT;uKj%a(qlS9uvQKfdKN-oe04~ zzFj@IxJt?gEau(SA}ZzdRZ2b{d)DXUF|)HX+B@4`-l|kkN=dujA_yc&l2FVvv)S*< zV#5VMR{<^>yh;ZHrBH!H1%HcF5duw;8VdahW9FsdObN1c8BKtlpDtf8$T|gG$U2&% zxrk@``&=F#GJSW<^xZL0uZPjv1KEDQMeDgMy~B13sg(aN>h%^&$sMu?*VZ6IXduJONMx-B49q@5XYjX?3Qh@P2l{!lDKmJ zftnB~(;n4s5ymg-MFOjc<+nD**ZNHs*2PF2eATnA2g{Ttb zcJoDU^Zsf*%RP+c3b0Bg>)26MDzZ^^d!e@l8?4DD$1;Tw4p!TeB!yW^xmciauQWJ=~C%BiK%Whz~ExPJ?tD98Ia~7LbQ2*Sm`LlugX9TO&Y^IOP zz}KJ7-K^?1rm5?}6oo;|W+|7K6HZT0nNBC(4^@*ZYc(TkbHR7@s_z2lA%Gt0BsRfY zC2Lhz@V@h9dCP`hmfHmXp&eQ!8PN##rE5#$kqHD5N-wC!4?2X?u{x8ri5!RQZiLC$t N002ovPDHLkV1iRUmPr5r literal 0 HcmV?d00001 diff --git a/busybox/docs/busybox.net/images/ltbutton2.png b/busybox/docs/busybox.net/images/ltbutton2.png new file mode 100644 index 0000000000000000000000000000000000000000..556f72a6c15c3f53d2ea0f481e7cb425daa863cb GIT binary patch literal 6799 zcmV;A8gS)_P)0T3Vw5+Jym)J8G#N}CzaOeQnZ$aa;Tv6PxB+fTO2 zFE0Bb53U+7dXb&P&e#=Cl2N2_B55LOWJ$3`E=iUxkrV-vG8d5G0s;gYz2E(wB@f5y zp;DF1DOA^8&-u^)f8Y22_Z+~96DKN_3II?_;r)gXqLdOs2q6H#7&A>%AJ97*Ap~QL z5Ylhe>vaGql}d~;0MM^sj7`%tO;bOH5W*M(fMFQLVo@KI%VphzZQB6gd7l1tT^9f> z%L0IEwWw6$8q#DDW!&CXxp?6g+f7FMSb6Y z@WBTwl?r1_N~zv&gph8x>$)x>B+qlpvZR!Go)ba_6Vu}=iBY}=;&x1$GXEPinJH>aA}$fAq>N?Z5tt!=Q-y*P17w~wq#kx zIgg^q_kG>7c7&8tN(lf`N+E=l(zb2GFm!JSAw5u%B%bFvj>9?6vMf#0JkJY-0z&9V zKl%|`US4iCn+Fdblv3(<03gdU!!Uv%&?Y8HVi*SJ93doxKp$+{7kQrBwjD*0<2X`E zjByx-mSyF6E`-q0(_JBiOw-guWLY-AAONIk${0(M#BrP;2r$Nu_0A zvQjRWF~)hG51^yX62~!|IdkUv_3KKhD2jBDHD%&BzH#Hm zH@@)=%d%Y8bsVQwtNrw+Kjj~6d7f*?DWy)GIyE&lrM+|T;K5g4eRaUTEX(vfbxV>Y z3`2caN~ycjew0!sNfJd-6h&be_Ika3zn^7U5CntQg%D|)>g(F4gIjSNYh#im5kd^E zr)jF0l_W{4)f!xHHk-F+HHw?oxO-gB==NeX;i2xACaih@~8XD51KnS_6t8u4g zdoUJbOi$9XtSrkc%hK%Aoy2jh2{qu0X_}gDj4{qRV@yja0CYMX{aY*+wTf%F*|tqJ zGPV5b18s4?-_P@0-!)Bh;ORT1bReQSEaT(jk3Rb7`1rV#G6({Vl%b)aTeog4EiLJ& zYUU3O4RtylAw;9m=yto))6*LEcinYY5CmF5&!0cPwzgI-m-9ULJg;7_o2F?PhEnRi z_ukWV(172uV@IV@X|-AxE?m$=&}4L7cYb~z09IC3*4Ni9%hI9kbUItMY@w7Yr8Lo^ zC@PgoQc5^^^5msUm$EF=3f%Aa2gGSMo8SKSw+9)4G4{d>FQjRzALg8k_nU_6>#x7Q zfB$|Rful!{zWCycN~w*FjmI8)Oh?A^Jm2?+hll6p<}Aw^1Yc*n+1c4Q-+WUutJmv& z{_~%Q_p|l%^z=(Fy|li*u9Vu`+PN(zo%P)WGQ=if))^%OR z*k?cc*`=kWMx*hSuYAS#eaCTh$d4R362~#;JV}xyNi?~nl*M8ZYkg*n>1i8=p);J$ z#;sNhA;dY?aM0-hW1J+3RwLcgGzEZKt=4L_w3Sg5xvndvtkr57bUFn_QM9?a$vMXu z&&C&a|eCIow7q)FTo6S3!B7~?`t4gU-siY-Po1}%%w(Vobj(zWY-#dQ% zxK7M@onVHm#h$}3;} z>Q@Vef|OG0OO|EbZWjPJ=elapz4dxM?N*J=Mx&u;qNmjB^>iIDH#hgqZ+`RW(W6FI@qh2zJM4-XFuA#@7Y)q}2U@SUaCz!wOiG)>bqwJb{r zk!6|Ys^d69h$xD*=nj&(es@p^4La1PI!6rdmP(~Pd-f=$3WdVG_ui`u2dzX1AW2>>KK%2a|9oO%qTOz%X{rfQC=>wT zvBw^J{`u#9-(Oi-(HTSAqwA^Pu4+SY@rBczIXw0|U?U!D9Nn6!!xAg=x(1Z}@&Ydfl%dYE| zN~Nt^x1Kn0;tOB+LZi`;QtAMBo<|7z!4H1${qKLj(P#{+{^8+a(=>G+)Ul)kINX^v zwF@}sypM3JiKmM`)1Pa4Y``EVa&wcK5U;5IQw2!rL>ZX|)$Fc55kD}#< zF;=Nm!Z7rGzuj(kyIm<|zuzC!+JjVT7)Gzx+t}F9a9dbdXti2eO5-@zj_CDz#bPlG z!>@hqYfnG@bhTRTcDr#LhheCtx=<)wx^(IB#~**>kw-K=+wJzC!qSsfO3{Jr4Zc1G zBBE0fy#Gl80B^qerVzq$oPn_GT0nDQfQvzInqL}djInaL+-Nj(4XQ`!cDub^uib8I zr`KvV{qdG%+3f7>*x1Z*3)i4!N5mzUSp)})jgq=iBuNs_@wV6)l$$xnXr)KgF8 zdEV`I8DkGU^w1-ZJff6({PD+s^PAsbj60pq@#Du29Xj;&uYbK>uTx63j%ub*O0mR9 z5X1pE#tMKaz+B*5K$f}$Y>Su{Rx$%2hU@|rf&!QXBPo@XQi7Dw>GXIekdR17%q0cD zTp|uwASIw5L>v@gl=HmX?-#P|NOK6_#b}Ac^1bp zGmS#2_!nP%^w_aa?cKZokN)UiRfeik$y~{I-Z}N$-#_2(2gEc|!GVyNCmBxxB!EP( zo_f0<1lxDbA|Y*o%p@%%EW!XA5aaxvi;JhvpBI_*z_k-(8D@yHej1jlwe@BP8#WhW zyk4Iit>1so-51WCJ$H6tv(dts)`mxR&dp8D&ZGq1I{DV(!n+mU>$ck_BlY^kM?d^g zBC*M=&9%n6@19y&y@^dCiFAGM6URSIC}Wg%Za4C7aQgMP@^0YTPCre#!Aj%f#cH+N z4_fO@918@}wCU`gUAuSfTwFYJ?!q}mP!PwK>)BS}lgIw0$(YFbW5 z=yxzuahw*MLa!YZi@wFII1U8}V33KB5lR%!!q}juXW89Gvs(1AK!7onO3h9?M2fne z6aWHTM5#h~7TJ!GWN{D&naZeTSdPncND^Lj3qda|l}Z4z(Oma@hhP>5sb$$2l8Ojw z(j<$Kkd=~;GcmJc2dz84FvV1<9CzazmzQoVp7oN{L&{=Os4NB{5C%j+FdHn_u-fe= zvaHqO(&e?)@JS8{wQrHN@83!+HDb(sN`q6c{n7%Q40&lP!`NN&1zqTEiW6i0s&@V*c4HOM34y#6y-V2 zRdDsj+kf|Le`T%J3nGdm*ZSBW|AFB#Vb~a`TPT=-D9AJllv30&D8bO>VB01m5>$}I zy7fTHE> z*tQ*lz?E5zP&bV)pL{27wGD&ycxIIi6v(nW#e^B;Br6bO8WF zuF{nAl$gwP9EKrJ1wauH<_=1Tpp*fCl_5waY_z~Ih(Spl5d{$TK|*wKxwWuZK-9-5 z3X(l{?PH!LU78TcdEd;H3kFDO#u*R1|lkf+e?K(S;RREDuxbb*>_#tnliz|<;wQ5b?0q*9!J;J*1odqHNJ;L?yf!6Lx;ezawVBzf^C5mfRH5xv^p!7FUCO__j`hKgEHH;9m}M| z^y)J(RHm*K^jp}*t^`mHgo(Jm8b|qN6s~s~L*-%#lcCw&!`K|nlyBiKA>1+!?U{Ed zMbrp79T&57b#rsIfm&T7PhFeEkTy0uCaJii_2Q9(1%oPr5tkcposMqat~iC2cEgz3 zvg4kEg`yQDu^}bO^W@a&n-?!rzl2JqwJ@68Jy+i~mnEDrDg~mOJjhiV%9tpyOai6@ ziUIFmf918`EnRT3)b&d?!9(L)$}@EsDYWCXTpog)Lm0#LTWjx~hb#};-RGWr4kP55 zwr^QxB6z2Vql8f7o=^XAdEcyg?C?m@1Ds!3SY+4Qa;5#dH%_ zCO22=2j=|o321iL-+A}cFMeq$)JS`UecP*J#q#bMMY1qU8s{%u_=o40&zxOPVpS-5 z6I&lJoch#k!LtMhr4a4riQ`(V;0c~|v&2BcHW}FL;_{LN@H{UFg3UCn7hOD3?aLU7 zZU)lGa!9g#`TC1L`I(840vIV4kYN;Dufz|30ra|&SFO~xPC}^wCgZ?z&J6+b+=XQMW*r-=>#LQ?@wjB)d;Hkc z-o4;@aC-6APyWyB%1vrw%99Ik|8~~{d(7>ljgx0K&McbE$S@0i-uJd``Q)Py+56|S ztCvF~T73PTnW;)=v)?Cly@bZDl52#`cTd%~RJ<^Lid2!v#8fE*G8e)VUoyHB#H3+^0xi^CU)!sY6MB@ zSQU#xe&cul?FT=yR#r!CyY5#qBp>|TXXuCafyp3AU}kFn$L?QBUp@Ku>EW3aQ?`B^*wvRbAVvRtoOe+C9P1KM!8DUf@wuz z=m`o5oO|{~pE|AqjAtYDiQRYK2Rn8G7@lEvqQIsW706J^M#w-?fgGRSO2@~*GQl;$ zwUcYt-+b?E9=1pJ?^-iRX<`&8{Nqcntev}1p|)kok!r0g((3kc!Ezx}((8*(k9UHZ zv8mo>CzWX5y+`U(+ab)t%PZNLYd`&WkMFA0rJ=$yZ9%`jb=czB>RSu*{wNPZZm_Yb ziJc$XmpN2g)FRB^r=GEwEym6{>hd72nAY3Rym;;W(qwgnWO*nuHaY_1b#UAy z;g$tyQ;>+uAjc{9J$JlVXtZ1B-#B^S=r;Jo#~~F9|LK3c{fn0hX|j8K$JOQrt$Nk^ z6bNJW+HKn|nhA21>j-P@)Y8P>71+5=o?SfoiGrwW7RQPar3d-{(tddVcHoO8JKC%o1C3n zFil0+>dn<6F~=vTlZ>_nojP*g#HWtK^b{Bd@KhQGq67d@LID7Qq`jaIhM^Fco>MHA zAcKCV=Ne8BCTS8y2C5evs8qnT1j%y_W`O{81jWLp*IVs?cOBcy^7yI0ed^X<|4bP6 z^tP>jWvGAaMu(u$`PolD@ zW^-!W7@nKQwrNU{pA}sZ(P4oF<7pIEEHP~?eN&45G#rp;1mc0Yv0Tt ze)Zp8`RyAgUw_rLNh)Nc(<_XR$BMjj^IDp8?>oHj;}88I)F)tg5)uvw?*GDPFE5;V z;qRVl$t(~0c4Q%m7%}GNXKzw;Ici- zJAA-7ejiL#c#aT;6o|qA0EtYIkc?3R07#}Q&eB-& z$fbpC6ZYgpj3DA*Q-A~<1fViN39MdQKEE(My(4J%Ew9$@r5uymmMytV6=cp>6+l65 zLZJdl0C@_obMy2m3mY^?IF_Z+djIN;LVcvwSVINc&H^E&k+ZG4=AcplnM0PWUAi<{ z9g2b;vPd^ioLVV}BgT~nG`hA?uq|40TWL0Q@GihqW-9CUEQ3A!SAUgXz6`BR#8YKk zJ3smH0}uaMULS2spr2r&I2||L9z$GZ9C`P6YIR}Y= z7_hwj>l6QQ=8e}22)9}-=GyxoIC9sg|G?O@8w`?2Ac$fG0+dqX_X~GeVFd)?TCGNf zl0+#6*fLDPIRJ1ITYfdsyS5ksMo1|H5=)5zfyxDdzyN>&jKLDI#1a6sDbSk$fDq`o0!RP_fB^nKi|_wz8>~+2)4?{);37f@q=NSs;;EEM zO7y;u;e%`R000O$fIwi{4j@D(CwCs$H!?CjQmL%1t(R)m%FGN*)`2oWfEc#1#Q@O% zH6j22tgo*NA?Sq*7Zw&47SAj&#!9Z2=XnyvdgC4;q!1_s2qA@#LVy%tDL!b?KQn)I z$MK!MfIIed?%e1h3I-STKKY#s(RaQG00KdzfHA@nZgK8oLX!jq@%s9@X_~jM^|R~O xQp=1e>0>3CNlXKh1mr(iGU$0^WTe~e{vYirxhCCM;qCwc002ovPDHLkV1idL`eOh9 literal 0 HcmV?d00001 diff --git a/busybox/docs/busybox.net/images/sdsmall.png b/busybox/docs/busybox.net/images/sdsmall.png new file mode 100644 index 0000000000000000000000000000000000000000..b1024501b624641e42d6e9d1e0b3d597da84d93b GIT binary patch literal 1593 zcmV-92FCe`P)RZwdy$XI-reo)?=(zKEi^NPhln(j zl98jMB0N2OuCH2$hckwTIcsWQqNA6=z`nx5M5Cf|qoKCg*L}LV{r&qVOigCGyc%3w zLRwm-rluZxd1QHcM0j_$(9vmXX-!X0B$Jb(wX{Z&ksKu?8-|4%Yii!#-(Fu}M$5SU z00009a7bBm000XT000XT0n*)m`~Uy|2XskIMF-IZ0uVO?Vs~&a000F1Nkll@j_YVZm zGejV=aPahYAGEVuBmf1Rddl+(|RVF8LX(}gC??i$kwyRc3wLXDe3A@v}SsF zKqQqT6%HDjuG?j6u9*=@GDN@ntxcW{Y+9M}~7(KM6xt^D2Fn(=*6F}ZN{S(be zpaXi?8fj)GDR0n70gHJDecf*%=B2F%oNRnwjg_N$YUg^{N8bV-<3j zw@j@6b2NMQjVO-e=vv;<$14tuj_xAkAG>iJk9U(AopPE`sSdG>&}oZkLPig-@O8uk ziJ0JlLR%lj&>Jox{Yb3io9Kka}DmKBAiybc#Qc=I?Wulzibr4d)BEF+{Go&0#6%m3b$=5+E9ubmfbq$F$ zR|#&TURORCfLt;MOFQH03shfEL=%XK!)~-rYq&CnveKGc3}siLMYX9*ITlrLD(dQv zWUNHpq6$v-4J$2#c^}i<)~Q<{c!p@^xT;Tp5w-%X4vPE<ZKY=`on)7pQ%&>?w~s(uly=8t+u{Eimj@>qMgTE91X9khZcl5izL zq*Jxe7|WruBkFuTn;@PQ4<6s9(`lCs2PLcj2aRtxpes3FU`6Pu``|+licM$J%TUv! zeYB!C68}Um(6okU&}CQL+pD?}dGht<`&WwF_g?+@YBC?PC=aS{qQ{-bUX8`X_tttK z)v~{VcIKF^&0VU~-t!hZJP7Nf{stN@+KQj_x5Xk&*J+wA7EsOj{YHET-6qHiD&_?;CY zBztGX|Lb+H=bqQ|JfG+JKA-U(cX1ZxhD;ZEFG3&?CSxN#ORz_RO_ZJ%d|wEcBZ3`` zla84V1X7XAaQyH*IHt9~X{ZM|J^Q?AB;d~iJ&o@BKp+fkXB$-*29*L%()k*j>C=(V zQ?s#4lXfgY94or6?rOG+E8jiN&M@M-?Rt+(aHC!Ds@Z=XWpl7x>7<;iI%ZNA zgwG3|*RuE^yeTBfE}SPl<^qVhar4+^O4;1Km0brn zCjq8YdSbXcF=lGWx-QoeRAp$}X5;ltz3e@|Nl3Agf2+UGo=+K1%iwB>KeyP<4-~kXT}qwrsc@YBlYd82a&lsP%y=Ww@6M`vg||NwiAdgWzhgA_{V@bD#&cyUclW^1dCUEs|5!^6YB8*MBHYXQwY9--TpeXm}DfQW@Z zBPD#=p}GD2=3G$d0f@-T&BfT;M`Dyo8ykud@bK5K`8zVgc1uuV{K-QjBTZ)4#!8+r&xb~F3{wa6;W1t z3tQWyR`kz*`_mHKjFy&`k%IJTX=xT#R#C{(Z&+gf$m2Ti`HS1z+ZwOTX=!N#*Bke_ zd3f>(gcu{yNMz~4LSGIeBjXDr(KO8td!@spBOU<(Cmb#t%RlRAG$yA(UcXzk;hkInJAh<0$ znij^-g2&_A?Ug_s`}+D|M=L~d8N3evZyd4|!F=UvJGs!1yyQDs=i^jDLDEqvp3}l? zJ?MINb*qX~^1hF4tw+y((6ds~R0d_nQ)43##>m7ZWXzs?eUBTopncAZnI9&}FzCF2 zYh+0ii7%M-`&d>cY|LKc{=;(Z%LDlx+;s@;(};qC0=K#tWGPppT{2ks&;v@1hBS0N zzjw1vIg6CS}qiv{KlqoommsBw2iLrt(q?7>m<1f^E+NA zslB;4=3ATM=Iz)d|Cyq-Ox+!RB)451TWu|S2AP@(P0ybY=9qJN1Dz4)=&DziHv>P7 zxgO6#n*~|vzYh=V+1OleY;4TE>K6|eY{^n_`ny{m);Bph>0iA4{ml*W%J?KvXj$pO zVz0}%OWysGe|}vF#S<9W{1>wnA(O0z%q=Ej*c%)#X*A%3fXEV%&xxrw!VMI z&hGiplHMhKfE~}e2Wc4@?DRB7grSx3ssv|M6H)foCEo(Fvo#VaoNgkuCekuHE6mBs z$<0XXm|Iz?+>r>{u&}V8m}Tl*h5S16X;A&r#fNG+0>5X?OieuxcODIQi92Xls^MrN zW)(uHm@1>8^eV~E3rJYwm2rIog6@qy<;s@7!l=mm++2e&3$o2Xi(*jwJ#PD*b%(=1uiPD+Ho? zN#p#>KI~%`7xgDokE`7G(=9~8wh-_l@~6{-#rH5T>KE!TDkE`r4AQ2;*B5=*%JO1z z%}<%Hf_t#er@hQpB*O@8=+AaW*);${qN zw4>2!S>Fe&1!fa4!rc!8>Ryt99F~y{K|FV*5iI=eNAn8jB<-FXq@q1pXP&Z1;Ij!A^1#it8`K*8;OwG)|sJEhDfVHP^%W@eR z8DQv(h(tUflJSWN%0YV`l%^$i#Va=a{w1C-{z-v`bej`3BuAq6)`uFXk0AUiUZ=r3 zYQ!SeRqZB>->L3P4m$W684mH2j#5$>>M~$D8PjxKeL6NkZ(AE~UZ_(}!3VDX zZ|y-{5q>W+YJq=#@rXTD%Z`jR$n?Br*PB7ghCHTmV z3x~5{VkzvG=t>RJe5^*RaIKmwBO{ae{5inxyAL0}+E=)a5m@UuC+ix6_oai_!VeCD zK*bfg8Sf$xl>j|p>XD@x@$naXu`1!SuU1qg#~58MG*Nu=c)8K_&3PGwJnY`aL+^vS zL;j#=9Sy1E|7aN)J_-g4oaf$N-MOwD@R6L5kkGKzEpxnA)9e@;YRBcSIygMsjylW}Z4wD)vTDT+kOkItpkS*rNG zTwEOVxW!p@3owR?re>TuVU8q`hrwc_LC!$*fzAQgf+0#^ zBPq?>qBRY@zp7k%c`siE&F)^r{X6vIW2Q?=O3I0~KFgx$ zX6E-L-{T#Eb_bTYIVW)XGFj^|HX?#b)OdDpbpi~2=oUWj=PvGGh9%6%$!W28iKA&RJ z%2AmZa`Q%ip3~l}{sE14rbwzezV8(Aj^XWq(8OZjrwHeSj>v&U-I9^5so?dB;C*)> znwo(dZz{8XutcGCQ$^<=u>9()C@W(w8TmjX{kRGlv{^-&&=K-vqoGPSDcX?>yykN5 za+1N*G1t(ar`nb97fHNw5ScMq%sQ@HIH392S0Q-BFHW^vhi&3|B_odkjz>mD{{9Os zAWH%V3X0%n94<(IiIY=mmqDQc0>5_;3fy2Pf3PX}@B8;sgOA2aS!kawtF@J))pC?_ z`t*d^fRnMdwbd`Otf;N+0>bBBW1|U|l>LS70rnIy)w9`Idtkk#6cjq`D>hD!)&U-YwH$apSakklcGJUeA9v>ec+c|f}U4@3p1t66rUtob8*CupC zQBdm4uQt5fdI?=Qw#v75u$QN?wdFHg2O%NLHq*lV$>rB7E3IW!)z$GNjM@{bQbdVf zgu44Adp}g=1qE8{N)Z`d^#_{d z|K(Li1{Yw@8BcEDWps7Vx!2@?5=6(uu<56qqhsFaRT;2Ap>DzW3H9~abDd!MPowe~ zTSc)+LGd&br*o!M6I$Je9$$BMRGkK1|Lg74^jTnxfTNuZ@1L3hdobIlK7HKVdHelc z8fq$Z(vn8l39R{eUO{`i^_0TO95c^gocfl63bXxCUXIH7-*H$_?fm>HgkRaIFr}&Z RIrtX?VythjS9Sv(`F~ZGgO~sS literal 0 HcmV?d00001 diff --git a/busybox/docs/busybox.net/index.html b/busybox/docs/busybox.net/index.html new file mode 100644 index 000000000..753b21265 --- /dev/null +++ b/busybox/docs/busybox.net/index.html @@ -0,0 +1,376 @@ + + + + +BusyBox + + + + + + + +
+ + + + +
+ + B u s y B o x + +
+ BusyBox
+ + + + + + + + + + + + + +
+ + The Swiss Army Knife of Embedded Linux + +
+ +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 +embedded system. 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 kernel. +

+ +BusyBox is now maintained by + +Erik Andersen, and its ongoing development is being sponsored by +Lineo. +

+BusyBox is licensed under the +GNU GENERAL PUBLIC LICENSE. +

+ + +

Screenshot

+ +

Because everybody loves screenshots, a screenshot of BusyBox +is now available right here. + + +

Mailing List Information

+BusyBox now has a mailing list! +To subscribe, go and visit this page. + + + +
+ + + Latest News + + +
+ +
    + +
  • 2 August 2001 -- BusyBox 0.60.0 released +
    + I am very pleased to announce the immediate availability of + BusyBox 0.60.0. I have personally tested this release with libc5, glibc, + and uClibc 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. + +

    + + Those wanting an easy way to test the 0.60.0 release with uClibc can + use User-Mode Linux + to give it a try by downloading and compiling + buildroot.tar.gz. + 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 oss.lineo.com. +

    + Another cool thing is the nifty + BusyBox Tutorial contributed by K Computing. This requires + a ShockWave plugin (or standalone viewer), so you may want to grab the + the GPLed shockwave viewer from here + to view the tutorial. +

    + + Finally, In case you didn't notice anything odd about the + version number of this release, let me point out that this release + is not 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. + +

    + The + changelog has all + the details. As usual BusyBox 0.60.0 can be downloaded from + ftp://oss.lineo.com/busybox. +

    Have Fun! +

    + + +

  • 7 July 2001 -- BusyBox 0.52 released +
    + + 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 many 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). + +

    + The + changelog 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 + ftp://oss.lineo.com/busybox. +

    Have Fun! +

    + + +

  • 10 April 2001 - Graph of Busybox Growth +
    + 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 right here. + +

    (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.) +

    + + + +

  • Old News +
    + For the old news, visit the old news page. +
+ + + + +
+ + Download + +
+
    + +
  • Source for the latest release can always be downloaded from + ftp://oss.lineo.com/busybox. + +
  • A new snapshot of the source is made daily and is available as a GNU + gzipped tarball right here. + +
  • BusyBox now has its own publically browsable + CVS tree, + anonymous + CVS access, and + for those that are actively contributing there is even + CVS write access. + +
+ + + + +
+ + Documentation + +
+Current documentation for BusyBox includes: +
    +
  • BusyBox.html. + 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 lot of time updating these docs and trying to + make them fairly comprehensive. If you find any errors (factual, + grammatical, whatever) please let me know. +
  • README. + This is the README file included in the busybox source release. +
  • BusyBox Bugs. + Need to report a bug? Need to check if a bug has been filed? +
  • If you need more help, the BusyBox + mailing list is + a good place to start. +
+ + + + +
+ + + Important Links + + +
+ + + + + + +
+ + Products/Projects Using BusyBox + +
+ +

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: + +

+ +

Do you use BusyBox? I'd love to know about it and I'd be happy to link to +you. + + + + + +

+ + + + +
+ + + + + + + + + + + + + + + +
+ + Mail all comments, insults, suggestions and bribes to + Erik Andersen
+ The Busybox logo is copyright 1999,2000,2001, Erik Andersen. +
+
+ This site created with the vi editor + + Graphics by GIMP + + Linux Today + +

Slashdot +

+ Freshmeat +
+ + +
+ + + diff --git a/busybox/docs/busybox.net/oldnews.html b/busybox/docs/busybox.net/oldnews.html new file mode 100644 index 000000000..d97bb2684 --- /dev/null +++ b/busybox/docs/busybox.net/oldnews.html @@ -0,0 +1,465 @@ + + + + +BusyBox + + + + + + + +
+ + + + +
+ + B u s y B o x + +
+ BusyBox
+ + + + + + + + + +
+ + + Older BusyBox News + + +
+ +
    + +

  • Take me back to the BusyBox web site. +
    + +
  • 10 April 2001 -- BusyBox 0.51 released +
    + + 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. +

    + + 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!). +

    + You can read the + changelog for + complete details. BusyBox 0.51 can be downloaded from + ftp://oss.lineo.com/busybox. +

    Have Fun! +

    + +

  • Busybox Boot-Floppy Image + +

    Because you asked for it, we have made available a Busybox boot floppy + image. Here's how you use it: + +

      + +
    1. + Download the image + +
    2. dd it onto a floppy like so: dd if=busybox.floppy.img + of=/dev/fd0 ; sync + +
    3. Pop it in a machine and boot up. + +
    + +

    If you want to look at the contents of the initrd image, do this: + +

    +	    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
    +    
    + + +
  • 15 March 2001 -- BusyBox 0.50 released +
    + + 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 + changelog. +

    + 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 + Larry Doolittle's website. +

    + + +

  • 27 January 2001 -- BusyBox 0.49 released +
    + + 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 changelog. +

    + 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. +

    + Special Note
    + + 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 Larry's website + and help out if you can. This shell will be included in the next + release of BusyBox. +

    + +

  • 13 December 2000 -- BusyBox 0.48 released +
    + + 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. +

    + The curious can get a list of some of the more interesting changes by reading + the changelog. +

    + Many thanks go out to the many many people that have contributed to + this release, especially Matt Kraai, Larry Doolittle, and Kent Robotti. +

    +

  • 26 September 2000 -- BusyBox 0.47 released +
    + + 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 changelog + for complete details. + + +

  • 11 July 2000 -- BusyBox 0.46 released +
    + + 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 changelog + for complete details. + + +

  • 21 June 2000 -- BusyBox 0.45 released +
    + + 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 changelog for + details). +

    + Also, some exciting infrastructure news! Busybox now has its own + mailing list, + publically browsable + CVS tree, + anonymous + CVS access, and + for those that are actively contributing there is even + CVS write access. + I think this will be a huge help to the ongoing development of BusyBox. +

    + 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. +

    + Many thanks go out to the many people that have contributed to this release + of BusyBox (esp. Pavel Roskin)! + + +

  • 19 April 2000 -- syslogd bugfix +
    + Turns out that there was still a bug in busybox syslogd. + For example, with the following test app: +
    +	#include <syslog.h>
    +
    +	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);
    +	}
    +
    + 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). +

    + Karl M. Hegbloom has created a + fix for the problem. + Thanks Karl! + + +

  • 18 April 2000 -- BusyBox 0.43 released (finally!) +
    + 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 + changelog. + Oh, and as a special bonus, I wrote some fairly comprehensive + documentation, complete with examples and full usage information. + +

    + 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. +

    + + You can grab BusyBox 0.43 tarballs here. + +

  • 9 April 2000 -- BusyBox 0.43 pre release +
    + 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. +

    + The pre-release can be found here. + Please let me know ASAP if you find any bugs. + +

  • 28 March 2000 -- Andersen Baby Boy release +
    + 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). +

    + 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. +

    + 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. + + +

  • 11 February 2000 -- BusyBox 0.42 released +
    + + This is the most solid BusyBox release so far. Many, many + bugs have been fixed. See the +changelog 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. + +

  • 19 January 2000 -- BusyBox 0.41 released +
    + + 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 here. + +

  • 7 January 2000 -- BusyBox 0.40 released +
    + + 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 here. + +

  • 11 December 1999 -- BusyBox Website +
    + 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. + +

  • 10 December 1999 -- BusyBox 0.39 released +
    + 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 here. +

  • 5 December 1999 -- BusyBox 0.38 released +
    + This release includes fixes to tar, cat, ls, dd, rm, umount, find, df, + and make install, and includes new apps syslogd/klogd and logger. +
+ + + + +
+ + + Important Links + + +
+ + + + + + +
+

+ + + + +
+ + + + + + + + + + + + + + + +
+ + Mail all comments, insults, suggestions and bribes to + Erik Andersen
+ The Busybox logo is copyright 1999,2000, Erik Andersen. +
+
+ This site created with the vi editor + + Graphics by GIMP + + Linux Today + +

Slashdot +

+ Freshmeat +
+ + + + + diff --git a/busybox/docs/busybox.net/screenshot.html b/busybox/docs/busybox.net/screenshot.html new file mode 100644 index 000000000..8424fcef6 --- /dev/null +++ b/busybox/docs/busybox.net/screenshot.html @@ -0,0 +1,54 @@ + + + + + Busybox Screenshot! + + + + + + + + +

Busybox Screenshot!

+ + + +
+ + +
+
+
+$ ./busybox
+BusyBox v0.49 (2001.01.30-17:35+0000) multi-call binary -- GPL2
+
+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:
+        basename, busybox, cat, chgrp, chmod, chown, chroot, chvt, clear,
+        cp, cut, date, dd, df, dirname, dmesg, du, echo, false, find,
+        free, grep, gunzip, gzip, halt, head, id, init, kill, killall,
+        ln, logger, ls, lsmod, mkdir, mknod, mkswap, more, mount, mv,
+        poweroff, ps, pwd, reboot, reset, rm, rmdir, sed, sh, sleep, sort,
+        swapoff, swapon, sync, syslogd, tail, tar, touch, true, tty, umount,
+        uname, uniq, uptime, wc, which, whoami, xargs, yes, zcat
+
+$ _
+
+
+ +
+ + + + + diff --git a/busybox/docs/busybox.sgml b/busybox/docs/busybox.sgml new file mode 100644 index 000000000..2d372506b --- /dev/null +++ b/busybox/docs/busybox.sgml @@ -0,0 +1,3974 @@ + + + + BusyBox - The Swiss Army Knife of Embedded Linux + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + 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 more details see the file COPYING in the source + distribution of Linux. + + + + + + + Introduction + + + 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 embedded system. 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 a kernel, a + shell (such as ash), and an editor (such as elvis-tiny or ae). + + + + + How to use BusyBox + + Syntax + + + + BusyBox <function> [arguments...] # or + + + + + + <function> [arguments...] # if symlinked + + + + + + Invoking BusyBox + + + When you create a link to BusyBox for the function you wish to use, when + BusyBox is called using that link it will behave as if the command itself + has been invoked. + + + + For example, entering + + + + + ln -s ./BusyBox ls + ./ls + + + + + will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled + into BusyBox). + + + + You can also invoke BusyBox by issuing the command as an argument on the + command line. For example, entering + + + + + ./BusyBox ls + + + + + will also cause BusyBox to behave as 'ls'. + + + + + + Common options + + + Most BusyBox commands support the --help option to provide + a terse runtime description of their behavior. + + + + + + BusyBox Commands + + Available BusyBox Commands + + Currently defined functions include: + + + + ar, basename, cat, chgrp, chmod, chown, chroot, chvt, clear, + cp, cut, date, dc, dd, deallocvt, df, dirname, dmesg, dpkg-deb, + du, dumpkmap, dutmp, echo, false, fbset, fdflush, find, free, + freeramdisk, fsck.minix, grep, gunzip, gzip, halt, head, + hostid, hostname, id, init, insmod, kill, killall, length, ln, + loadacm, loadfont, loadkmap, logger, logname, ls, lsmod, + makedevs, mkdir, mkfifo, mkfs.minix, mknod, mkswap, mktemp, + more, mount, mt, mv, nc, nslookup, ping, poweroff, printf, ps, + pwd, reboot, renice, reset, rm, rmdir, rmmod, sed, setkeycodes, sh, sleep, + sort, swapoff, swapon, sync, syslogd, tail, tar, tee, telnet, + test, touch, tr, true, tty, umount, uname, uniq, update, + uptime, usleep, uudecode, uuencode, wc, which, whoami, yes, + zcat, [ + + + + + ar + + + Usage: ar [OPTION] archive [FILENAME]... + + + + Extract or list files from an ar archive. + + + + Options: + + + + + o Preserve original dates + p Extract to stdout + t List + x Extract + v Verbosely list files processed + + + + + + basename + + Usage: basename FILE [SUFFIX] + + + + Strip directory path and suffixes from FILE. If specified, also removes + any trailing SUFFIX. + + + + Example: + + + + + $ basename /usr/local/bin/foo + foo + $ basename /usr/local/bin/ + bin + $ basename /foo/bar.txt .txt + bar + + + + + + cat + + + Usage: cat [FILE]... + + + + Concatenate FILE(s) and prints them to the standard + output. + + + + Example: + + + + + $ cat /proc/uptime + 110716.72 17.67 + + + + + + chgrp + + + Usage: chgrp [OPTION]... GROUP FILE... + + + + Change the group membership of each FILE to GROUP. + + + + Options: + + + + + -R Change files and directories recursively + + + + + Example: + + + + + $ ls -l /tmp/foo + -r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo + $ chgrp root /tmp/foo + $ ls -l /tmp/foo + -r--r--r-- 1 andersen root 0 Apr 12 18:25 /tmp/foo + + + + + + chmod + + + Usage: chmod [-R] MODE[,MODE]... FILE... + + + + Change file access permissions for the specified + FILE(s) (or directories). Each MODE is defined by + combining the letters for WHO has access to the file, an OPERATOR for + selecting how the permissions should be changed, and a PERMISSION for + FILE(s) (or directories). + + + + WHO may be chosen from + + + + + u User who owns the file + g Users in the file's Group + o Other users not in the file's group + a All users + + + + + OPERATOR may be chosen from + + + + + + Add a permission + - Remove a permission + = Assign a permission + + + + + PERMISSION may be chosen from + + + + + r Read + w Write + x Execute (or access for directories) + s Set user (or group) ID bit + t Sticky bit (for directories prevents removing files by non-owners) + + + + + Alternately, permissions can be set numerically where the first three + numbers are calculated by adding the octal values, such as + + + + + 4 Read + 2 Write + 1 Execute + + + + + An optional fourth digit can also be used to specify + + + + + 4 Set user ID + 2 Set group ID + 1 Sticky bit + + + + + Options: + + + + + -R Change files and directories recursively. + + + + + Example: + + + + + $ ls -l /tmp/foo + -rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo + $ chmod u+x /tmp/foo + $ ls -l /tmp/foo + -rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo* + $ chmod 444 /tmp/foo + $ ls -l /tmp/foo + -r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo + + + + + + chown + + Usage: chown [OPTION]... OWNER[<.|:>[GROUP] FILE... + + + + Change the owner and/or group of each FILE to OWNER and/or GROUP. + + + + Options: + + + + + -R Change files and directories recursively + + + + + Example: + + + + + $ ls -l /tmp/foo + -r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo + $ chown root /tmp/foo + $ ls -l /tmp/foo + -r--r--r-- 1 root andersen 0 Apr 12 18:25 /tmp/foo + $ chown root.root /tmp/foo + ls -l /tmp/foo + -r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo + + + + + + chroot + + Usage: chroot NEWROOT [COMMAND...] + + + + Run COMMAND with root directory set to NEWROOT. + + + + Example: + + + + + $ ls -l /bin/ls + lrwxrwxrwx 1 root root 12 Apr 13 00:46 /bin/ls -> /BusyBox + $ mount /dev/hdc1 /mnt -t minix + $ chroot /mnt + $ ls -l /bin/ls + -rwxr-xr-x 1 root root 40816 Feb 5 07:45 /bin/ls* + + + + + + chvt + + Usage: chvt N + + + + Change the foreground virtual terminal to /dev/ttyN + + + + + clear + + + Usage: clear + + + + Clear the screen. + + + + + cp + + + Usage: cp [OPTION]... SOURCE DEST + + + + + or: cp [OPTION]... SOURCE... DIRECTORY + + + + + Copy SOURCE to DEST, or multiple SOURCE(s) to + DIRECTORY. + + + + Options: + + + + + -a Same as -dpR + -d Preserve links + -p Preserve file attributes if possible + -R Copy directories recursively + + + + + + cut + + + Usage: cut [OPTION]... [FILE]... + + + + Print selected fields from each input FILE to standard output. + + + + Options: + + + + + -b LIST Output only bytes from LIST + -c LIST Output only characters from LIST + -d CHAR Use CHAR instead of tab as the field delimiter + -s Output only the lines containing delimiter + -f N Print only these fields + -n Ignored + + + + + Example: + + + + + $ echo "Hello world" | cut -f 1 -d ' ' + Hello + $ echo "Hello world" | cut -f 2 -d ' ' + world + + + + + + date + + + Usage: date [OPTION]... [+FORMAT] + + + + + or: date [OPTION] [MMDDhhmm[[CC]YY][.ss]] + + + + + Display the current time in the given FORMAT, or set the system date. + + + + Options: + + + + + -R Output RFC-822 compliant date string + -s Set time described by STRING + -u Print or set Coordinated Universal Time + + + + + Example: + + + + + $ date + Wed Apr 12 18:52:41 MDT 2000 + + + + + + dc + + + Usage: dc [EXPRESSION] + + + + This is a Tiny RPN calculator that understands the + following operations: +, -, /, *, and, or, not, eor. If + no arguments are given, dc will process input from + stdin. + + + + The behaviour of BusyBox/dc deviates (just a little ;-) + from GNU/dc, but this will be remedied in the future. + + + + Example: + + + + + $ dc 2 2 + + 4 + $ dc 8 8 \* 2 2 + / + 16 + $ dc 0 1 and + 0 + $ dc 0 1 or + 1 + $ echo 72 9 div 8 mul | dc + 64 + + + + + + dd + + + Usage: dd [OPTION]... + + + + Copy a file, converting and formatting according to + options. + + + + Options: + + + + + if=FILE Read from FILE instead of stdin + of=FILE Write to FILE instead of stdout + bs=N Read and write N bytes at a time + count=N Copy only N input blocks + skip=N Skip N input blocks + seek=N Skip N output blocks + + + + + Numbers may be suffixed by w (x2), k (x1024), b (x512), + or M (x1024^2). + + + + Example: + + + + + $ dd if=/dev/zero of=/dev/ram1 bs=1M count=4 + 4+0 records in + 4+0 records out + + + + + + deallocvt + + + Usage: deallocvt N + + + + Deallocate unused virtual terminal /dev/ttyN. + + + + + df + + + Usage: df [FILE]... + + + + Print the filesystem space used and space available. + + + + Example: + + + + + $ df + Filesystem 1k-blocks Used Available Use% Mounted on + /dev/sda3 8690864 8553540 137324 98% / + /dev/sda1 64216 36364 27852 57% /boot + $ df /dev/sda3 + Filesystem 1k-blocks Used Available Use% Mounted on + /dev/sda3 8690864 8553540 137324 98% / + + + + + + dirname + + + Usage: dirname NAME + + + + Strip non-directory suffix from NAME. + + + + Example: + + + + + $ dirname /tmp/foo + /tmp + $ dirname /tmp/foo/ + /tmp + + + + + + dmesg + + + Usage: dmesg [OPTION]... + + + + Print or control the kernel ring buffer. + + + + Options: + + + + + -c Clear the ring buffer after printing + -n LEVEL Set the console logging level to LEVEL + -s BUFSIZE Query ring buffer using a buffer of BUFSIZE + + + + + + dos2unix + + + Usage: dos2unix < dosfile > unixfile + + + + Converts a text file from dos format to unix format. + + + + + + dpkg-deb + + + Usage: dpkg-deb [OPTION] archive [directory] + + + + Debian package archive (.deb) manipulation tool + + + + Options: + + + + + -c List the contents of the filesystem tree archive portion of the package + -e Extracts the control information files from a package archive into the specified directory. + If no directory is specified then a subdirectory DEBIAN in the current directory is used. + -x Silently extracts the filesystem tree from a package archive into the specified directory. + -X Extracts the filesystem tree from a package archive into the specified directory, listing the files as it goes. + If required the specified directory (but not its parents) will be created. + + + + + Example: + + + + + dpkg-deb -e ./busybox_0.48-1_i386.deb + dpkg-deb -x ./busybox_0.48-1_i386.deb ./unpack_dir + + + + + + du + + + Usage: du [OPTION]... [FILE]... + + + + Summarize the disk space used for each FILE or current + directory. Disk space printed in units of 1k (i.e., + 1024 bytes). + + + + Options: + + + + + -l Count sizes many times if hard linked + -s Display only a total for each argument + + + + + Example: + + + + + $ du + 16 ./CVS + 12 ./kernel-patches/CVS + 80 ./kernel-patches + 12 ./tests/CVS + 36 ./tests + 12 ./scripts/CVS + 16 ./scripts + 12 ./docs/CVS + 104 ./docs + 2417 . + + + + + + dumpkmap + + + Usage: dumpkmap + + + + Prints out a binary keyboard translation table to standard output. + + + + Example: + + + + + $ dumpkmap < keymap + + + + + + dutmp + + + Usage: dutmp [FILE] + + + + Dump utmp file format (pipe delimited) from FILE or + stdin to stdout. + + + + Example: + + + + + $ dutmp /var/run/utmp + 8|7||si|||0|0|0|955637625|760097|0 + 2|0|~|~~|reboot||0|0|0|955637625|782235|0 + 1|20020|~|~~|runlevel||0|0|0|955637625|800089|0 + 8|125||l4|||0|0|0|955637629|998367|0 + 6|245|tty1|1|LOGIN||0|0|0|955637630|998974|0 + 6|246|tty2|2|LOGIN||0|0|0|955637630|999498|0 + 7|336|pts/0|vt00andersen|andersen|:0.0|0|0|0|955637763|0|0 + + + + + + echo + + + Usage: echo [OPTION]... [ARG]... + + + + Print ARGs to stdout. + + + + Options: + + + + + -n Suppress trailing newline + -e Enable interpretation of escaped characters + -E Disable interpretation of escaped characters + + + + + Example: + + + + + $ echo "Erik is cool" + Erik is cool + $ echo -e "Erik\nis\ncool" + Erik + is + cool + $ echo "Erik\nis\ncool" + Erik\nis\ncool + + + + + + expr + + + Usage: expr EXPRESSION + + + + Prints the value of EXPRESSION to standard output. + + + + EXPRESSION may be: + + + + + ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 + ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 + ARG1 < ARG2 ARG1 is less than ARG2 + ARG1 <= ARG2 ARG1 is less than or equal to ARG2 + ARG1 = ARG2 ARG1 is equal to ARG2 + ARG1 != ARG2 ARG1 is unequal to ARG2 + ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 + ARG1 > ARG2 ARG1 is greater than ARG2 + ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 + ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 + ARG1 * ARG2 arithmetic product of ARG1 and ARG2 + ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 + ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 + STRING : REGEXP anchored pattern match of REGEXP in STRING + match STRING REGEXP same as STRING : REGEXP + substr STRING POS LENGTH substring of STRING, POS counted from 1 + index STRING CHARS index in STRING where any CHARS is found, or 0 + length STRING length of STRING + quote TOKEN interpret TOKEN as a string, even if it is a + keyword like `match' or an operator like `/' + ( EXPRESSION ) value of EXPRESSION + + + + + Beware that many operators need to be escaped or quoted for shells. + Comparisons are arithmetic if both ARGs are numbers, else + lexicographical. Pattern matches return the string matched between + \( and \) or null; if \( and \) are not used, they return the number + of characters matched or 0. + + + + + + + false + + + Usage: false + + + + Return an exit code of FALSE (1). + + + + Example: + + + + + $ false + $ echo $? + 1 + + + + + + fbset + + + Usage: fbset [OPTION]... [MODE] + + + + Show and modify frame buffer device settings. + + + + Options: + + + + + -h Display option summary + -fb DEVICE Operate on DEVICE + -db FILE Use FILE for mode database + -g XRES YRES VXRES VYRES DEPTH Set all geometry parameters + -t PIXCLOCK LEFT RIGHT UPPER LOWER HSLEN VSLEN Set all timing parameters + -xres RES Set visible horizontal resolution + -yres RES Set visible vertical resolution + + + + + Example: + + + + + $ fbset + mode "1024x768-76" + # D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz + geometry 1024 768 1024 768 16 + timings 12714 128 32 16 4 128 4 + accel false + rgba 5/11,6/5,5/0,0/0 + endmode + + + + + + fdflush + + + Usage: fdflush DEVICE + + + + Force floppy disk drive to detect disk change on DEVICE. + + + + + find + + + Usage: find [PATH]... [EXPRESSION] + + + + Search for files in a directory hierarchy. The default + PATH is the current directory; default EXPRESSION is + '-print'. + + + + EXPRESSION may consist of: + + + + + -follow Dereference symbolic links + -name PATTERN File name (leading directories removed) matches PATTERN + -type X Filetype matches X (where X is one of: f,d,l,b,c,...) + -perm PERMS Permissions match any of (+NNN); all of (-NNN); or exactly (NNN) + -mtime TIME Modified time is greater than (+N); less than (-N); or exactly (N) days + + + + + Example: + + + + + $ find / -name /etc/passwd + /etc/passwd + + + + + + free + + + Usage: free + + + + Displays the amount of free and used system memory. + + + + Example: + + + + + $ free + total used free shared buffers + Mem: 257628 248724 8904 59644 93124 + Swap: 128516 8404 120112 + Total: 386144 257128 129016 + + + + + + freeramdisk + + + Usage: freeramdisk DEVICE + + + + Free all memory used by the ramdisk DEVICE. + + + + Example: + + + + + $ freeramdisk /dev/ram2 + + + + + + fsck.minix + + + Usage: fsck.minix [OPTION]... DEVICE + + + + Perform a consistency check on the MINIX filesystem on + DEVICE. + + + + Options: + + + + + -l List all filenames + -r Perform interactive repairs + -a Perform automatic repairs + -v Verbose + -s Output super-block information + -m Activate MINIX-like "mode not cleared" warnings + -f Force file system check. + + + + + + getopt + + + Usage: getopt [OPTIONS]... + + + + Parse command options + + + + + -a, --alternative Allow long options starting with single -\n" + -l, --longoptions=longopts Long options to be recognized\n" + -n, --name=progname The name under which errors are reported\n" + -o, --options=optstring Short options to be recognized\n" + -q, --quiet Disable error reporting by getopt(3)\n" + -Q, --quiet-output No normal output\n" + -s, --shell=shell Set shell quoting conventions\n" + -T, --test Test for getopt(1) version\n" + -u, --unqote Do not quote the output\n" + + + + + + Example: + + + + + $ cat getopt.test + #!/bin/sh + GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \ + -n 'example.busybox' -- "$@"` + if [ $? != 0 ] ; then exit 1 ; fi + eval set -- "$GETOPT" + while true ; do + case $1 in + -a|--a-long) echo "Option a" ; shift ;; + -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;; + -c|--c-long) + case "$2" in + "") echo "Option c, no argument"; shift 2 ;; + *) echo "Option c, argument \`$2'" ; shift 2 ;; + esac ;; + --) shift ; break ;; + *) echo "Internal error!" ; exit 1 ;; + esac + done + + + + + + grep + + + Usage: grep [OPTIONS]... PATTERN [FILE]... + + + + Search for PATTERN in each FILE or stdin. + + + + Options: + + + + + -h Suppress the prefixing filename on output + -i Ignore case distinctions + -n Print line number with output lines + -q Be quiet. Returns 0 if result was found, 1 otherwise + -v Select non-matching lines + + + + + This version of grep matches full regular expressions. + + + + Example: + + + + + $ grep root /etc/passwd + root:x:0:0:root:/root:/bin/bash + $ grep ^[rR]oo. /etc/passwd + root:x:0:0:root:/root:/bin/bash + + + + + + gunzip + + + Usage: gunzip [OPTION]... FILE + + + + Uncompress FILE (or stdin if FILE is '-'). + + + + Options: + + + + + -c Write output to standard output + -t Test compressed file integrity + + + + + Example: + + + + + $ ls -la /tmp/BusyBox* + -rw-rw-r-- 1 andersen andersen 557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz + $ gunzip /tmp/BusyBox-0.43.tar.gz + $ ls -la /tmp/BusyBox* + -rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar + + + + + + gzip + + + Usage: gzip [OPTION]... FILE + + + + Compress FILE (or stdin if FILE is '-') with maximum + compression to FILE.gz (or stdout if FILE is '-'). + + + + Options: + + + + + -c Write output to standard output + -d decompress + + + + + Example: + + + + + $ ls -la /tmp/BusyBox* + -rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar + $ gzip /tmp/BusyBox-0.43.tar + $ ls -la /tmp/BusyBox* + -rw-rw-r-- 1 andersen andersen 554058 Apr 14 17:49 /tmp/BusyBox-0.43.tar.gz + + + + + + halt + + + Usage: halt + + + + Halt the system. + + + + + head + + + Usage: head [OPTION] FILE... + + + + Print first 10 lines of each FILE to standard output. + With more than one FILE, precede each with a header + giving the file name. With no FILE, or when FILE is -, + read standard input. + + + + Options: + + + + + -n NUM Print first NUM lines instead of first 10 + + + + + Example: + + + + + $ head -n 2 /etc/passwd + root:x:0:0:root:/root:/bin/bash + daemon:x:1:1:daemon:/usr/sbin:/bin/sh + + + + + + hostid + + + Usage: hostid + + + + Prints out a unique 32-bit identifier for the current + machine. The 32-bit identifier is intended to be unique + among all UNIX systems in existence. + + + + + hostname + + + Usage: hostname [OPTION]... [HOSTNAME|-F FILE] + + + + Get or set the hostname or DNS domain name. If a + hostname is given (or a file with the -F parameter), the + host name will be set. + + + + Options: + + + + + -s Short + -i Addresses for the hostname + -d DNS domain name + -F, --file FILE Use the contents of FILE to specify the hostname + + + + + Example: + + + + + $ hostname + slag + + + + + + id + + + Usage: id [OPTION]... [USERNAME] + + + + Print information for USERNAME or the current user. + + + + Options: + + + + + -g Print only the group ID + -u Print only the user ID + -n print a name instead of a number (with for -ug) + -r Print the real user ID instead of the effective ID (with -ug) + + + + + Example: + + + + + $ id + uid=1000(andersen) gid=1000(andersen) + + + + + + init + + + Usage: init + + + + Init is the parent of all processes. + + + + This version of init is designed to be run only by the + kernel. + + + + BusyBox init doesn't support multiple runlevels. The + runlevels field of the /etc/inittab file is completely + ignored by BusyBox init. If you want runlevels, use + sysvinit. + + + + 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 + + + + + If it detects that /dev/console is _not_ a serial + console, it will also run: + + + + + tty2::askfirst:/bin/sh + + + + + If you choose to use an /etc/inittab file, the inittab + entry format is as follows: + + + + + <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, the controlling tty is set + to the console. Also note that if BusyBox detects that a serial + console is in use, then only entries whose controlling tty is + either the serial console or /dev/null will 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, and ctrlaltdel. + + + + + The available actions can be classified into two groups: actions + that are run only once, and actions that are re-run when the specified + process exits. + + + + Run only-once actions: + + + + 'sysinit' is the first item run on boot. init waits until all + sysinit actions are completed before continuing. Following the + completion of all sysinit actions, all 'wait' actions are run. + 'wait' actions, like 'sysinit' actions, cause init to wait until + the specified task completes. 'once' actions are asyncronous, + therefore, init does not wait for them to complete. 'ctrlaltdel' + actions are run immediately before init causes the system to reboot + (unmounting filesystems with a 'ctrlaltdel' action is a very good + idea). + + + + Run repeatedly actions: + + + + 'respawn' actions are run after the 'once' actions. When a process + started with a 'respawn' action exits, init automatically restarts + it. Unlike sysvinit, BusyBox init does not stop processes from + respawning out of control. The 'askfirst' actions acts just like + respawn, except that 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. + + + + Unrecognized actions (like initdefault) will cause init to emit an + error message, and then go along with its business. All actions are + run in the reverse order from how they appear in /etc/inittab. + + + + + + process + + + Specifies the process to be executed and its + command line. + + + + + Example /etc/inittab file + + + + # This is run first except when booting in single-user mode. + # + ::sysinit:/etc/init.d/rcS + + # /bin/sh invocations on selected ttys + # + # 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 + tty2::askfirst:-/bin/sh + tty2::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 before rebooting + ::ctrlaltdel:/bin/umount -a -r + ::ctrlaltdel:/sbin/swapoff + + + + + + + insmod + + + Usage: insmod [OPTION]... MODULE [symbol=value]... + + + + Load MODULE into the kernel. + + + + Options: + + + + + -f Force module to load into the wrong kernel version. + -k Make module autoclean-able. + -v Verbose output + -x Do not export externs + -L Prevent simultaneous loads of the same module + + + + + + kill + + + Usage: kill [OPTION] PID... + + + + Send a signal (default is SIGTERM) to the specified + PID(s). + + + + Options: + + + + + -l List all signal names and numbers + -SIG Send signal SIG + + + + + Example: + + + + + $ ps | grep apache + 252 root root S [apache] + 263 www-data www-data S [apache] + 264 www-data www-data S [apache] + 265 www-data www-data S [apache] + 266 www-data www-data S [apache] + 267 www-data www-data S [apache] + $ kill 252 + + + + + + killall + + + Usage: killall [OPTION] NAME... + + + + Send a signal (default is SIGTERM) to the specified + NAME(s). + + + + Options: + + + + + -l List all signal names and numbers + -SIG Send signal SIG + + + + + Example: + + + + + $ killall apache + + + + + + length + + + Usage: length STRING + + + + Print the length of STRING. + + + + Example: + + + + + $ length "Hello" + 5 + + + + + + ln + + + Usage: ln [OPTION]... TARGET FILE|DIRECTORY + + + + Create a link named FILE or DIRECTORY to the specified + TARGET. You may use '--' to indicate that all following + arguments are non-options. + + + + Options: + + + + + -s Make symbolic link instead of hard link + -f Remove existing destination file + + + + + Example: + + + + + $ ln -s BusyBox /tmp/ls + $ ls -l /tmp/ls + lrwxrwxrwx 1 root root 7 Apr 12 18:39 ls -> BusyBox* + + + + + + loadacm + + + Usage: loadacm + + + + Load an acm from stdin. + + + + Example: + + + + + $ loadacm < /etc/i18n/acmname + + + + + + loadfont + + + Usage: loadfont + + + + Load a console font from stdin. + + + + Example: + + + + + $ loadfont < /etc/i18n/fontname + + + + + + loadkmap + + + Usage: loadkmap + + + + Load a binary keyboard translation table from stdin. + + + + Example: + + + + + $ loadkmap < /etc/i18n/lang-keymap + + + + + + logger + + + Usage: logger [OPTION]... [MESSAGE] + + + + Write MESSAGE to the system log. If MESSAGE is omitted, log + stdin. + + + + Options: + + + + + -s Log to stderr as well as the system log + -t Log using the specified tag (defaults to user name) + -p Enter the message with the specified priority + This may be numerical or a ``facility.level'' pair + + + + + Example: + + + + + $ logger "hello" + + + + + + logname + + + Usage: logname + + + + Print the name of the current user. + + + + Example: + + + + + $ logname + root + + + + + + ls + + + Usage: ls [OPTION]... [FILE]... + + + + + + + + Options: + + + + + -a Do not hide entries starting with . + -c With -l: show ctime (the time of last + modification of file status information) + -d List directory entries instead of contents + -e List both full date and full time + -l Use a long listing format + -n List numeric UIDs and GIDs instead of names + -p Append indicator (one of /=@|) to entries + -u With -l: show access time (the time of last + access of the file) + -x List entries by lines instead of by columns + -A Do not list implied . and .. + -C List entries by columns + -F Append indicator (one of */=@|) to entries + -L list entries pointed to by symbolic links + -R List subdirectories recursively + + + + + Example: + + + + + + + + + + lsmod + + + Usage: lsmod + + + + List currently loaded kernel modules. + + + + + makedevs + + + Usage: makedevsf NAME TYPE MAJOR MINOR FIRST LAST [s] + + + + Create a range of block or character special files. + + + + TYPE may be: + + + + + b Make a block (buffered) device + c or u Make a character (un-buffered) device + p Make a named pipe. MAJOR and MINOR are ignored for named pipes + + + + + FIRST specifies the number appended to NAME to create + the first device. LAST specifies the number of the last + item that should be created. If 's' is the last + argument, the base device is created as well. + + + + Example: + + + + + $ makedevs /dev/ttyS c 4 66 2 63 + [creates ttyS2-ttyS63] + $ makedevs /dev/hda b 3 0 0 8 s + [creates hda,hda1-hda8] + + + + + + md5sum + + + Usage: md5sum [OPTION]... FILE... + + + + Print or check MD5 checksums. + + + + Options: + + + + + -b Read files in binary mode + -c Check MD5 sums against given list + -t Read files in text mode (default) + -g Read a string + + + + + The following two options are useful only when verifying + checksums: + + + + + -s Don't output anything, status code shows success + -w Warn about improperly formated MD5 checksum lines + + + + + Example: + + + + + $ md5sum busybox + 6fd11e98b98a58f64ff3398d7b324003 busybox + $ md5sum -c + 6fd11e98b98a58f64ff3398d7b324003 busybox + 6fd11e98b98a58f64ff3398d7b324002 busybox + md5sum: MD5 check failed for 'busybox' + ^D + + + + + + mkdir + + + Usage: mkdir [OPTION]... DIRECTORY... + + + + Create the DIRECTORY(s), if they do not already exist. + + + + Options: + + + + + -m Set permission mode (as in chmod), not rwxrwxrwx - umask + -p No error if directory exists, make parent directories as needed + + + + + Example: + + + + + $ mkdir /tmp/foo + $ mkdir /tmp/foo + /tmp/foo: File exists + $ mkdir /tmp/foo/bar/baz + /tmp/foo/bar/baz: No such file or directory + $ mkdir -p /tmp/foo/bar/baz + + + + + + mkfifo + + + Usage: mkfifo [OPTION] NAME + + + + Create a named pipe (identical to 'mknod NAME p'). + + + + Options: + + + + + -m MODE Create the pipe using the specified mode (default a=rw) + + + + + + mkfs.minix + + + Usage: mkfs.minix [OPTION]... NAME [BLOCKS] + + + + Make a MINIX filesystem. + + + + Options: + + + + + -c Check the device for bad blocks + -n [14|30] Specify the maximum length of filenames + -i Specify the number of inodes for the filesystem + -l FILENAME Read the bad blocks list from FILENAME + -v Make a Minix version 2 filesystem + + + + + + mknod + + + Usage: mknod [OPTION]... NAME TYPE MAJOR MINOR + + + + Create a special file (block, character, or pipe). + + + + Options: + + + + + -m Create the special file using the specified mode (default a=rw) + + + + + TYPE may be: + + + + + b Make a block (buffered) device + c or u Make a character (un-buffered) device + p Make a named pipe. MAJOR and MINOR are ignored for named pipes + + + + + Example: + + + + + $ mknod /dev/fd0 b 2 0 + $ mknod -m 644 /tmp/pipe p + + + + + + mkswap + + + Usage: mkswap [OPTION]... DEVICE [BLOCKS] + + + + Prepare a disk partition to be used as a swap partition. + + + + Options: + + + + + -c Check for read-ability. + -v0 Make version 0 swap [max 128 Megs]. + -v1 Make version 1 swap [big!] (default for kernels > 2.1.117). + BLOCKS Number of block to use (default is entire partition). + + + + + + mktemp + + + Usage: mktemp TEMPLATE + + + + Creates a temporary file with its name based on + TEMPLATE. TEMPLATE is any name with six `Xs' (i.e., + /tmp/temp.XXXXXX). + + + + Example: + + + + + $ mktemp /tmp/temp.XXXXXX + /tmp/temp.mWiLjM + $ ls -la /tmp/temp.mWiLjM + -rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM + + + + + + more + + + Usage: more [FILE]... + + + + Page through text one screenful at a time. + + + + Example: + + + + + $ dmesg | more + + + + + + mount + + + Usage: mount [OPTION]... + + + + + or: mount [OPTION]... DEVICE DIRECTORY + + + + + Mount filesystems. + + + + Options: + + + + + -a Mount all filesystems in /etc/fstab + -o One of the many filesystem options listed below + -r Mount the filesystem read-only + -t TYPE Specify the filesystem type + -w Mount the filesystem read-write + + + + + Options for use with the -o flag: + + + + + async/sync Writes are asynchronous / synchronous + atime/noatime Enable / disable updates to inode access times + dev/nodev Allow / disallow use of special device files + exec/noexec Allow / disallow use of executable files + loop Mount a file via loop device + suid/nosuid Allow / disallow set-user-id-root programs + remount Remount a currently mounted filesystem + ro/rw Mount filesystem read-only / read-write + + + + + There are even more flags that are filesystem specific. + You'll have to see the written documentation for those. + + + + Example: + + + + + $ mount + /dev/hda3 on / type minix (rw) + proc on /proc type proc (rw) + devpts on /dev/pts type devpts (rw) + $ mount /dev/fd0 /mnt -t msdos -o ro + $ mount /tmp/diskimage /opt -t ext2 -o loop + + + + + + mt + + + Usage: mt [OPTION] OPCODE VALUE + + + + Control magnetic tape drive operation. + + + + Options: + + + + + -f DEVICE Control DEVICE + + + + + + mv + + + Usage: mv SOURCE DEST + + + + + or: mv SOURCE... DIRECTORY + + + + + Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY. + + + + Example: + + + + + $ mv /tmp/foo /bin/bar + + + + + + nc + + + Usage: nc HOST PORT + + + + or: nc -p PORT -l + + + + + Open a pipe to HOST:PORT or listen for a connection on PORT. + + + + Example: + + + + + $ nc foobar.somedomain.com 25 + 220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600 + help + 214-Commands supported: + 214- HELO EHLO MAIL RCPT DATA AUTH + 214 NOOP QUIT RSET HELP + quit + 221 foobar closing connection + + + + + + nslookup + + + Usage: nslookup [HOST] + + + + Query the nameserver for the IP address of the given + HOST. + + + + Example: + + + + + $ nslookup localhost + Server: default + Address: default + + Name: debian + Address: 127.0.0.1 + + + + + + ping + + + Usage: ping [OPTION]... HOST + + + + Send ICMP ECHO_REQUEST packets to HOST. + + + + Options: + + + + + -c COUNT Send only COUNT pings + -s SIZE Send SIZE data bytes in packets (default=56) + -q Quiet mode, only displays output at start and when finished + + + + + Example: + + + + + $ ping localhost + PING slag (127.0.0.1): 56 data bytes + 64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=20.1 ms + + --- debian ping statistics --- + 1 packets transmitted, 1 packets received, 0% packet loss + round-trip min/avg/max = 20.1/20.1/20.1 ms + + + + + + poweroff + + + Usage: poweroff + + + + Shut down the system, and request that the kernel turn + off power upon halting. + + + + + printf + + + Usage: printf FORMAT [ARGUMENT]... + + + + Format and print the given data in a manner similar to + the C printf command. + + + + Example: + + + + + $ printf "Val=%d\n" 5 + Val=5 + + + + + + ps + + + Usage: ps + + + + Report process status. This version of ps accepts no + options. + + + + Options: + + + + + + + + + Example: + + + + + $ ps + PID Uid Gid State Command + 1 root root S init + 2 root root S [kflushd] + 3 root root S [kupdate] + 4 root root S [kpiod] + 5 root root S [kswapd] + 742 andersen andersen S [bash] + 743 andersen andersen S -bash + 745 root root S [getty] + 2990 andersen andersen R ps + + + + + + pwd + + + Usage: pwd + + + + Print the full filename of the current working + directory. + + + + Example: + + + + + $ pwd + /root + + + + + + rdate + + + Usage: rdate [OPTION] HOST + + + + Get and possibly set the system date and time from a remote HOST. + + + + Options: + + + + + -s Set the system date and time (default). + -p Print the date and time. + + + + + + reboot + + + Usage: reboot + + + + Reboot the system. + + + + + renice + + + Usage: renice priority pid [pid ...] + + + + Changes priority of running processes. Allowed priorities range + from 20 (the process runs only when nothing else is running) to 0 + (default priority) to -20 (almost nothing else ever gets to run). + + + + + reset + + + Usage: reset + + + + Resets the screen. + + + + + rm + + + Usage: rm [OPTION]... FILE... + + + + Remove (unlink) the FILE(s). You may use '--' to + indicate that all following arguments are non-options. + + + + Options: + + + + + -i Always prompt before removing each destinations + -f Remove existing destinations, never prompt + -r or -R Remove the contents of directories recursively + + + + + Example: + + + + + $ rm -rf /tmp/foo + + + + + + rmdir + + + Usage: rmdir DIRECTORY... + + + + Remove DIRECTORY(s) if they are empty. + + + + Example: + + + + + $ rmdir /tmp/foo + + + + + + rmmod + + + Usage: rmmod [OPTION]... [MODULE]... + + + + Unload MODULE(s) from the kernel. + + + + Options: + + + + + -a Try to remove all unused kernel modules + + + + + Example: + + + + + $ rmmod tulip + + + + + + sed + + + Usage: sed [OPTION]... SCRIPT [FILE]... + + + + Allowed sed scripts come in the following form: + + + + + ADDR [!] COMMAND + + + + + ADDR can be: + + + + + NUMBER Match specified line number + $ Match last line + /REGEXP/ Match specified regexp + + + + + ! inverts the meaning of the match + + + + COMMAND can be: + + + + + s/regexp/replacement/[igp] + which attempt to match regexp against the pattern space + and if successful replaces the matched portion with replacement. + aTEXT + which appends TEXT after the pattern space + + + + + This version of sed matches full regular expressions. + + + + Options: + + + + + -e Add the script to the commands to be executed + -n Suppress automatic printing of pattern space + + + + + Example: + + + + + $ echo "foo" | sed -e 's/f[a-zA-Z]o/bar/g' + bar + + + + + + setkeycodes + + + Usage: setkeycodes SCANCODE KEYCODE ... + + + + Set entries into the kernel's scancode-to-keycode map, + allowing unusual keyboards to generate usable keycodes. + + + + SCANCODE may be either xx or e0xx (hexadecimal), and + KEYCODE is given in decimal. + + + + Example: + + + + + $ setkeycodes e030 127 + + + + + + + sh + + + Usage: sh + + + + lash -- the BusyBox LAme SHell (command interpreter) + + + + This command does not yet have proper documentation. + + + + Use lash just as you would use any other shell. It + properly handles pipes, redirects, job control, can be + used as the shell for scripts (#!/bin/sh), and has a + sufficient set of builtins to do what is needed. It does + not (yet) support Bourne Shell syntax. If you need + things like ``if-then-else'', ``while'', and such, use + ash or bash. If you just need a very simple and + extremely small shell, this will do the job. + + + + + sleep + + + Usage: sleep N + + + + Pause for N seconds. + + + + Example: + + + + + $ sleep 2 + [2 second delay results] + + + + + + sort + + + Usage: sort [OPTION]... [FILE]... + + + + Sort lines of text in FILE(s). + + + + Options: + + + + + -n Compare numerically + -r Reverse after sorting + + + + + Example: + + + + + $ echo -e "e\nf\nb\nd\nc\na" | sort + a + b + c + d + e + f + + + + + + swapoff + + + Usage: swapoff [OPTION] [DEVICE] + + + + Stop swapping virtual memory pages on DEVICE. + + + + Options: + + + + + -a Stop swapping on all swap devices + + + + + + swapon + + + Usage: swapon [OPTION] [DEVICE] + + + + Start swapping virtual memory pages on the given device. + + + + Options: + + + + + -a Start swapping on all swap devices + + + + + + sync + + + Usage: sync + + + + Write all buffered filesystem blocks to disk. + + + + + syslogd + + + Usage: syslogd [OPTION]... + + + + Linux system and kernel (provides klogd) logging + utility. Note that this version of syslogd/klogd ignores + /etc/syslog.conf. + + + + Options: + + + + + -m NUM Interval between MARK lines (default=20min, 0=off) + -n Run as a foreground process + -K Do not start up the klogd process + -O FILE Use an alternate log file (default=/var/log/messages) + -R HOST[:PORT] Log remotely to IP or hostname on PORT (default PORT=514/UDP) + -L Log locally as well as network logging (default is network only) + + + + + Example: + + + + + $ syslogd -R masterlog:514 + $ syslogd -R 192.168.1.1:601 + + + + + + tail + + + Usage: tail [OPTION] [FILE]... + + + + Print last 10 lines of each FILE to standard output. + With more than one FILE, precede each with a header + giving the file name. With no FILE, or when FILE is -, + read stdin. + + + + Options: + + + + + -n NUM Print last NUM lines instead of last 10 + -f Output data as the file grows. This version + of 'tail -f' supports only one file at a time. + + + + + Example: + + + + + $ tail -n 1 /etc/resolv.conf + nameserver 10.0.0.1 + + + + + + tar + + + Usage: tar [MODE] [OPTION] [FILE]... + + + + + + + + MODE may be chosen from + + + + + c Create + x Extract + t List + + + + + Options: + + + + + f FILE Use FILE for tarfile (or stdin if '-') + O Extract to stdout + exclude FILE File to exclude + v List files processed + + + + + Example: + + + + + $ zcat /tmp/tarball.tar.gz | tar -xf - + $ tar -cf /tmp/tarball.tar /usr/local + + + + + + tee + + + Usage: tee [OPTION]... [FILE]... + + + + Copy stdin to FILE(s), and also to stdout. + + + + Options: + + + + + -a Append to the given FILEs, do not overwrite + + + + + Example: + + + + + $ echo "Hello" | tee /tmp/foo + Hello + $ cat /tmp/foo + Hello + + + + + + telnet + + + Usage: telnet HOST [PORT] + + + + Establish interactive communication with another + computer over a network using the TELNET protocol. + + + + + test, [ + + + Usage: test EXPRESSION + + + + or: [ EXPRESSION ] + + + + Check file types and compare values returning an exit + code determined by the value of EXPRESSION. + + + + Example: + + + + + $ test 1 -eq 2 + $ echo $? + 1 + $ test 1 -eq 1 + $ echo $? + 0 + $ [ -d /etc ] + $ echo $? + 0 + $ [ -d /junk ] + $ echo $? + 1 + + + + + + touch + + + Usage: touch [OPTION]... FILE... + + + + Update the last-modified date on (or create) FILE(s). + + + + Options: + + + + + -c Do not create files + + + + + Example: + + + + + $ ls -l /tmp/foo + /bin/ls: /tmp/foo: No such file or directory + $ touch /tmp/foo + $ ls -l /tmp/foo + -rw-rw-r-- 1 andersen andersen 0 Apr 15 01:11 /tmp/foo + + + + + + tr + + + Usage: tr [OPTION]... STRING1 [STRING2] + + + + Translate, squeeze, and/or delete characters from stdin, + writing to stdout. + + + + Options: + + + + + -c Take complement of STRING1 + -d Delete input characters coded STRING1 + -s Squeeze multiple output characters of STRING2 into one character + + + + + Example: + + + + + $ echo "gdkkn vnqkc" | tr [a-y] [b-z] + hello world + + + + + + true + + + Usage: true + + + + Return an exit code of TRUE (1). + + + + Example: + + + + + $ true + $ echo $? + 0 + + + + + + tty + + + Usage: tty + + + + Print the file name of the terminal connected to stdin. + + + + Options: + + + + + -s Print nothing, only return an exit status + + + + + Example: + + + + + $ tty + /dev/tty2 + + + + + + umount + + + Usage: umount [OPTION]... DEVICE|DIRECTORY + + + + + + + + Options: + + + + + -a Unmount all file systems + -r Try to remount devices as read-only if mount is busy + -f Force filesystem umount (i.e., unreachable NFS server) + -l Do not free loop device (if a loop device has been used) + + + + + Example: + + + + + $ umount /dev/hdc1 + + + + + + uname + + + Usage: uname [OPTION]... + + + + Print certain system information. With no OPTION, same + as -s. + + + + Options: + + + + + -a Print all information + -m Print the machine (hardware) type + -n Print the machine's network node hostname + -r Print the operating system release + -s Print the operating system name + -p Print the host processor type + -v Print the operating system version + + + + + Example: + + + + + $ uname -a + Linux debian 2.2.15pre13 #5 Tue Mar 14 16:03:50 MST 2000 i686 unknown + + + + + + uniq + + + Usage: uniq [INPUT [OUTPUT]] + + + + Discard all but one of successive identical lines from + INPUT (or stdin), writing to OUTPUT (or stdout). + + + + Options: + + + + + -c prefix lines by the number of occurrences + -d only print duplicate lines + -u only print unique lines + + + + + Example: + + + + + $ echo -e "a\na\nb\nc\nc\na" | sort | uniq + a + b + c + + + + + + unix2dos + + + Usage: unix2dos < unixfile > dosfile + + + + Converts a text file from unix format to dos format. + + + + + + unrpm + + + Usage: unrpm < package.rpm | gzip -d | cpio -idmuv + + + + Extracts an rpm archive. + + + + + + update + + + Usage: update [OPTION]... + + + + Periodically flush filesystem buffers. + + + + Options: + + + + + -S Force use of sync(2) instead of flushing + -s SECS Call sync this often (default 30) + -f SECS Flush some buffers this often (default 5) + + + + + + uptime + + + Usage: uptime + + + + Display how long the system has been running since boot. + + + + Example: + + + + + $ uptime + 1:55pm up 2:30, load average: 0.09, 0.04, 0.00 + + + + + + usleep + + + Usage: usleep N + + + + Pause for N microseconds. + + + + Example: + + + + + $ usleep 1000000 + [pauses for 1 second] + + + + + + uudecode + + + Usage: uudecode [OPTION] [FILE] + + + + Uudecode a uuencoded file. + + + + Options: + + + + + -o FILE Direct output to FILE + + + + + Example: + + + + + $ uudecode -o busybox busybox.uu + $ ls -l busybox + -rwxr-xr-x 1 ams ams 245264 Jun 7 21:35 busybox + + + + + + uuencode + + + Usage: uuencode [OPTION] [INFILE] OUTFILE + + + + Uuencode a file. + + + + Options: + + + + + -m Use base64 encoding as of RFC1521 + + + + + Example: + + + + + $ uuencode busybox busybox + begin 755 busybox + M?T5,1@$!`0````````````(``P`!````L+@$"#0```!0N@,``````#0`(``& + ..... + $ uudecode busybox busybox > busybox.uu + $ + + + + + + watchdog + + + Usage: watchdog device + + + + Periodically writes to watchdog device B. + + + + + wc + + + Usage: wc [OPTION]... [FILE]... + + + + Print line, word, and byte counts for each FILE, and a + total line if more than one FILE is specified. With no + FILE, read stdin. + + + + Options: + + + + + -c Print the byte counts + -l Print the newline counts + -L Print the length of the longest line + -w Print the word counts + + + + + Example: + + + + + $ wc /etc/passwd + 31 46 1365 /etc/passwd + + + + + + which + + + Usage: which [COMMAND]... + + + + Locate COMMAND(s). + + + + Example: + + + + + $ which login + /bin/login + + + + + + whoami + + + Usage: whoami + + + + Print the user name associated with the current + effective user id. + + + + Example: + + + + + $ whoami + andersen + + + + + + xargs + + + Usage: xargs [OPTIONS] [COMMAND] [ARGS...] + + + + Executes COMMAND on every item given by standard input. + + + + Options: + + + + + -t Print the command just before it is run + + + + + + Example: + + + + + $ ls | xargs gzip + $ find . -name '*.c' -print | xargs rm + + + + + + yes + + + Usage: yes [STRING]... + + + + Repeatedly output a line with all specified STRING(s), + or `y'. + + + + + zcat + + + Usage: zcat [OPTION]... FILE + + + + Uncompress FILE (or stdin if FILE is '-') to stdout. + + + + Options: + + + + + -t Test compressed file integrity + + + + + Example: + + + + + + + + + + + LIBC NSS + + + GNU Libc 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. BusyBox has made it Policy that it will never use + NSS, and will never use libc calls that make use of NSS. This + allows you to run an embedded system without the need for + installing an /etc/nsswitch.conf file and without /lib/libnss_* + libraries installed. + + + + If you are using a system that is using a remote LDAP server for + authentication via GNU libc NSS, and you want to use BusyBox, + then you will need to adjust the BusyBox source. Chances are + though, that if you have enough space to install of that stuff + on your system, then you probably want the full GNU utilities. + + + + + SEE ALSO + + + textutils(1), + shellutils(1), + etc... + + + + + MAINTAINER + + + Erik Andersen <andersee@debian.org> <andersen@lineo.com> + + + + + AUTHORS + + + The following people have made significant contributions to + BusyBox -- whether they know it or not. + + + + Erik Andersen <andersee@debian.org> + + + + Edward Betts <edward@debian.org> + + + + John Beppu <beppu@lineo.com> + + + + Brian Candler <B.Candler@pobox.com> + + + + Randolph Chung <tausq@debian.org> + + + + Dave Cinege <dcinege@psychosis.com> + + + + Karl M. Hegbloom <karlheg@debian.org> + + + + Daniel Jacobowitz <dan@debian.org> + + + + Matt Kraai <kraai@alumni.carnegiemellon.edu> + + + + John Lombardo <john@deltanet.com> + + + + Glenn McGrath <bug1@netconnect.com.au> + + + + Bruce Perens <bruce@perens.com> + + + + Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com> + + + + Pavel Roskin <proski@gnu.org> + + + + Gyepi Sam <gyepi@praxis-sw.com> + + + + Linus Torvalds <torvalds@transmeta.com> + + + + Mark Whitley <markw@lineo.com> + + + + Charles P. Wright <cpwright@villagenet.com> + + + + Enrique Zanardi <ezanardi@ull.es> + + + + + diff --git a/busybox/docs/busybox_footer.pod b/busybox/docs/busybox_footer.pod new file mode 100644 index 000000000..2ab4e166e --- /dev/null +++ b/busybox/docs/busybox_footer.pod @@ -0,0 +1,169 @@ +=back + +=head1 LIBC NSS + +GNU Libc 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. BusyBox has made it Policy that it +will never use NSS, and will never use and libc calls that make use of NSS. +This allows you to run an embedded system without the need for installing an +/etc/nsswitch.conf file and without and /lib/libnss_* libraries installed. + +If you are using a system that is using a remote LDAP server for authentication +via GNU libc NSS, and you want to use BusyBox, then you will need to adjust the +BusyBox source. Chances are though, that if you have enough space to install +of that stuff on your system, then you probably want the full GNU utilities. + +=head1 SEE ALSO + +textutils(1), shellutils(1), etc... + +=head1 MAINTAINER + +Erik Andersen + +=head1 AUTHORS + +The following people have contributed code to BusyBox whether +they know it or not. + + +=for html
+ +Erik Andersen , + + Tons of new stuff, major rewrite of most of the + core apps, tons of new apps as noted in header files. + +=for html
+ +John Beppu + + du, head, nslookup, sort, tee, uniq (so Kraai could rewrite them ;-), + documentation + +=for html
+ +Edward Betts + + expr, hostid, logname, tty, wc, whoami, yes + +=for html
+ +Brian Candler + + tiny-ls(ls) + +=for html
+ +Randolph Chung + + fbset, ping, hostname, and mkfifo + +=for html
+ +Dave Cinege + + more(v2), makedevs, dutmp, modularization, auto links file, + various fixes, Linux Router Project maintenance + +=for html
+ +Larry Doolittle + + various fixes, shell rewrite + +=for html
+ +Karl M. Hegbloom + + cp_mv.c, the test suite, various fixes to utility.c, &c. + +=for html
+ +Sterling Huxley + + vi (!!!) + +=for html
+ +Daniel Jacobowitz + + mktemp.c + +=for html
+ +Matt Kraai + + documentation, bugfixes + +=for html
+ +John Lombardo + + dirname, tr + +=for html
+ +Glenn McGrath + + ar.c + +=for html
+ +Vladimir Oleynik + + cmdedit, stty-port, locale, various fixes + and irreconcilable critic of everything not perfect. + +=for html
+ +Bruce Perens + + Original author of BusyBox. His code is still in many apps. + +=for html
+ +Chip Rosenthal , + + wget - Contributed by permission of Covad Communications + +=for html
+ +Pavel Roskin + + Lots of bugs fixes and patches. + +=for html
+ +Gyepi Sam + + Remote logging feature for syslogd + +=for html
+ +Linus Torvalds + + mkswap, fsck.minix, mkfs.minix + +=for html
+ +Mark Whitley + + sed remix, bug fixes, style-guide, etc. + +=for html
+ +Charles P. Wright + + gzip, mini-netcat(nc) + +=for html
+ +Enrique Zanardi + + tarcat (since removed), loadkmap, various fixes, Debian maintenance + +=cut + +# $Id: busybox_footer.pod,v 1.4 2001/04/17 17:09:34 beppu Exp $ diff --git a/busybox/docs/busybox_header.pod b/busybox/docs/busybox_header.pod new file mode 100644 index 000000000..84a2a5f44 --- /dev/null +++ b/busybox/docs/busybox_header.pod @@ -0,0 +1,73 @@ +# vi: set sw=4 ts=4: + +=head1 NAME + +BusyBox - The Swiss Army Knife of Embedded Linux + +=head1 SYNTAX + + BusyBox [arguments...] # or + + [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 fileutils, shellutils, findutils, textutils, grep, gzip, +tar, etc. BusyBox provides a fairly complete POSIX environment for any small +or embedded system. The utilities in BusyBox generally have fewer options than +their full-featured GNU cousins; 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 a kernel, a shell (such as ash), +and an editor (such as elvis-tiny or ae). + +=head1 USAGE + +When you create a link to BusyBox for the function you wish to use, when BusyBox +is called using that link it will behave as if the command itself has been invoked. + +For example, entering + + ln -s ./BusyBox ls + ./ls + +will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled +into BusyBox). + +You can also invoke BusyBox by issuing the command as an argument on the +command line. For example, entering + + ./BusyBox ls + +will also cause BusyBox to behave as 'ls'. + +=head1 COMMON OPTIONS + +Most BusyBox commands support the B<-h> option to provide a +terse runtime description of their behavior. + +=head1 COMMANDS + +Currently defined functions include: + +adjtimex, ar, basename, busybox, cat, chgrp, chmod, chown, chroot, chvt, clear, +cmp, cp, cpio, cut, date, dc, dd, deallocvt, df, dirname, dmesg, dos2unix, dpkg, +dpkg-deb, du, dumpkmap, dutmp, echo, expr, false, fbset, fdflush, find, free, +freeramdisk, fsck.minix, getopt, grep, gunzip, gzip, halt, head, hostid, +hostname, id, ifconfig, init, insmod, kill, killall, klogd, length, ln, +loadacm, loadfont, loadkmap, logger, logname, ls, lsmod, makedevs, md5sum, +mkdir, mkfifo, mkfs.minix, mknod, mkswap, mktemp, more, mount, mt, mv, nc, +nslookup, ping, pivot_root, poweroff, printf, ps, pwd, rdate, readlink, reboot, +renice, reset, rm, rmdir, rmmod, route, rpm2cpio, rpmunpack, sed, setkeycodes, +sh, sleep, sort, stty, swapoff, swapon, sync, syslogd, tail, tar, tee, telnet, +test, tftp, touch, tr, true, tty, umount, uname, uniq, unix2dos, update, uptime, +usleep, uudecode, uuencode, watchdog, wc, wget, which, whoami, xargs, yes, zcat, +[ + +=over 4 + diff --git a/busybox/docs/contributing.txt b/busybox/docs/contributing.txt new file mode 100644 index 000000000..2e0049289 --- /dev/null +++ b/busybox/docs/contributing.txt @@ -0,0 +1,476 @@ +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.lineo.com + + + +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://oss.lineo.com/cvs_anon.html + http://oss.lineo.com/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://opensource.lineo.com/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://opensource.lineo.com/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 +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 a bug in Busybox, you can send a bug report to our bug tracking +system (homepage: http://bugs.lineo.com). Instructions on how to send a bug +report to the tracking system can be found at: + + http://bugs.lineo.com/Reporting.html + +The README file that comes with Busybox also describes how to submit a bug. + +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: + + When I execute Busybox 'date' it produces unexpected results. + + This is using GNU date: + $ date + Wed Mar 21 14:19:41 MST 2001 + + This is using Busybox date: + $ date + codswaddle + + +Bug Triage +~~~~~~~~~~ + +Validating and confirming bugs is nearly as important as reporting them in the +first place. It is valuable to know if a bug can be duplicated on a different +machine, on a different filesystem, on a different architecture, with a +different C library, and so forth. + +To see a listing of all the bugs currently filed against Busybox, look here: + + http://bugs.lineo.com/db/pa/lbusybox.html + +If you have comments to add to a bug (can / can't duplicate, think a bug +should be closed / reopened), please send it to [bugnumber]@bugs.lineo.com. +The message you send will automatically be forwarded to the mailing list for +all to see. + + +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 grep $i *.[ch]; 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. + + +Consult The Bug-Tracking System +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Head to: http://bugs.lineo.com/db/pa/lBusybox.html and look at the bugs on +there. Pick one you think you can fix, and fix it. If it's a wishlist item and +someone's requesting a new feature, take a stab at adding it. Everything +previously said about "reading the mailing list" and "coordinating with the +applet maintainer" still applies. + + +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 'grep -L + getopt *.c' 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 utility.c. + + - Security audits: + http://www.securityfocus.com/frames/?content=/forums/secprog/secure-programming.html + + - 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 funciton 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://web.onetelnet.ch/~twolf/tw/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 bug +tracking system (http://bugs.lineo.com). You do this by emailing your patch in +a message to submit@bugs.lineo.com 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: v0.50pre (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. + +Detailed instructions on how to submit a bug to the tracking system are at: + + http://bugs.lineo.com/Reporting.html + +If you have a patch that will fix and close a reported bug, please send a +message to [bugnumber]@bugs.lineo.com with your patch attached. It will catch +people's attention if you have a subject line like the following: + + [PATCH INCLUDED] - Fix attached, please apply and close this bug + + + +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 index 000000000..1f5c3ebd5 --- /dev/null +++ b/busybox/docs/new-applet-HOWTO.txt @@ -0,0 +1,138 @@ +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 + + +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 _main instead +of main. And be sure to put it in .c. 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] + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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. + + +Usage String(s) +--------------- + +Next, add usage information for you applet to 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 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 BB_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 Config.h: + + #define BB_MU + + +Documentation +------------- + +If you're feeling especially nice, you should also document your applet in the +docs directory (but nobody ever does that). + + +The Grand Announcement +---------------------- + +Then create a diff -urN of the files you added (.c, usage.c, +applets.h, Config.h) and send it to the mailing list: +busybox@opensource.lineo.com. 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 index 000000000..c71f1e609 --- /dev/null +++ b/busybox/docs/style-guide.txt @@ -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 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 'scripts/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 'scripts/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 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 BB_FEATURE_FUNKY + maybe_do_funky_stuff(bar, baz); + #endif + + Do this instead: + + (in .h header file) + + #ifdef BB_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_BB_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 + +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/dos2unix.c b/busybox/dos2unix.c new file mode 100644 index 000000000..02b70d915 --- /dev/null +++ b/busybox/dos2unix.c @@ -0,0 +1,188 @@ +/* + * 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 . + * 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 +#include +#include +#include +#include +#include "busybox.h" + +/* Teach libc5 what a uint64_t is */ +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) +typedef unsigned long int uint64_t; +#endif + +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 uint64_t value=0; + FILE *in = stdin, *out = stdout; + + if (fn != NULL) { + if ((in = wfopen(fn, "rw")) == NULL) { + return -1; + } + strcpy(tempFn, fn); + c = strlen(tempFn); + tempFn[c] = '.'; + while(1) { + if (c >=BUFSIZ) + 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 += ((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) { + perror_msg(NULL); + 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)) { + perror_msg(NULL); + 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) { + perror_msg(NULL); + remove(tempFn); + return -2; + } + + /* Assume they are both on the same filesystem */ + if (rename(tempFn, fn) < 0) { + 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: + 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/dpkg.c b/busybox/dpkg.c new file mode 100644 index 000000000..48c392894 --- /dev/null +++ b/busybox/dpkg.c @@ -0,0 +1,1445 @@ +/* + * Mini dpkg implementation for busybox. + * This is not meant as a replacemnt for dpkg + * + * 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 dont + * 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. + * - The .control file is extracted and kept in the info dir. + * - When installing a package the Status: field is placed at the end of the + * section, rather than just after the Package: field. + * - Packages with previously unknown status are inserted at the begining of + * the status file + * + * Bugs that need to be fixed + * - (unknown, please let me know when you find any) + * + */ + +#include +#include +#include +#include +#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 leaway 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 dependecies 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, dont 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] = 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 = xstrdup(""); + } + if (version2 != NULL) { + version2 = 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 = xstrndup(&version1[len1], tmp_int); + len1 += tmp_int; + tmp_int = strcspn(&version2[len2], "0123456789"); + name2_char = 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 = xstrndup(&version1[len1], tmp_int); + len1 += tmp_int; + tmp_int = strspn(&version2[len2], "0123456789"); + name2_char = 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 = xstrdup(ver1_ptr); + upstream_ver2 = 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); +} + +/* + * Create one new node and one new edge for every dependency. + */ +void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned int edge_type) +{ + char *line = xstrdup(whole_line); + char *line2; + char *line_ptr1 = NULL; + char *line_ptr2 = NULL; + char *field; + char *field2; + char *version; + edge_t *edge; + int offset_ch; + int type; + + field = strtok_r(line, ",", &line_ptr1); + do { + line2 = xstrdup(field); + field2 = strtok_r(line2, "|", &line_ptr2); + if ((edge_type == EDGE_DEPENDS) && (strcmp(field, field2) != 0)) { + type = EDGE_OR_DEPENDS; + } + else if ((edge_type == EDGE_PRE_DEPENDS) && (strcmp(field, field2) != 0)) { + type = EDGE_OR_PRE_DEPENDS; + } else { + type = edge_type; + } + + do { + edge = (edge_t *) xmalloc(sizeof(edge_t)); + edge->type = 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 charactors */ + 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 { + 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); + + /* link the new edge to the current node */ + parent_node->num_of_edges++; + parent_node->edge = xrealloc(parent_node->edge, sizeof(edge_t) * (parent_node->num_of_edges + 1)); + parent_node->edge[parent_node->num_of_edges - 1] = 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) +{ + int i; + if (node != NULL) { + for (i = 0; i < node->num_of_edges; i++) { + if (node->edge[i] != NULL) { + free(node->edge[i]); + } + } + if (node->edge != NULL) { + free(node->edge); + } + if (node != NULL) { + free(node); + } + } +} + +unsigned int fill_package_struct(char *control_buffer) +{ + common_node_t *new_node = (common_node_t *) xcalloc(1, sizeof(common_node_t)); + + char **field_name = xmalloc(sizeof(char *)); + char **field_value = xmalloc(sizeof(char *)); + 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) { + 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 !! + } + + if (strcmp(*field_name, "Package") == 0) { + new_node->name = search_name_hashtable(*field_value); + } + else if (strcmp(*field_name, "Version") == 0) { + new_node->version = search_name_hashtable(*field_value); + } + else if (strcmp(*field_name, "Pre-Depends") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_PRE_DEPENDS); + } + else if (strcmp(*field_name, "Depends") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_DEPENDS); + } + else if (strcmp(*field_name, "Replaces") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_REPLACES); + } + else if (strcmp(*field_name, "Provides") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_PROVIDES); + } + else if (strcmp(*field_name, "Conflicts") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_CONFLICTS); + } + else if (strcmp(*field_name, "Suggests") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_SUGGESTS); + } + else if (strcmp(*field_name, "Recommends") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_RECOMMENDS); + } + else if (strcmp(*field_name, "Enhances") == 0) { + add_split_dependencies(new_node, *field_value, EDGE_ENHANCES); + } +fill_package_struct_cleanup: + if (*field_name) { + free(*field_name); + } + if (*field_value) { + free(*field_value); + } + } + 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 seperating spaces */ + status_string += strspn(status_string, " "); + } + len = strcspn(status_string, " \n\0"); + state_sub_string = 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: + 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; +} + +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 = 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 = 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; +} + + +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); +} + +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 = xfopen("/var/lib/dpkg/status", "r"); + FILE *new_status_file = 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) { + tmp_string = strstr(control_buffer, "Package:") + 8; + tmp_string += strspn(tmp_string, " \n\t"); + package_name = 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 = 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) { + 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 == FALSE) { + fprintf(new_status_file, "%s\n\n", control_buffer); + } + + if (status_from_file != NULL) { + 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 seperate 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) { + error_msg_and_die("Couldnt create backup status file"); + } + /* Its ok if renaming the status file fails becasue status + * file doesnt exist, maybe we are starting from scratch */ + error_msg("No status file found, creating new one"); + } + + if (rename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status") == -1) { + error_msg_and_die("DANGER: Couldnt create status file, you need to manually repair your status file"); + } +} + +int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count) +{ + int *conflicts = NULL; + int conflicts_num = 0; + int state_status; + int state_flag; + int state_want; + unsigned int status_package_num; + int i = deb_start; + int j, k; + + /* 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 */ + for (i = 0; i < conflicts_num; i++) { + /* Check for conflicts */ + for (j = 0; j < STATUS_HASH_PRIME; j++) { + if (status_hashtable[j] == NULL) { + continue; + } + state_flag = get_status(j, 2); + state_status = get_status(j, 3); + if ((state_status != search_name_hashtable("installed")) + && (state_flag != search_name_hashtable("want-install"))) { + continue; + } + status_package_num = status_hashtable[j]->package; + for (k = 0; k < package_hashtable[status_package_num]->num_of_edges; k++) { + const edge_t *package_edge = package_hashtable[status_package_num]->edge[k]; + if (package_edge->type != EDGE_CONFLICTS) { + continue; + } + if (package_edge->name != package_hashtable[conflicts[i]]->name) { + continue; + } + /* There is a conflict against the package name + * check if version conflict as well */ + if (test_version(package_hashtable[deb_file[i]->package]->version, + package_edge->version, package_edge->operator)) { + error_msg_and_die("Package %s conflict with %s", + name_hashtable[package_hashtable[deb_file[i]->package]->name], + name_hashtable[package_hashtable[status_package_num]->name]); + } + } + } + } + + /* Check dependendcies */ + i = 0; + while (deb_file[i] != NULL) { + const common_node_t *package_node = package_hashtable[deb_file[i]->package]; + int status_num = 0; + + for (j = 0; j < package_hashtable[deb_file[i]->package]->num_of_edges; j++) { + const edge_t *package_edge = package_node->edge[j]; + const unsigned int package_num = search_package_hashtable(package_edge->name, + package_edge->version, package_edge->operator); + + status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]); + state_status = get_status(status_num, 3); + state_want = get_status(status_num, 1); + switch (package_edge->type) { + case(EDGE_PRE_DEPENDS): + case(EDGE_OR_PRE_DEPENDS): + /* It must be already installed */ + /* NOTE: This is untested, nothing apropriate in my status file */ + if ((package_hashtable[package_num] == NULL) || (state_status != search_name_hashtable("installed"))) { + error_msg_and_die("Package %s pre-depends on %s, but it is not installed", + name_hashtable[package_node->name], + name_hashtable[package_edge->name]); + } + break; + case(EDGE_DEPENDS): + case(EDGE_OR_DEPENDS): + /* It must be already installed, or to be installed */ + if ((package_hashtable[package_num] == NULL) || + ((state_status != search_name_hashtable("installed")) && + (state_want != search_name_hashtable("want_install")))) { + error_msg_and_die("Package %s depends on %s, but it is not installed, or flaged to be installed", + name_hashtable[package_node->name], + name_hashtable[package_edge->name]); + } + break; + } + } + i++; + } + free(conflicts); + return(TRUE); +} + +char **create_list(const char *filename) +{ + FILE *list_stream; + char **file_list = xmalloc(sizeof(char *)); + char *line = NULL; + char *last_char; + int length = 0; + int count = 0; + + /* dont use [xw]fopen here, handle error ourself */ + list_stream = fopen(filename, "r"); + if (list_stream == NULL) { + *file_list = NULL; + return(file_list); + } + while (getline(&line, &length, list_stream) != -1) { + file_list = xrealloc(file_list, sizeof(char *) * (length + 1)); + last_char = last_char_is(line, '\n'); + if (last_char) { + *last_char = '\0'; + } + file_list[count] = xstrdup(line); + free(line); + count++; + length = 0; + } + 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; + + 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) { + free(script_path); + return(EXIT_SUCCESS); + } else { + free(script_path); + return(system(script_path)); + } +} + +void all_control_list(char **remove_files, const char *package_name) +{ + const char *all_extensions[11] = {"preinst", "postinst", "prerm", "postrm", + "list", "md5sums", "shlibs", "conffiles", "config", "templates", NULL }; + int i; + + /* Create a list of all /var/lib/dpkg/info/ files */ + for(i = 0; i < 10; i++) { + remove_files[i] = xmalloc(strlen(package_name) + strlen(all_extensions[i]) + 21); + sprintf(remove_files[i], "/var/lib/dpkg/info/%s.%s", package_name, all_extensions[i]); + } + remove_files[10] = NULL; +} + +void remove_package(const unsigned int package_num) +{ + const char *package_name = name_hashtable[package_hashtable[package_num]->name]; + 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; + + printf("Removing %s ...\n", package_name); + + /* run prerm script */ + return_value = run_package_script(package_name, "prem"); + if (return_value == -1) { + error_msg_and_die("script failed, prerm failure"); + } + + /* Create a list of files to remove, and a seperate 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) == TRUE); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = xmalloc(11); + all_control_list(remove_files, package_name); + + /* Create a list of files in /var/lib/dpkg/info/.* to keep */ + exclude_files = xmalloc(sizeof(char*) * 3); + exclude_files[0] = 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; + + remove_file_array(remove_files, exclude_files); + + /* rename .conffile to .list */ + rename(conffile_name, list_name); + + /* Change package status */ + set_status(status_num, "deinstall", 1); + 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 unsigned int status_num = search_status_hashtable(package_name); + char **remove_files; + char **exclude_files; + char list_name[strlen(package_name) + 25]; + + /* run prerm script */ + if (run_package_script(package_name, "prerm") == -1) { + 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(1); + exclude_files[0] = NULL; + + /* Some directories cant be removed straight away, so do multiple passes */ + while (remove_file_array(remove_files, exclude_files) == TRUE); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = xmalloc(11); + all_control_list(remove_files, package_name); + remove_file_array(remove_files, exclude_files); + + /* run postrm script */ + if (run_package_script(package_name, "postrm") == -1) { + error_msg_and_die("postrm fialure.. set status to what?"); + } + + /* Change package status */ + set_status(status_num, "purge", 1); + set_status(status_num, "not-installed", 3); +} + +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]->status; + + FILE *out_stream; + char *info_prefix; + + /* 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); + } else { + printf("Unpacking %s (from %s) ...\n", package_name, deb_file->filename); + } + + /* Extract control.tar.gz to /var/lib/dpkg/info/.filename */ + info_prefix = (char *) xmalloc(sizeof(package_name) + 20 + 4 + 1); + sprintf(info_prefix, "/var/lib/dpkg/info/%s.", package_name); + deb_extract(deb_file->filename, stdout, (extract_quiet | extract_control_tar_gz | extract_all_to_fs), info_prefix, NULL); + + /* Extract data.tar.gz to the root directory */ + deb_extract(deb_file->filename, stdout, (extract_quiet | extract_data_tar_gz | extract_all_to_fs), "/", NULL); + + /* Create the list file */ + strcat(info_prefix, "list"); + out_stream = xfopen(info_prefix, "w"); + deb_extract(deb_file->filename, out_stream, (extract_quiet | extract_data_tar_gz | extract_list), NULL, NULL); + 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); + int return_value; + + printf("Setting up %s (%s)\n", package_name, package_version); + + /* Run the preinst prior to extracting */ + return_value = run_package_script(package_name, "postinst"); + if (return_value == -1) { + /* TODO: handle failure gracefully */ + 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); +} + +extern int dpkg_main(int argc, char **argv) +{ + deb_file_t **deb_file = NULL; + status_node_t *status_node; + char opt = 0; + 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; + } + case 'i': + dpkg_opt |= dpkg_opt_install; + dpkg_opt |= dpkg_opt_filename; + break; + case 'l': + dpkg_opt |= dpkg_opt_list_installed; + 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: + show_usage(); + } + } + + if ((argc == optind) || (dpkg_opt == 0)) { + show_usage(); + } + + puts("(Reading database ... xxxxx files and directories installed.)"); + index_status_file("/var/lib/dpkg/status"); + + /* Read arguments and store relevant info in structs */ + deb_file = xmalloc(sizeof(deb_file_t)); + while (optind < argc) { + deb_file[deb_count] = (deb_file_t *) xmalloc(sizeof(deb_file_t)); + if (dpkg_opt & dpkg_opt_filename) { + deb_file[deb_count]->filename = xstrdup(argv[optind]); + deb_file[deb_count]->control_file = deb_extract(argv[optind], stdout, (extract_control_tar_gz | extract_one_to_buffer), NULL, "./control"); + if (deb_file[deb_count]->control_file == NULL) { + error_msg_and_die("Couldnt extract control file"); + } + package_num = fill_package_struct(deb_file[deb_count]->control_file); + + if (package_num == -1) { + 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)) { + status_node = (status_node_t *) xmalloc(sizeof(status_node_t)); + status_node->package = deb_file[deb_count]->package; + /* use 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_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + status_hashtable[status_num] = status_node; + } + } + 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) { + error_msg_and_die("Package %s is uninstalled or unknown\n", argv[optind]); + } + state_status = get_status(search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]), 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)) { + error_msg_and_die("%s is already removed.", name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + } + } + 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) { + error_msg_and_die("%s is already purged.", name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + } + } + } + deb_count++; + optind++; + } + deb_file[deb_count] = NULL; + + /* Check that the deb file arguments are installable */ + /* TODO: check dependencies before removing */ + if ((dpkg_opt & dpkg_opt_force_ignore_depends) != dpkg_opt_force_ignore_depends) { + if (!check_deps(deb_file, 0, deb_count)) { + error_msg_and_die("Dependency check failed"); + } + } + + for (i = 0; i < deb_count; i++) { + /* Remove or purge packages */ + if (dpkg_opt & dpkg_opt_remove) { + remove_package(deb_file[i]->package); + } + 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]); + configure_package(deb_file[i]); + } + else if (dpkg_opt & dpkg_opt_configure) { + 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++) { + if (name_hashtable[i] != NULL) { + free(name_hashtable[i]); + } + } + + for (i = 0; i < PACKAGE_HASH_PRIME; i++) { + free_package(package_hashtable[i]); + } + + for (i = 0; i < STATUS_HASH_PRIME; i++) { + if (status_hashtable[i] != NULL) { + free(status_hashtable[i]); + } + } + + return(EXIT_FAILURE); +} + diff --git a/busybox/dpkg_deb.c b/busybox/dpkg_deb.c new file mode 100644 index 000000000..a933c6948 --- /dev/null +++ b/busybox/dpkg_deb.c @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 +#include +#include +#include "busybox.h" + +extern int dpkg_deb_main(int argc, char **argv) +{ + char *prefix = NULL; + char *filename = NULL; + char *output_buffer = NULL; + int opt = 0; + int arg_type = 0; + int deb_extract_funct = extract_create_leading_dirs | extract_unconditional; + + const int arg_type_prefix = 1; + const int arg_type_field = 2; + const int arg_type_filename = 4; +// const int arg_type_un_ar_gz = 8; + + while ((opt = getopt(argc, argv, "ceftXxI")) != -1) { + switch (opt) { + case 'c': + deb_extract_funct |= extract_data_tar_gz; + deb_extract_funct |= extract_verbose_list; + break; + case 'e': + arg_type = arg_type_prefix; + deb_extract_funct |= extract_control_tar_gz; + deb_extract_funct |= extract_all_to_fs; + break; + case 'f': + arg_type = arg_type_field; + deb_extract_funct |= extract_control_tar_gz; + deb_extract_funct |= extract_one_to_buffer; + filename = xstrdup("./control"); + break; + case 't': /* --fsys-tarfile, i just made up this short name */ + /* Integrate the functionality needed with some code from ar.c */ + error_msg_and_die("Option disabled"); +// arg_type = arg_type_un_ar_gz; + break; + case 'X': + arg_type = arg_type_prefix; + deb_extract_funct |= extract_data_tar_gz; + deb_extract_funct |= extract_all_to_fs; + deb_extract_funct |= extract_list; + case 'x': + arg_type = arg_type_prefix; + deb_extract_funct |= extract_data_tar_gz; + deb_extract_funct |= extract_all_to_fs; + break; + case 'I': + arg_type = arg_type_filename; + deb_extract_funct |= extract_control_tar_gz; + deb_extract_funct |= extract_one_to_buffer; + break; + default: + show_usage(); + } + } + + if (optind == argc) { + show_usage(); + } + + /* Workout where to extract the files */ + if (arg_type == arg_type_prefix) { + /* argument is a dir name */ + if ((optind + 1) == argc ) { + prefix = xstrdup("./DEBIAN/"); + } else { + prefix = (char *) xmalloc(strlen(argv[optind + 1]) + 2); + strcpy(prefix, argv[optind + 1]); + /* Make sure the directory has a trailing '/' */ + if (last_char_is(prefix, '/') == NULL) { + strcat(prefix, "/"); + } + } + mkdir(prefix, 0777); + } + + if (arg_type == arg_type_filename) { + if ((optind + 1) != argc) { + filename = xstrdup(argv[optind + 1]); + } else { + error_msg_and_die("-I currently requires a filename to be specified"); + } + } + + output_buffer = deb_extract(argv[optind], stdout, deb_extract_funct, prefix, filename); + + if ((arg_type == arg_type_filename) && (output_buffer != NULL)) { + puts(output_buffer); + } + else if (arg_type == arg_type_field) { + char *field = NULL; + char *name; + char *value; + int field_start = 0; + + while (1) { + field_start += read_package_field(&output_buffer[field_start], &name, &value); + if (name == NULL) { + break; + } + if (strcmp(name, argv[optind + 1]) == 0) { + puts(value); + } + free(field); + } + } + + return(EXIT_SUCCESS); +} diff --git a/busybox/du.c b/busybox/du.c new file mode 100644 index 000000000..fb649aee5 --- /dev/null +++ b/busybox/du.c @@ -0,0 +1,257 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini du implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +#ifdef BB_FEATURE_HUMAN_READABLE +static unsigned long disp_hr = KILOBYTE; +#endif + +typedef void (Display) (long, char *); + +static int du_depth = 0; +static int count_hardlinks = 0; + +static Display *print; + +static void print_normal(long size, char *filename) +{ +#ifdef BB_FEATURE_HUMAN_READABLE + printf("%s\t%s\n", make_human_readable_str(size<<10, 1, disp_hr), filename); +#else + printf("%ld\t%s\n", size, filename); +#endif +} + +static void print_summary(long size, char *filename) +{ + if (du_depth == 1) { + print_normal(size, filename); + } +} + +#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. + */ +static 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 */ +static 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; +} + +/* Clear statbuf hash table */ +static 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; + } + } +} + +/* tiny recursive du */ +static long du(char *filename) +{ + struct stat statbuf; + long sum; + + if ((lstat(filename, &statbuf)) != 0) { + perror_msg("%s", filename); + return 0; + } + + du_depth++; + sum = (statbuf.st_blocks >> 1); + + /* Don't add in stuff pointed to by symbolic links */ + if (S_ISLNK(statbuf.st_mode)) { + sum = 0L; + if (du_depth == 1) { + } + } + if (S_ISDIR(statbuf.st_mode)) { + DIR *dir; + struct dirent *entry; + char *newfile; + + dir = opendir(filename); + if (!dir) { + du_depth--; + return 0; + } + + newfile = last_char_is(filename, '/'); + if (newfile) + *newfile = '\0'; + + while ((entry = readdir(dir))) { + char *name = entry->d_name; + + if ((strcmp(name, "..") == 0) + || (strcmp(name, ".") == 0)) { + continue; + } + newfile = concat_path_file(filename, name); + sum += du(newfile); + free(newfile); + } + closedir(dir); + print(sum, filename); + } + else if (statbuf.st_nlink > 1 && !count_hardlinks) { + /* Add files with hard links only once */ + if (is_in_ino_dev_hashtable(&statbuf, NULL)) { + sum = 0L; + if (du_depth == 1) + print(sum, filename); + } + else { + add_to_ino_dev_hashtable(&statbuf, NULL); + } + } + du_depth--; + return sum; +} + +int du_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int i; + int c; + + /* default behaviour */ + print = print_normal; + + /* parse argv[] */ + while ((c = getopt(argc, argv, "sl" +#ifdef BB_FEATURE_HUMAN_READABLE +"hm" +#endif +"k")) != EOF) { + switch (c) { + case 's': + print = print_summary; + break; + case 'l': + count_hardlinks = 1; + break; +#ifdef BB_FEATURE_HUMAN_READABLE + case 'h': disp_hr = 0; break; + case 'm': disp_hr = MEGABYTE; break; +#endif + case 'k': break; + default: + show_usage(); + } + } + + /* go through remaining args (if any) */ + if (optind >= argc) { + if (du(".") == 0) + status = EXIT_FAILURE; + } else { + long sum; + + for (i=optind; i < argc; i++) { + sum = du(argv[i]); + if(is_directory(argv[i], FALSE, NULL)==FALSE) { + print_normal(sum, argv[i]); + } + reset_ino_dev_hashtable(); + } + } + + return status; +} + +/* $Id: du.c,v 1.50 2001/06/30 17:54:20 andersen Exp $ */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/dumpkmap.c b/busybox/dumpkmap.c new file mode 100644 index 000000000..22652a5e2 --- /dev/null +++ b/busybox/dumpkmap.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dumpkmap implementation for busybox + * + * Copyright (C) Arne Bernin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* From */ +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 */ +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]=='-') { + show_usage(); + } + + fd = open(CURRENT_VC, O_RDWR); + if (fd < 0) { + perror_msg("Error opening " CURRENT_VC); + return EXIT_FAILURE; + } + + 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) { + + error_msg("ioctl returned: %s, %s, %s, %xqq", strerror(errno),(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/dutmp.c b/busybox/dutmp.c new file mode 100644 index 000000000..df7f64d30 --- /dev/null +++ b/busybox/dutmp.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * public domain -- Dave 'Kill a Cop' Cinege + * + * dutmp + * Takes utmp formated file on stdin and dumps it's contents + * out in colon delimited fields. Easy to 'cut' for shell based + * versions of 'who', 'last', etc. IP Addr is output in hex, + * little endian on x86. + * + * Modified to support all sorts of libcs by + * Erik Andersen + */ + +#include +#include + +#include +#include +#include +#include +#include "busybox.h" + +extern int dutmp_main(int argc, char **argv) +{ + + int file; + struct utmp ut; + + if (argc<2) { + file = fileno(stdin); + } else if (*argv[1] == '-' ) { + show_usage(); + } else { + file = open(argv[1], O_RDONLY); + if (file < 0) { + perror_msg_and_die(io_error, argv[1]); + } + } + +/* Kludge around the fact that the binary format for utmp has changed. */ +#if __GNU_LIBRARY__ < 5 || defined __UCLIBC__ + /* Linux libc5 */ + while (read(file, (void*)&ut, sizeof(struct utmp))) { + printf("%d|%d|%s|%s|%s|%s|%s|%lx\n", + ut.ut_type, ut.ut_pid, ut.ut_line, + ut.ut_id, ut.ut_user, ut.ut_host, + ctime(&(ut.ut_time)), + (long)ut.ut_addr); + } +#else + /* Glibc, uClibc, etc. */ + while (read(file, (void*)&ut, sizeof(struct utmp))) { + printf("%d|%d|%s|%s|%s|%s|%d|%d|%ld|%ld|%ld|%x\n", + ut.ut_type, ut.ut_pid, ut.ut_line, + ut.ut_id, ut.ut_user, ut.ut_host, + ut.ut_exit.e_termination, ut.ut_exit.e_exit, + ut.ut_session, + ut.ut_tv.tv_sec, ut.ut_tv.tv_usec, + ut.ut_addr); + } +#endif + return EXIT_SUCCESS; +} diff --git a/busybox/echo.c b/busybox/echo.c new file mode 100644 index 000000000..31c031528 --- /dev/null +++ b/busybox/echo.c @@ -0,0 +1,152 @@ +/* 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. + */ + +#include +#include +#include +#include "busybox.h" + +extern int +echo_main(int argc, char** argv) +{ + int nflag = 0; + int eflag = 0; + + /* Skip argv[0]. */ + argc--; + argv++; + + while (argc > 0 && *argv[0] == '-') + { + register char *temp; + register int ix; + + /* + * 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. + */ + temp = argv[0] + 1; + + for (ix = 0; temp[ix]; ix++) + { + if (strrchr("neE", temp[ix]) == 0) + goto just_echo; + } + + if (!*temp) + goto just_echo; + + /* + * All of the options in temp are valid options to echo. + * Handle them. + */ + while (*temp) + { + if (*temp == 'n') + nflag = 1; + else if (*temp == 'e') + eflag = 1; + else if (*temp == 'E') + eflag = 0; + else + goto just_echo; + + temp++; + } + argc--; + argv++; + } + +just_echo: + while (argc > 0) { + const char *arg = argv[0]; + register int c; + + while ((c = *arg++)) { + + /* Check for escape sequence. */ + if (c == '\\' && eflag && *arg) { + if (*arg == 'c') { + /* '\c' means cancel newline. */ + nflag = 1; + arg++; + continue; + } else { + c = process_escape_sequence(&arg); + } + } + + putchar(c); + } + argc--; + argv++; + if (argc > 0) + putchar(' '); + } + if (!nflag) + putchar('\n'); + fflush(stdout); + + return 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. + * + * 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/editors/sed.c b/busybox/editors/sed.c new file mode 100644 index 000000000..a18cfc7c3 --- /dev/null +++ b/busybox/editors/sed.c @@ -0,0 +1,800 @@ +/* + * sed.c - very minimalist version of sed + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + 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) + + (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: + + - transliteration (y/source-chars/dest-chars/) (use 'tr') + - no pattern space hold space storing / swapping (x, etc.) + - no labels / branching (: label, b, t, and friends) + - and lots, lots more. +*/ + +#include +#include /* for getopt() */ +#include +#include /* for strdup() */ +#include +#include /* for isspace() */ +#include +#include "busybox.h" + +/* externs */ +extern void xregcomp(regex_t *preg, const char *regex, int cflags); +extern int optind; /* in unistd.h */ +extern char *optarg; /* ditto */ + +/* options */ +static int be_quiet = 0; + + +struct sed_cmd { + + + /* GENERAL FIELDS */ + char delimiter; /* The delimiter used to separate regexps */ + + /* address storage */ + int beg_line; /* 'sed 1p' 0 == no begining line, apply commands to all lines */ + int end_line; /* 'sed 1,3p' 0 == no end line, use only beginning. -1 == $ */ + regex_t *beg_match; /* sed -e '/match/cmd' */ + regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */ + + /* the command */ + char cmd; /* p,d,s (add more at your leisure :-) */ + + + /* SUBSTITUTION COMMAND SPECIFIC FIELDS */ + + /* sed -e 's/sub_match/replace/' */ + regex_t *sub_match; + char *replace; + unsigned int num_backrefs:4; /* how many back references (\1..\9) */ + /* Note: GNU/POSIX sed does not save more than nine backrefs, so + * we only use 4 bits to hold the number */ + unsigned int sub_g:1; /* sed -e 's/foo/bar/g' (global) */ + unsigned int sub_p:2; /* sed -e 's/foo/bar/p' (print substitution) */ + + + /* EDIT COMMAND (a,i,c) SPEICIFIC FIELDS */ + + char *editline; + + + /* FILE COMMAND (r) SPEICIFIC FIELDS */ + + char *filename; +}; + +/* globals */ +static struct sed_cmd *sed_cmds = NULL; /* growable arrary holding a sequence of sed cmds */ +static int ncmds = 0; /* number of sed commands */ + +/*static char *cur_file = NULL;*/ /* file currently being processed XXX: do I need this? */ + +#ifdef BB_FEATURE_CLEAN_UP +static void destroy_cmd_strs() +{ + if (sed_cmds == NULL) + return; + + /* destroy all the elements in the array */ + while (--ncmds >= 0) { + + if (sed_cmds[ncmds].beg_match) { + regfree(sed_cmds[ncmds].beg_match); + free(sed_cmds[ncmds].beg_match); + } + if (sed_cmds[ncmds].end_match) { + regfree(sed_cmds[ncmds].end_match); + free(sed_cmds[ncmds].end_match); + } + if (sed_cmds[ncmds].sub_match) { + regfree(sed_cmds[ncmds].sub_match); + free(sed_cmds[ncmds].sub_match); + } + if (sed_cmds[ncmds].replace) + free(sed_cmds[ncmds].replace); + } + + /* destroy the array */ + free(sed_cmds); + sed_cmds = NULL; +} +#endif + + +/* + * 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 preceeded by + * a backslash ('\'). + */ +static int index_of_next_unescaped_regexp_delim(struct sed_cmd *sed_cmd, const char *str, int idx) +{ + for ( ; str[idx]; idx++) { + if (str[idx] == sed_cmd->delimiter && str[idx-1] != '\\') + return idx; + } + + /* if we make it to here, we've hit the end of the string */ + return -1; +} + +/* + * returns the index in the string just past where the address ends. + */ +static int get_address(struct sed_cmd *sed_cmd, const char *str, int *linenum, regex_t **regex) +{ + char *my_str = strdup(str); + int idx = 0; + char olddelimiter; + olddelimiter = sed_cmd->delimiter; + sed_cmd->delimiter = '/'; + + if (isdigit(my_str[idx])) { + do { + idx++; + } while (isdigit(my_str[idx])); + my_str[idx] = 0; + *linenum = atoi(my_str); + } + else if (my_str[idx] == '$') { + *linenum = -1; + idx++; + } + else if (my_str[idx] == '/') { + idx = index_of_next_unescaped_regexp_delim(sed_cmd, my_str, ++idx); + if (idx == -1) + error_msg_and_die("unterminated match expression"); + my_str[idx] = '\0'; + *regex = (regex_t *)xmalloc(sizeof(regex_t)); + xregcomp(*regex, my_str+1, REG_NEWLINE); + idx++; /* so it points to the next character after the last '/' */ + } + else { + error_msg("get_address: no address found in string\n" + "\t(you probably didn't check the string you passed me)"); + idx = -1; + } + + free(my_str); + sed_cmd->delimiter = olddelimiter; + return idx; +} + +static int parse_subst_cmd(struct sed_cmd *sed_cmd, const char *substr) +{ + int oldidx, cflags = REG_NEWLINE; + char *match; + int idx = 0; + int j; + + /* + * the string that gets passed to this function should look like this: + * s/match/replace/gIp + * || | ||| + * mandatory optional + * + * (all three of the '/' slashes are mandatory) + */ + + /* verify that the 's' is followed by something. That something + * (typically a 'slash') is now our regexp delimiter... */ + if (!substr[++idx]) + error_msg_and_die("bad format in substitution expression"); + else + sed_cmd->delimiter=substr[idx]; + + /* save the match string */ + oldidx = idx+1; + idx = index_of_next_unescaped_regexp_delim(sed_cmd, substr, ++idx); + if (idx == -1) + error_msg_and_die("bad format in substitution expression"); + match = xstrndup(substr + oldidx, idx - oldidx); + + /* 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 */ + + /* sed_cmd->num_backrefs = 0; */ /* XXX: not needed? --apparently not */ + for (j = 0; match[j]; j++) { + /* GNU/POSIX sed does not save more than nine backrefs */ + if (match[j] == '\\' && match[j+1] == '(' && sed_cmd->num_backrefs <= 9) + sed_cmd->num_backrefs++; + } + + /* save the replacement string */ + oldidx = idx+1; + idx = index_of_next_unescaped_regexp_delim(sed_cmd, substr, ++idx); + if (idx == -1) + error_msg_and_die("bad format in substitution expression"); + sed_cmd->replace = xstrndup(substr + oldidx, idx - oldidx); + + /* process the flags */ + while (substr[++idx]) { + switch (substr[idx]) { + case 'g': + sed_cmd->sub_g = 1; + break; + case 'I': + cflags |= REG_ICASE; + break; + case 'p': + sed_cmd->sub_p = 1; + break; + default: + /* any whitespace or semicolon trailing after a s/// is ok */ + if (strchr("; \t\v\n\r", substr[idx])) + goto out; + /* else */ + error_msg_and_die("bad option in substitution expression"); + } + } + +out: + /* compile the match string into a regex */ + sed_cmd->sub_match = (regex_t *)xmalloc(sizeof(regex_t)); + xregcomp(sed_cmd->sub_match, match, cflags); + free(match); + + return idx; +} + +static int parse_edit_cmd(struct sed_cmd *sed_cmd, const char *editstr) +{ + int idx = 0; + int slashes_eaten = 0; + char *ptr; /* shorthand */ + + /* + * the string that gets passed to this function should look like this: + * + * need one of these + * | + * | this backslash (immediately following the edit command) is mandatory + * | | + * [aic]\ + * TEXT1\ + * TEXT2\ + * TEXTN + * + * as soon as we hit a TEXT line that has no trailing '\', we're done. + * this means a command like: + * + * i\ + * INSERTME + * + * is a-ok. + * + */ + + if (editstr[1] != '\\' && (editstr[2] != '\n' || editstr[2] != '\r')) + error_msg_and_die("bad format in edit expression"); + + /* store the edit line text */ + /* make editline big enough to accomodate the extra '\n' we will tack on + * to the end */ + sed_cmd->editline = xmalloc(strlen(&editstr[3]) + 2); + strcpy(sed_cmd->editline, &editstr[3]); + ptr = sed_cmd->editline; + + /* now we need to go through * and: s/\\[\r\n]$/\n/g on the edit line */ + while (ptr[idx]) { + while (ptr[idx] != '\\' || (ptr[idx+1] != '\n' && ptr[idx+1] != '\r')) { + idx++; + if (!ptr[idx]) { + goto out; + } + } + /* move the newline over the '\' before it (effectively eats the '\') */ + memmove(&ptr[idx], &ptr[idx+1], strlen(&ptr[idx+1])); + ptr[strlen(ptr)-1] = 0; + slashes_eaten++; + /* substitue \r for \n if needed */ + if (ptr[idx] == '\r') + ptr[idx] = '\n'; + } + +out: + /* this accounts for discrepancies between the modified string and the + * original string passed in to this function */ + idx += slashes_eaten; + + /* figure out if we need to add a newline */ + if (ptr[idx-1] != '\n') { + ptr[idx] = '\n'; + idx++; + } + + /* terminate string */ + ptr[idx]= 0; + /* adjust for opening 2 chars [aic]\ */ + idx += 2; + + return idx; +} + + +static int parse_file_cmd(struct sed_cmd *sed_cmd, const char *filecmdstr) +{ + int idx = 0; + int filenamelen = 0; + + /* + * the string that gets passed to this function should look like this: + * '[ ]filename' + * | | + * | a filename + * | + * optional whitespace + + * re: the file to be read, the GNU manual says the following: "Note that + * if filename cannot be read, it is treated as if it were an empty file, + * without any error indication." Thus, all of the following commands are + * perfectly leagal: + * + * sed -e '1r noexist' + * sed -e '1r ;' + * sed -e '1r' + */ + + /* the file command may be followed by whitespace; move past it. */ + while (isspace(filecmdstr[++idx])) + { ; } + + /* the first non-whitespace we get is a filename. the filename ends when we + * hit a normal sed command terminator or end of string */ + filenamelen = strcspn(&filecmdstr[idx], "; \n\r\t\v\0"); + sed_cmd->filename = xmalloc(filenamelen + 1); + safe_strncpy(sed_cmd->filename, &filecmdstr[idx], filenamelen + 1); + + return idx + filenamelen; +} + + +static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) +{ + int idx = 0; + + /* parse the command + * format is: [addr][,addr]cmd + * |----||-----||-| + * part1 part2 part3 + */ + + /* first part (if present) is an address: either a number or a /regex/ */ + if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/') + idx = get_address(sed_cmd, cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); + + /* second part (if present) will begin with a comma */ + if (cmdstr[idx] == ',') + idx += get_address(sed_cmd, &cmdstr[++idx], &sed_cmd->end_line, &sed_cmd->end_match); + + /* last part (mandatory) will be a command */ + if (cmdstr[idx] == '\0') + error_msg_and_die("missing command"); + sed_cmd->cmd = cmdstr[idx]; + + /* if it was a single-letter command that takes no arguments (such as 'p' + * or 'd') all we need to do is increment the index past that command */ + if (strchr("pd", cmdstr[idx])) { + idx++; + } + /* handle (s)ubstitution command */ + else if (sed_cmd->cmd == 's') { + idx += parse_subst_cmd(sed_cmd, &cmdstr[idx]); + } + /* 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') + error_msg_and_die("only a beginning address can be specified for edit commands"); + idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]); + } + /* handle file cmds: (r)ead */ + else if (sed_cmd->cmd == 'r') { + if (sed_cmd->end_line || sed_cmd->end_match) + error_msg_and_die("Command only uses one address"); + idx += parse_file_cmd(sed_cmd, &cmdstr[idx]); + } + else { + error_msg_and_die("invalid command"); + } + + /* give back whatever's left over */ + return (char *)&cmdstr[idx]; +} + +static void add_cmd_str(const char *cmdstr) +{ + char *mystr = (char *)cmdstr; + + do { + + /* trim leading whitespace and semicolons */ + memmove(mystr, &mystr[strspn(mystr, "; \n\r\t\v")], strlen(mystr)); + /* if we ate the whole thing, that means there was just trailing + * whitespace or a final / no-op semicolon. either way, get out */ + if (strlen(mystr) == 0) + return; + /* if this is a comment, jump past it and keep going */ + if (mystr[0] == '#') { + mystr = strpbrk(mystr, ";\n\r"); + continue; + } + /* grow the array */ + sed_cmds = xrealloc(sed_cmds, sizeof(struct sed_cmd) * (++ncmds)); + /* zero new element */ + memset(&sed_cmds[ncmds-1], 0, sizeof(struct sed_cmd)); + /* load command string into new array element, get remainder */ + mystr = parse_cmd_str(&sed_cmds[ncmds-1], mystr); + + } while (mystr && strlen(mystr)); +} + + +static void load_cmd_file(char *filename) +{ + FILE *cmdfile; + char *line; + char *nextline; + + cmdfile = xfopen(filename, "r"); + + while ((line = get_line_from_file(cmdfile)) != NULL) { + /* if a line ends with '\' it needs the next line appended to it */ + while (line[strlen(line)-2] == '\\' && + (nextline = get_line_from_file(cmdfile)) != NULL) { + line = xrealloc(line, strlen(line) + strlen(nextline) + 1); + strcat(line, nextline); + free(nextline); + } + /* eat trailing newline (if any) --if I don't do this, edit commands + * (aic) will print an extra newline */ + chomp(line); + add_cmd_str(line); + free(line); + } +} + +static void print_subst_w_backrefs(const char *line, const char *replace, regmatch_t *regmatch) +{ + int i; + + /* 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] == '\\' && isdigit(replace[i+1])) { + int j; + char tmpstr[2]; + int backref; + ++i; /* i now indexes the backref number, instead of the leading slash */ + tmpstr[0] = replace[i]; + tmpstr[1] = 0; + backref = atoi(tmpstr); + /* print out the text held in regmatch[backref] */ + for (j = regmatch[backref].rm_so; j < regmatch[backref].rm_eo; j++) + fputc(line[j], stdout); + } + + /* if we find a backslash escaped character, print the character */ + else if (replace[i] == '\\') { + ++i; + fputc(replace[i], stdout); + } + + /* if we find an unescaped '&' print out the whole matched text. + * fortunately, regmatch[0] contains the indicies to the whole matched + * expression (kinda seems like it was designed for just such a + * purpose...) */ + else if (replace[i] == '&' && replace[i-1] != '\\') { + int j; + for (j = regmatch[0].rm_so; j < regmatch[0].rm_eo; j++) + fputc(line[j], stdout); + } + /* nothing special, just print this char of the replacement string to stdout */ + else + fputc(replace[i], stdout); + } +} + +static int do_subst_command(const struct sed_cmd *sed_cmd, const char *line) +{ + char *hackline = (char *)line; + int altered = 0; + regmatch_t *regmatch = NULL; + + /* we only proceed if the substitution 'search' expression matches */ + if (regexec(sed_cmd->sub_match, line, 0, NULL, 0) == REG_NOMATCH) + return 0; + + /* whaddaya know, it matched. get the number of back references */ + regmatch = xmalloc(sizeof(regmatch_t) * (sed_cmd->num_backrefs+1)); + + /* and now, as long as we've got a line to try matching and if we can match + * the search string, we make substitutions */ + while (*hackline && (regexec(sed_cmd->sub_match, hackline, + sed_cmd->num_backrefs+1, regmatch, 0) == 0) ) { + int i; + + /* print everything before the match */ + for (i = 0; i < regmatch[0].rm_so; i++) + fputc(hackline[i], stdout); + + /* then print the substitution string */ + print_subst_w_backrefs(hackline, sed_cmd->replace, regmatch); + + /* advance past the match */ + hackline += regmatch[0].rm_eo; + /* flag that something has changed */ + altered++; + + /* if we're not doing this globally, get out now */ + if (!sed_cmd->sub_g) + break; + } + + puts(hackline); + + /* cleanup */ + free(regmatch); + + return altered; +} + + +static void process_file(FILE *file) +{ + char *line = NULL; + static int linenum = 0; /* GNU sed does not restart counting lines at EOF */ + unsigned int still_in_range = 0; + int altered; + int i; + + /* go through every line in the file */ + while ((line = get_line_from_file(file)) != NULL) { + + chomp(line); + linenum++; + altered = 0; + + /* for every line, go through all the commands */ + for (i = 0; i < ncmds; i++) { + + + /* + * entry point into sedding... + */ + if ( + /* no range necessary */ + (sed_cmds[i].beg_line == 0 && sed_cmds[i].end_line == 0 && + sed_cmds[i].beg_match == NULL && + sed_cmds[i].end_match == NULL) || + /* this line number is the first address we're looking for */ + (sed_cmds[i].beg_line && (sed_cmds[i].beg_line == linenum)) || + /* this line matches our first address regex */ + (sed_cmds[i].beg_match && (regexec(sed_cmds[i].beg_match, line, 0, NULL, 0) == 0)) || + /* we are currently within the beginning & ending address range */ + still_in_range + ) { + + /* + * actual sedding + */ + switch (sed_cmds[i].cmd) { + + case 'p': + puts(line); + break; + + case 'd': + altered++; + break; + + case 's': + + /* + * Some special cases for 's' printing to make it compliant with + * GNU sed printing behavior (aka "The -n | s///p Matrix"): + * + * -n ONLY = never print anything regardless of any successful + * substitution + * + * s///p ONLY = always print successful substitutions, even if + * the line is going to be printed anyway (line will be printed + * twice). + * + * -n AND s///p = print ONLY a successful substitution ONE TIME; + * no other lines are printed - this is the reason why the 'p' + * flag exists in the first place. + */ + + /* if the user specified that they didn't want anything printed (i.e., a -n + * flag and no 'p' flag after the s///), then there's really no point doing + * anything here. */ + if (be_quiet && !sed_cmds[i].sub_p) + break; + + /* we print the line once, unless we were told to be quiet */ + if (!be_quiet) + altered |= do_subst_command(&sed_cmds[i], line); + + /* we also print the line if we were given the 'p' flag + * (this is quite possibly the second printing) */ + if (sed_cmds[i].sub_p) + altered |= do_subst_command(&sed_cmds[i], line); + + break; + + case 'a': + puts(line); + fputs(sed_cmds[i].editline, stdout); + altered++; + break; + + case 'i': + fputs(sed_cmds[i].editline, stdout); + break; + + case 'c': + /* single-address case */ + if (sed_cmds[i].end_match == NULL && sed_cmds[i].end_line == 0) { + fputs(sed_cmds[i].editline, stdout); + } + /* multi-address case */ + else { + /* matching text */ + if (sed_cmds[i].end_match && (regexec(sed_cmds[i].end_match, line, 0, NULL, 0) == 0)) + fputs(sed_cmds[i].editline, stdout); + /* matching line numbers */ + if (sed_cmds[i].end_line > 0 && sed_cmds[i].end_line == linenum) + fputs(sed_cmds[i].editline, stdout); + } + altered++; + + break; + + case 'r': { + FILE *outfile; + puts(line); + outfile = fopen(sed_cmds[i].filename, "r"); + if (outfile) + print_file(outfile); + /* else if we couldn't open the output file, + * no biggie, just don't print anything */ + altered++; + } + break; + } + + /* + * exit point from sedding... + */ + if ( + /* this is a single-address command or... */ + (sed_cmds[i].end_line == 0 && sed_cmds[i].end_match == NULL) || ( + /* we were in the middle of our address range (this + * isn't the first time through) and.. */ + (still_in_range == 1) && ( + /* this line number is the last address we're looking for or... */ + (sed_cmds[i].end_line && (sed_cmds[i].end_line == linenum)) || + /* this line matches our last address regex */ + (sed_cmds[i].end_match && (regexec(sed_cmds[i].end_match, line, 0, NULL, 0) == 0)) + ) + ) + ) { + /* we're out of our address range */ + still_in_range = 0; + } + + /* didn't hit the exit? then we're still in the middle of an address range */ + else { + still_in_range = 1; + } + } + } + + /* we will print the line unless we were told to be quiet or if the + * line was altered (via a 'd'elete or 's'ubstitution), in which case + * the altered line was already printed */ + if (!be_quiet && !altered) + puts(line); + + free(line); + } +} + +extern int sed_main(int argc, char **argv) +{ + int opt; + +#ifdef BB_FEATURE_CLEAN_UP + /* destroy command strings on exit */ + if (atexit(destroy_cmd_strs) == -1) + perror_msg_and_die("atexit"); +#endif + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "ne:f:")) > 0) { + switch (opt) { + case 'n': + be_quiet++; + break; + case 'e': + add_cmd_str(optarg); + break; + case 'f': + load_cmd_file(optarg); + break; + default: + 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 (ncmds == 0) { + if (argv[optind] == NULL) + show_usage(); + else { + add_cmd_str(argv[optind]); + optind++; + } + } + + + /* 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)) { + process_file(stdin); + } + else { + int i; + FILE *file; + for (i = optind; i < argc; i++) { + file = fopen(argv[i], "r"); + if (file == NULL) { + perror_msg("%s", argv[i]); + } else { + process_file(file); + fclose(file); + } + } + } + + return 0; +} diff --git a/busybox/editors/vi.c b/busybox/editors/vi.c new file mode 100644 index 000000000..8d7506d0f --- /dev/null +++ b/busybox/editors/vi.c @@ -0,0 +1,3947 @@ +/* vi: set sw=8 ts=8: */ +/* + * tiny vi.c: A small 'vi' clone + * Copyright (C) 2000, 2001 Sterling Huxley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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.15 2001/08/02 05:26:41 andersen Exp $"; + +/* + * To compile for standalone use: + * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c + * or + * gcc -Wall -Os -s -DSTANDALONE -DBB_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 immplement +#ifdef STANDALONE +#define vi_main main +#define BB_FEATURE_VI_COLON // 4288 +#define BB_FEATURE_VI_YANKMARK // 1408 +#define BB_FEATURE_VI_SEARCH // 1088 +#define BB_FEATURE_VI_USE_SIGNALS // 1056 +#define BB_FEATURE_VI_DOT_CMD // 576 +#define BB_FEATURE_VI_READONLY // 128 +#define BB_FEATURE_VI_SETOPTS // 576 +#define BB_FEATURE_VI_SET // 224 +#define BB_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 BB_FEATURE_VI_CRASHME // randomly pick commands to execute +#endif /* STANDALONE */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef STANDALONE +#include "busybox.h" +#endif /* STANDALONE */ + +#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 + +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 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 err_method; // indicate error with beep or flash +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 char erase_char; // the users erase character +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 char *SOs, *SOn; // terminal standout start/normal ESC sequence +static char *bell; // terminal bell sequence +static char *Ceol, *Ceos; // Clear-end-of-line and Clear-end-of-screen ESC sequence +static char *CMrc; // Cursor motion arbitrary destination ESC sequence +static char *CMup, *CMdown; // Cursor motion up and down ESC sequence +static Byte *status_buffer; // mesages to the user +static Byte last_input_char; // last char read from user +static Byte last_forward_char; // last char searched for with 'f' +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 + +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR +static int last_row; // where the cursor was last moved to +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ +#ifdef BB_FEATURE_VI_USE_SIGNALS +static jmp_buf restart; // catch_sig() +#endif /* BB_FEATURE_VI_USE_SIGNALS */ +#ifdef BB_FEATURE_VI_WIN_RESIZE +static struct winsize winsize; // remember the window size +#endif /* BB_FEATURE_VI_WIN_RESIZE */ +#ifdef BB_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 /* BB_FEATURE_VI_DOT_CMD */ +#if defined(BB_FEATURE_VI_DOT_CMD) || defined(BB_FEATURE_VI_YANKMARK) +static Byte *modifying_cmds; // cmds that modify text[] +#endif /* BB_FEATURE_VI_DOT_CMD || BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_READONLY +static int vi_readonly, readonly; +#endif /* BB_FEATURE_VI_READONLY */ +#ifdef BB_FEATURE_VI_SETOPTS +static int autoindent; +static int showmatch; +static int ignorecase; +#endif /* BB_FEATURE_VI_SETOPTS */ +#ifdef BB_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 /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_SEARCH +static Byte *last_search_pattern; // last pattern from a '/' or '?' search +#endif /* BB_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 *dollar_line(Byte *); // return pointer to just before NL +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 print_literal(Byte *, Byte *); // copy s to buf, convert unprintable +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(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(); +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 beep(void); // beep the terminal +static void indicate_error(char); // use flash or beep to indicate error +static void show_status_line(void); // put a message on the bottom line +static void psb(char *, ...); // Print Status Buf +static void psbs(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[] + +#ifdef BB_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 /* BB_FEATURE_VI_SEARCH */ +#ifdef BB_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 /* BB_FEATURE_VI_COLON */ +static Byte *get_input_line(Byte *); // get input line- use "status line" +#ifdef BB_FEATURE_VI_USE_SIGNALS +static void winch_sig(int); // catch window size changes +static void suspend_sig(int); // catch ctrl-Z +static void alarm_sig(int); // catch alarm time-outs +static void catch_sig(int); // catch ctrl-C +static void core_sig(int); // catch a core dump signal +#endif /* BB_FEATURE_VI_USE_SIGNALS */ +#ifdef BB_FEATURE_VI_DOT_CMD +static void start_new_cmd_q(Byte); // new queue for command +static void end_cmd_q(); // stop saving input chars +#else /* BB_FEATURE_VI_DOT_CMD */ +#define end_cmd_q() +#endif /* BB_FEATURE_VI_DOT_CMD */ +#ifdef BB_FEATURE_VI_WIN_RESIZE +static void window_size_get(int); // find out what size the window is +#endif /* BB_FEATURE_VI_WIN_RESIZE */ +#ifdef BB_FEATURE_VI_SETOPTS +static void showmatching(Byte *); // show the matching pair () [] {} +#endif /* BB_FEATURE_VI_SETOPTS */ +#if defined(BB_FEATURE_VI_YANKMARK) || defined(BB_FEATURE_VI_COLON) || defined(BB_FEATURE_VI_CRASHME) +static Byte *string_insert(Byte *, Byte *); // insert the string at 'p' +#endif /* BB_FEATURE_VI_YANKMARK || BB_FEATURE_VI_COLON || BB_FEATURE_VI_CRASHME */ +#ifdef BB_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 +static Byte *swap_context(Byte *); // goto new context for '' command +#endif /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_CRASHME +static void crash_dummy(); +static void crash_test(); +static int crashme = 0; +#endif /* BB_FEATURE_VI_CRASHME */ + + +extern int vi_main(int argc, char **argv) +{ + int c; + +#ifdef BB_FEATURE_VI_YANKMARK + int i; +#endif /* BB_FEATURE_VI_YANKMARK */ + + CMrc= "\033[%d;%dH"; // Terminal Crusor motion ESC sequence + CMup= "\033[A"; // move cursor up one line, same col + CMdown="\n"; // move cursor down one line, same col + Ceol= "\033[0K"; // Clear from cursor to end of line + Ceos= "\033[0J"; // Clear from cursor to end of screen + SOs = "\033[7m"; // Terminal standout mode on + SOn = "\033[0m"; // Terminal standout mode off + bell= "\007"; // Terminal bell sequence +#ifdef BB_FEATURE_VI_CRASHME + (void) srand((long) getpid()); +#endif /* BB_FEATURE_VI_CRASHME */ + status_buffer = (Byte *) malloc(200); // hold messages to user +#ifdef BB_FEATURE_VI_READONLY + vi_readonly = readonly = FALSE; + if (strncmp(argv[0], "view", 4) == 0) { + readonly = TRUE; + vi_readonly = TRUE; + } +#endif /* BB_FEATURE_VI_READONLY */ +#ifdef BB_FEATURE_VI_SETOPTS + autoindent = 1; + ignorecase = 1; + showmatch = 1; +#endif /* BB_FEATURE_VI_SETOPTS */ +#ifdef BB_FEATURE_VI_YANKMARK + for (i = 0; i < 28; i++) { + reg[i] = 0; + } // init the yank regs +#endif /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_DOT_CMD + modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[] +#endif /* BB_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 BB_FEATURE_VI_CRASHME + case 'C': + crashme = 1; + break; +#endif /* BB_FEATURE_VI_CRASHME */ +#ifdef BB_FEATURE_VI_READONLY + case 'R': // Read-only flag + readonly = TRUE; + break; +#endif /* BB_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 + if (cfn != 0) + free(cfn); + cfn = (Byte *) strdup(argv[optind]); + edit_file(cfn); + } + } + //----------------------------------------------------------- + + return (0); +} + +static void edit_file(Byte * fn) +{ + char c; + int cnt, size, ch; + +#ifdef BB_FEATURE_VI_USE_SIGNALS + char *msg; + int sig; +#endif /* BB_FEATURE_VI_USE_SIGNALS */ +#ifdef BB_FEATURE_VI_YANKMARK + static Byte *cur_line; +#endif /* BB_FEATURE_VI_YANKMARK */ + + rawmode(); + rows = 24; + columns = 80; + ch= -1; +#ifdef BB_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* BB_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 BB_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 /* BB_FEATURE_VI_YANKMARK */ + + err_method = 1; // flash + last_forward_char = last_input_char = '\0'; + crow = 0; + ccol = 0; + edit_status(); + +#ifdef BB_FEATURE_VI_USE_SIGNALS + signal(SIGHUP, catch_sig); + signal(SIGINT, catch_sig); + signal(SIGALRM, alarm_sig); + signal(SIGTERM, catch_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 + signal(SIGWINCH, winch_sig); + signal(SIGTSTP, suspend_sig); + sig = setjmp(restart); + if (sig != 0) { + 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 /* BB_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 BB_FEATURE_VI_DOT_CMD + if (last_modifying_cmd != 0) + free(last_modifying_cmd); + if (ioq_start != NULL) + free(ioq_start); + ioq = ioq_start = last_modifying_cmd = 0; + adding2q = 0; +#endif /* BB_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 BB_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 /* BB_FEATURE_VI_CRASHME */ + last_input_char = c = get_one_char(); // get a cmd from user +#ifdef BB_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 /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_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 /* BB_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 BB_FEATURE_VI_CRASHME + if (crashme > 0) + crash_test(); // test editor variables +#endif /* BB_FEATURE_VI_CRASHME */ + } + //------------------------------------------------------------------- + + place_cursor(rows, 0, FALSE); // go to bottom of screen + clear_to_eol(); // Erase to end of line + cookmode(); +} + +static Byte readbuffer[BUFSIZ]; + +#ifdef BB_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 (strlen((char *) readbuffer) > 0) + goto cd1; + cd0: + startrbi = rbi = 0; + sleeptime = 0; // how long to pause between commands + memset(readbuffer, '\0', BUFSIZ - 1); // 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"); + } + 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], buf[BUFSIZ], msg[BUFSIZ]; + + msg[0] = '\0'; + if (end < text) { + strcat((char *) msg, "end textend) { + strcat((char *) msg, "end>textend "); + } + if (dot < text) { + strcat((char *) msg, "dot end) { + strcat((char *) msg, "dot>end "); + } + if (screenbegin < text) { + strcat((char *) msg, "screenbegin end - 1) { + strcat((char *) msg, "screenbegin>end-1 "); + } + + if (strlen(msg) > 0) { + alarm(0); + sprintf(buf, "\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s", + totalcmds, last_input_char, msg, SOs, SOn); + write(1, buf, strlen(buf)); + 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 /* BB_FEATURE_VI_CRASHME */ + +//--------------------------------------------------------------------- +//----- 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 (cmd_mode == 2) { + // 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 && c <= 127) { // only ASCII chars + 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 && c <= 127) { + dot = char_insert(dot, c); // only ASCII chars + } + goto dc1; + } + + 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 BB_FEATURE_VI_CRASHME + case 0x14: // dc4 ctrl-T + crashme = (crashme == 0) ? 1 : 0; + break; +#endif /* BB_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 BB_FEATURE_VI_USE_SIGNALS + case 0x03: // ctrl-C interrupt + longjmp(restart, 1); + break; + case 26: // ctrl-Z suspend + suspend_sig(SIGTSTP); + break; +#endif /* BB_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 BB_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 /* BB_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 + 1); + 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 seperate 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 BB_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 *) strdup((char *) last_modifying_cmd); + } + break; +#endif /* BB_FEATURE_VI_DOT_CMD */ +#ifdef BB_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 + if (last_search_pattern != 0) { + free(last_search_pattern); + } + last_search_pattern = (Byte *) strdup((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 /* BB_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 BB_FEATURE_VI_COLON + colon(p); // execute the command +#else /* BB_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 == TRUE && 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) { + cnt = file_write(cfn, text, end - 1); + file_modified = FALSE; + psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt); + if (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 /* BB_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 BB_FEATURE_VI_DOT_CMD + if (c == 'D') + end_cmd_q(); // stop adding to q +#endif /* BB_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 == TRUE +#ifdef BB_FEATURE_VI_READONLY + && vi_readonly == FALSE + && readonly == FALSE +#endif /* BB_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 BB_FEATURE_VI_YANKMARK + case 'y': // y- yank something + case 'Y': // Y- Yank a line +#endif /* BB_FEATURE_VI_YANKMARK */ + yf = YANKDEL; // assume either "c" or "d" +#ifdef BB_FEATURE_VI_YANKMARK + if (c == 'y' || c == 'Y') + yf = YANKONLY; +#endif /* BB_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 BB_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 /* BB_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 BB_FEATURE_VI_YANKMARK + check_context(c); // update the current context +#endif /* BB_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--; +} + +//----- The Colon commands ------------------------------------- +#ifdef BB_FEATURE_VI_COLON +static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present +{ + int st; + Byte *q; + +#ifdef BB_FEATURE_VI_YANKMARK + Byte c; +#endif /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_SEARCH + Byte *pat, buf[BUFSIZ]; +#endif /* BB_FEATURE_VI_SEARCH */ + + *addr = -1; // assume no addr + if (*p == '.') { // the current line + p++; + q = begin_line(dot); + *addr = count_lines(text, q); +#ifdef BB_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 /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_SEARCH + } else if (*p == '/') { // a search pattern + q = buf; + for (p++; *p; p++) { + if (*p == '/') + break; + *q++ = *p; + *q = '\0'; + } + pat = (Byte *) strdup((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 /* BB_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 seperator + 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); +} + +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, forced; + 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" + // :! // run then return + // + if (strlen((char *) buf) <= 0) + goto vc1; + if (*buf == ':') + buf++; // move past the ':' + + forced = useforce = FALSE; + 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 + (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 == TRUE && useforce != TRUE) { + 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 *) strdup((char *) fn); // save the cfn + if (cfn != 0) + 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 BB_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 /* BB_FEATURE_VI_YANKMARK */ + // how many lines in text[]? + li = count_lines(text, end - 1); + psb("\"%s\"%s" +#ifdef BB_FEATURE_VI_READONLY + "%s" +#endif /* BB_FEATURE_VI_READONLY */ + " %dL, %dC", cfn, + (sr < 0 ? " [New file]" : ""), +#ifdef BB_FEATURE_VI_READONLY + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), +#endif /* BB_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 + if (cfn != NULL) + free(cfn); + cfn = (Byte *) strdup((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 + write(1, "\r\n", 2); + for (; q <= r; q++) { + c = *q; + if (c > '~') + standout_start(); + if (c == '\n') { + write(1, "$\r", 2); + } else if (*q < ' ') { + write(1, "^", 1); + c += '@'; + } + write(1, &c, 1); + if (c > '~') + standout_end(); + } +#ifdef BB_FEATURE_VI_SET + vc2: +#endif /* BB_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 == TRUE) { + // 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 == TRUE) { + 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 BB_FEATURE_VI_READONLY + l= readonly; // remember current files' status +#endif + ch = file_insert(fn, q, file_size(fn)); +#ifdef BB_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 BB_FEATURE_VI_READONLY + "%s" +#endif /* BB_FEATURE_VI_READONLY */ + " %dL, %dC", fn, +#ifdef BB_FEATURE_VI_READONLY + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), +#endif /* BB_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 == TRUE && useforce != TRUE) { + psbs("No write since last change (:rewind! overrides)"); + } else { + // reset the filenames to edit + optind = fn_start - 1; + editing = 0; + } +#ifdef BB_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 BB_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 /* BB_FEATURE_VI_SETOPTS */ + printf("\r\n"); + goto vc2; + } + if (strncasecmp((char *) args, "no", 2) == 0) + i = 2; // ":set noautoindent" +#ifdef BB_FEATURE_VI_SETOPTS + if (strncasecmp((char *) args + i, "autoindent", 10) == 0 || + strncasecmp((char *) args + i, "ai", 2) == 0) { + autoindent = (i == 2) ? 0 : 1; + } + if (strncasecmp((char *) args + i, "flash", 5) == 0 || + strncasecmp((char *) args + i, "fl", 2) == 0) { + err_method = (i == 2) ? 0 : 1; + } + if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 || + strncasecmp((char *) args + i, "ic", 2) == 0) { + ignorecase = (i == 2) ? 0 : 1; + } + if (strncasecmp((char *) args + i, "showmatch", 9) == 0 || + strncasecmp((char *) args + i, "sm", 2) == 0) { + showmatch = (i == 2) ? 0 : 1; + } + if (strncasecmp((char *) args + i, "tabstop", 7) == 0) { + sscanf(strchr((char *) args + i, '='), "=%d", &ch); + if (ch > 0 && ch < columns - 1) + tabstop = ch; + } +#endif /* BB_FEATURE_VI_SETOPTS */ +#endif /* BB_FEATURE_VI_SET */ +#ifdef BB_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 /* BB_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)) { // write text to file + // is there a file name to write to? + if (strlen((char *) args) > 0) { + fn = args; + } +#ifdef BB_FEATURE_VI_READONLY + if ((vi_readonly == TRUE || readonly == TRUE) && useforce == FALSE) { + psbs("\"%s\" File is read only", fn); + goto vc3; + } +#endif /* BB_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 == TRUE) { + // 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 == TRUE && forced == TRUE) { + // 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[1] == 'q' && l == ch) { + editing = 0; + } +#ifdef BB_FEATURE_VI_READONLY + vc3:; +#endif /* BB_FEATURE_VI_READONLY */ +#ifdef BB_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 /* BB_FEATURE_VI_YANKMARK */ + } else { + // cmd unknown + ni((Byte *) cmd); + } + vc1: + dot = bound_dot(dot); // make sure "dot" is valid + return; +#ifdef BB_FEATURE_VI_SEARCH +colon_s_fail: + psb(":s expression missing delimiters"); + return; +#endif + +} + +static void Hit_Return(void) +{ + char c; + + standout_start(); // start reverse video + write(1, "[Hit return to continue]", 24); + standout_end(); // end reverse video + while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */ + ; + redraw(TRUE); // force redraw all +} +#endif /* BB_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 < ' ') { + 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 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 < ' ') { + 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; + + if (screen != 0) + free(screen); + screensize = ro * co + 8; + screen = (Byte *) malloc(screensize); + // initialize the new screen. assume this will be a empty file. + screen_erase(); + // non-existant 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 + if (text != 0) { + //text -= 4; + free(text); + } + text = (Byte *) malloc(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 BB_FEATURE_VI_SEARCH +static int mycmp(Byte * s1, Byte * s2, int len) +{ + int i; + + i = strncmp((char *) s1, (char *) s2, len); +#ifdef BB_FEATURE_VI_SETOPTS + if (ignorecase) { + i = strncasecmp((char *) s1, (char *) s2, len); + } +#endif /* BB_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 /* BB_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 BB_FEATURE_VI_DOT_CMD + // also rmove char from last_modifying_cmd + if (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 /* BB_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 BB_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 /* BB_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 (dot > text) + 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 BB_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 /* BB_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 BB_FEATURE_VI_YANKMARK + text_yank(start, stop, YDreg); +#endif /* BB_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 BB_FEATURE_VI_SEARCH + "\n\tPattern searches with / and ?" +#endif /* BB_FEATURE_VI_SEARCH */ +#ifdef BB_FEATURE_VI_DOT_CMD + "\n\tLast command repeat with \'.\'" +#endif /* BB_FEATURE_VI_DOT_CMD */ +#ifdef BB_FEATURE_VI_YANKMARK + "\n\tLine marking with 'x" + "\n\tNamed buffers with \"x" +#endif /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_READONLY + "\n\tReadonly if vi is called as \"view\"" + "\n\tReadonly with -R command line arg" +#endif /* BB_FEATURE_VI_READONLY */ +#ifdef BB_FEATURE_VI_SET + "\n\tSome colon mode commands with \':\'" +#endif /* BB_FEATURE_VI_SET */ +#ifdef BB_FEATURE_VI_SETOPTS + "\n\tSettable options with \":set\"" +#endif /* BB_FEATURE_VI_SETOPTS */ +#ifdef BB_FEATURE_VI_USE_SIGNALS + "\n\tSignal catching- ^C" + "\n\tJob suspend and resume with ^Z" +#endif /* BB_FEATURE_VI_USE_SIGNALS */ +#ifdef BB_FEATURE_VI_WIN_RESIZE + "\n\tAdapt to window re-sizes" +#endif /* BB_FEATURE_VI_WIN_RESIZE */ + ); +} + +static 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++) { + c = *s; + if (*s > '~') { + strcat((char *) buf, SOs); + c = *s - 128; + } + if (*s < ' ') { + strcat((char *) buf, "^"); + c += '@'; + } + b[0] = c; + strcat((char *) buf, (char *) b); + if (*s > '~') + strcat((char *) buf, SOn); + if (*s == '\n') { + strcat((char *) buf, "$"); + } + } +} + +#ifdef BB_FEATURE_VI_DOT_CMD +static void start_new_cmd_q(Byte c) +{ + // release old cmd + if (last_modifying_cmd != 0) + free(last_modifying_cmd); + // get buffer for new cmd + last_modifying_cmd = (Byte *) malloc(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() +{ +#ifdef BB_FEATURE_VI_YANKMARK + YDreg = 26; // go back to default Yank/Delete reg +#endif /* BB_FEATURE_VI_YANKMARK */ + adding2q = 0; + return; +} +#endif /* BB_FEATURE_VI_DOT_CMD */ + +#if defined(BB_FEATURE_VI_YANKMARK) || defined(BB_FEATURE_VI_COLON) || defined(BB_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 BB_FEATURE_VI_YANKMARK + psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg()); +#endif /* BB_FEATURE_VI_YANKMARK */ + return (p); +} +#endif /* BB_FEATURE_VI_YANKMARK || BB_FEATURE_VI_COLON || BB_FEATURE_VI_CRASHME */ + +#ifdef BB_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]; + if (t != 0) { // if already a yank register + free(t); // free it + } + t = (Byte *) malloc(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 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 /* BB_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); +#ifndef linux + term_vi.c_cc[VMIN] = 1; + term_vi.c_cc[VTIME] = 0; +#endif + erase_char = term_vi.c_cc[VERASE]; + tcsetattr(0, TCSANOW, &term_vi); +} + +static void cookmode(void) +{ + tcsetattr(0, TCSANOW, &term_orig); +} + +#ifdef BB_FEATURE_VI_WIN_RESIZE +//----- See what the window size currently is -------------------- +static void window_size_get(int sig) +{ + int i; + + i = ioctl(0, TIOCGWINSZ, &winsize); + if (i != 0) { + // force 24x80 + winsize.ws_row = 24; + winsize.ws_col = 80; + } + if (winsize.ws_row <= 1) { + winsize.ws_row = 24; + } + if (winsize.ws_col <= 1) { + winsize.ws_col = 80; + } + rows = (int) winsize.ws_row; + columns = (int) winsize.ws_col; +} +#endif /* BB_FEATURE_VI_WIN_RESIZE */ + +//----- Come here when we get a window resize signal --------- +#ifdef BB_FEATURE_VI_USE_SIGNALS +static void winch_sig(int sig) +{ + signal(SIGWINCH, winch_sig); +#ifdef BB_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* BB_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(getpid(), 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(getpid(), 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); + longjmp(restart, sig); +} + +static void alarm_sig(int sig) +{ + signal(SIGALRM, catch_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 + + dot = bound_dot(dot); // make sure "dot" is valid + + longjmp(restart, sig); +} +#endif /* BB_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 + 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)); +} + +//----- IO Routines -------------------------------------------- +static Byte readit(void) // read (maybe cursor) key from stdin +{ + Byte c; + int i, bufsiz, cnt, cmdindex; + struct esc_cmds { + Byte *seq; + Byte val; + }; + + static struct esc_cmds esccmds[] = { + {(Byte *) "OA", (Byte) VI_K_UP}, // cursor key Up + {(Byte *) "OB", (Byte) VI_K_DOWN}, // cursor key Down + {(Byte *) "OC", (Byte) VI_K_RIGHT}, // Cursor Key Right + {(Byte *) "OD", (Byte) VI_K_LEFT}, // cursor key Left + {(Byte *) "OH", (Byte) VI_K_HOME}, // Cursor Key Home + {(Byte *) "OF", (Byte) VI_K_END}, // Cursor Key End + {(Byte *) "", (Byte) VI_K_UP}, // cursor key Up + {(Byte *) "", (Byte) VI_K_DOWN}, // cursor key Down + {(Byte *) "", (Byte) VI_K_RIGHT}, // Cursor Key Right + {(Byte *) "", (Byte) VI_K_LEFT}, // cursor key Left + {(Byte *) "", (Byte) VI_K_HOME}, // Cursor Key Home + {(Byte *) "", (Byte) VI_K_END}, // Cursor Key End + {(Byte *) "[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert + {(Byte *) "[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up + {(Byte *) "[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down + {(Byte *) "OP", (Byte) VI_K_FUN1}, // Function Key F1 + {(Byte *) "OQ", (Byte) VI_K_FUN2}, // Function Key F2 + {(Byte *) "OR", (Byte) VI_K_FUN3}, // Function Key F3 + {(Byte *) "OS", (Byte) VI_K_FUN4}, // Function Key F4 + {(Byte *) "[15~", (Byte) VI_K_FUN5}, // Function Key F5 + {(Byte *) "[17~", (Byte) VI_K_FUN6}, // Function Key F6 + {(Byte *) "[18~", (Byte) VI_K_FUN7}, // Function Key F7 + {(Byte *) "[19~", (Byte) VI_K_FUN8}, // Function Key F8 + {(Byte *) "[20~", (Byte) VI_K_FUN9}, // Function Key F9 + {(Byte *) "[21~", (Byte) VI_K_FUN10}, // Function Key F10 + {(Byte *) "[23~", (Byte) VI_K_FUN11}, // Function Key F11 + {(Byte *) "[24~", (Byte) VI_K_FUN12}, // Function Key F12 + {(Byte *) "[11~", (Byte) VI_K_FUN1}, // Function Key F1 + {(Byte *) "[12~", (Byte) VI_K_FUN2}, // Function Key F2 + {(Byte *) "[13~", (Byte) VI_K_FUN3}, // Function Key F3 + {(Byte *) "[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 + // get input from User- are there already input chars in Q? + bufsiz = strlen((char *) readbuffer); + if (bufsiz <= 0) { + ri0: + // the Q is empty, wait for a typed char + bufsiz = read(0, readbuffer, BUFSIZ - 1); + if (bufsiz < 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; + bufsiz = 0; + } + readbuffer[bufsiz] = '\0'; + } + // return char if it is not part of ESC sequence + if (readbuffer[0] != 27) + goto ri1; + + // 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 && bufsiz <= (BUFSIZ - 5)) { + // read the rest of the ESC string + i = read(0, (void *) (readbuffer + bufsiz), BUFSIZ - bufsiz); + if (i > 0) { + bufsiz += i; + readbuffer[bufsiz] = '\0'; // Terminate the string + } + } + // Maybe cursor or function key? + for (cmdindex = 0; cmdindex < ESCCMDS_COUNT; cmdindex++) { + cnt = strlen((char *) esccmds[cmdindex].seq); + i = strncmp((char *) esccmds[cmdindex].seq, (char *) readbuffer, cnt); + if (i == 0) { + // is a Cursor key- put derived value back into Q + readbuffer[0] = esccmds[cmdindex].val; + // squeeze out the ESC sequence + for (i = 1; i < cnt; i++) { + memmove(readbuffer + 1, readbuffer + 2, BUFSIZ - 2); + readbuffer[BUFSIZ - 1] = '\0'; + } + break; + } + } + ri1: + c = readbuffer[0]; + // remove one char from Q + memmove(readbuffer, readbuffer + 1, BUFSIZ - 1); + readbuffer[BUFSIZ - 1] = '\0'; + (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 BB_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) { + // add new char to q + last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c; + } + } +#else /* BB_FEATURE_VI_DOT_CMD */ + c = readit(); // get the users input +#endif /* BB_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 + write(1, prompt, strlen((char *) 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 + write(1, " ", 3); // 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 + write(1, buf + i, 1); // echo the char back to user + i++; + } + } + refresh(FALSE); + if (obufp != NULL) + free(obufp); + obufp = (Byte *) strdup((char *) buf); + return (obufp); +} + +static int file_size(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 BB_FEATURE_VI_READONLY + readonly = FALSE; +#endif /* BB_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 BB_FEATURE_VI_READONLY + if (vi_readonly == TRUE) 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 BB_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 BB_FEATURE_VI_READONLY + // got the file- read-only + readonly = TRUE; +#endif /* BB_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. +// classicly 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; + int l; +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + char cm2[BUFSIZ]; + Byte *screenp; + // char cm3[BUFSIZ]; + int Rrow= last_row; +#endif /* BB_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 == FALSE) goto pc0; + +#ifdef BB_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 /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + pc0: + l= strlen(cm); + if (l) write(1, cm, l); // move the cursor +} + +//----- Erase from cursor to end of line ----------------------- +static void clear_to_eol() +{ + write(1, Ceol, strlen(Ceol)); // Erase from cursor to end of line +} + +//----- Erase from cursor to end of screen ----------------------- +static void clear_to_eos() +{ + write(1, Ceos, strlen(Ceos)); // Erase from cursor to end of screen +} + +//----- Start standout mode ------------------------------------ +static void standout_start() // send "start reverse video" sequence +{ + write(1, SOs, strlen(SOs)); // Start reverse video mode +} + +//----- End standout mode -------------------------------------- +static void standout_end() // send "end reverse video" sequence +{ + write(1, SOn, strlen(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 beep() +{ + write(1, bell, strlen(bell)); // send out a bell character +} + +static void indicate_error(char c) +{ +#ifdef BB_FEATURE_VI_CRASHME + if (crashme > 0) + return; // generate a random command +#endif /* BB_FEATURE_VI_CRASHME */ + if (err_method == 0) { + beep(); + } 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; + + 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 + write(1, status_buffer, cnt); + clear_to_eol(); + place_cursor(crow, ccol, FALSE); // put cursor back in correct place + } +} + +//----- format the status buffer, the bottom line of screen ------ +// print status buffer, with STANDOUT mode +static void psbs(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(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 BB_FEATURE_VI_READONLY + "%s" +#endif /* BB_FEATURE_VI_READONLY */ + "%s line %d of %d --%d%%--", + (cfn != 0 ? (char *) cfn : "No file"), +#ifdef BB_FEATURE_VI_READONLY + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), +#endif /* BB_FEATURE_VI_READONLY */ + (file_modified == TRUE ? " [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 < ' ' || c > '~') { + if (c == '\t') { + c = ' '; + // co % 8 != 7 + for (; (co % tabstop) != (tabstop - 1); co++) { + dest[co] = c; + } + } else { + dest[co++] = '^'; + c |= '@'; // make it visible + c &= 0x7f; // get rid of hi bit + } + } + // 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 BB_FEATURE_VI_OPTIMIZE_CURSOR + int last_li= -2; // last line that changed- for optimizing cursor movement +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + +#ifdef BB_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* BB_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 == TRUE) { + // 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 == TRUE) { + // 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 BB_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 /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + place_cursor(li, cs, FALSE); // use standard ESC sequence +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + } + + // write line out to terminal + write(1, sp+cs, ce-cs+1); +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + last_row = li; +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + } + } + +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE); + last_row = crow; +#else + place_cursor(crow, ccol, FALSE); +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + + if (offset != old_offset) + old_offset = offset; +} diff --git a/busybox/env.c b/busybox/env.c new file mode 100644 index 000000000..8bb690b72 --- /dev/null +++ b/busybox/env.c @@ -0,0 +1,106 @@ +/* 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 , + */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int env_main(int argc, char** argv) +{ + char **ep, *p; + char *cleanenv[1]; + int ignore_environment = 0; + int ch; + + while ((ch = getopt(argc, argv, "+iu:")) != -1) { + switch(ch) { + case 'i': + ignore_environment = 1; + break; + case 'u': + unsetenv(optarg); + break; + default: + show_usage(); + } + } + if (optind != argc && !strcmp(argv[optind], "-")) { + ignore_environment = 1; + argv++; + } + if (ignore_environment) { + environ = cleanenv; + cleanenv[0] = NULL; + } + for (argv += optind; *argv && (p = strchr(*argv, '=')); ++argv) + if (putenv(*argv) < 0) + perror_msg_and_die("%s", *argv); + if (*argv) { + execvp(*argv, argv); + perror_msg_and_die("%s", *argv); + } + for (ep = environ; *ep; ep++) + puts(*ep); + return 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. + * + * 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/examples/bootfloppy/bootfloppy.txt b/busybox/examples/bootfloppy/bootfloppy.txt new file mode 100644 index 000000000..575c93fcc --- /dev/null +++ b/busybox/examples/bootfloppy/bootfloppy.txt @@ -0,0 +1,185 @@ +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.lineo.com) + - 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. + + - Run ldconfig so busybox and other binaries can have access to the libraries + that they need: + + (path to)uClibc/ld.so-1/util/ldconfig -r loop/ + + - 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 index 000000000..399d326d9 --- /dev/null +++ b/busybox/examples/bootfloppy/display.txt @@ -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 index 000000000..ef14ca2cc --- /dev/null +++ b/busybox/examples/bootfloppy/etc/fstab @@ -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 index 000000000..4f29b923f --- /dev/null +++ b/busybox/examples/bootfloppy/etc/init.d/rcS @@ -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 index 000000000..eb3e979ce --- /dev/null +++ b/busybox/examples/bootfloppy/etc/inittab @@ -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 index 000000000..e9b11e90a --- /dev/null +++ b/busybox/examples/bootfloppy/etc/profile @@ -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 index 000000000..03a1a8550 --- /dev/null +++ b/busybox/examples/bootfloppy/mkdevs.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# +# makedev.sh - creates device files for a busybox boot floppy image + + +# we do our work in the dev/ directory +if [ -z "$1" ]; then + echo "usage: `basename $0` path/to/dev/dir" + exit 1 +fi + +cd $1 + + +# miscellaneous one-of-a-kind stuff +mknod console c 5 1 +mknod full c 1 7 +mknod kmem c 1 2 +mknod mem c 1 1 +mknod null c 1 3 +mknod port c 1 4 +mknod random c 1 8 +mknod urandom c 1 9 +mknod zero c 1 5 +ln -s /proc/kcore core + +# IDE HD devs +# note: not going to bother creating all concievable partitions; you can do +# that yourself as you need 'em. +mknod hda b 3 0 +mknod hdb b 3 64 +mknod hdc b 22 0 +mknod hdd b 22 64 + +# loop devs +for i in `seq 0 7`; do + mknod loop$i b 7 $i +done + +# ram devs +for i in `seq 0 9`; do + mknod ram$i b 1 $i +done +ln -s ram1 ram + +# ttys +mknod tty c 5 0 +for i in `seq 0 9`; do + mknod tty$i c 4 $i +done + +# virtual console screen devs +for i in `seq 0 9`; do + mknod vcs$i b 7 $i +done +ln -s vcs0 vcs + +# virtual console screen w/ attributes devs +for i in `seq 0 9`; do + mknod vcsa$i b 7 $i +done +ln -s vcsa0 vcsa diff --git a/busybox/examples/bootfloppy/mkrootfs.sh b/busybox/examples/bootfloppy/mkrootfs.sh new file mode 100755 index 000000000..b59b57af0 --- /dev/null +++ b/busybox/examples/bootfloppy/mkrootfs.sh @@ -0,0 +1,106 @@ +#!/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 +$UCLIBC_DIR/ld.so-1/util/ldconfig -r $TARGET_DIR + + +# 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 index 000000000..e25417353 --- /dev/null +++ b/busybox/examples/bootfloppy/mksyslinux.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Formats a floppy to use Syslinux + +dummy="" + + +# need to have mtools installed +if [ -z `which mformat` -o -z `which mcopy` ]; then + echo "You must have the mtools package installed to run this script" + exit 1 +fi + + +# need an arg for the location of the kernel +if [ -z "$1" ]; then + echo "usage: `basename $0` path/to/linux/kernel" + exit 1 +fi + + +# need to have a root file system built +if [ ! -f rootfs.gz ]; then + echo "You need to have a rootfs built first." + echo "Hit RETURN to make one now or Control-C to quit." + read dummy + ./mkrootfs.sh +fi + + +# prepare the floppy +echo "Please insert a blank floppy in the drive and press RETURN to format" +echo "(WARNING: All data will be erased! Hit Control-C to abort)" +read dummy + +echo "Formatting the floppy..." +mformat a: +echo "Making it bootable with Syslinux..." +syslinux -s /dev/fd0 +echo "Copying Syslinux configuration files..." +mcopy syslinux.cfg display.txt a: +echo "Copying root filesystem file..." +mcopy rootfs.gz a: +# XXX: maybe check for "no space on device" errors here +echo "Copying linux kernel..." +mcopy $1 a:linux +# XXX: maybe check for "no space on device" errors here too +echo "Finished: boot floppy created" diff --git a/busybox/examples/bootfloppy/quickstart.txt b/busybox/examples/bootfloppy/quickstart.txt new file mode 100644 index 000000000..0d8090824 --- /dev/null +++ b/busybox/examples/bootfloppy/quickstart.txt @@ -0,0 +1,15 @@ +Quickstart on making the Busybox boot-floppy: + + 1) Download Busybox and uClibc from CVS or tarballs. Make sure they share a + common parent directory. (i.e. busybox/ and uclibc/ are both right off of + /tmp, or wherever.) + + 2) Build a Linux kernel. Make sure you include support for initrd. + + 3) Put a floppy in the drive. Make sure it is a floppy you don't care about + because the contents will be overwritten. + + 4) As root, type ./mksyslinux.sh path/to/linux/kernel from this directory. + Wait patiently while the magic happens. + + 5) Boot up on the floppy. diff --git a/busybox/examples/bootfloppy/syslinux.cfg b/busybox/examples/bootfloppy/syslinux.cfg new file mode 100644 index 000000000..8d407cad4 --- /dev/null +++ b/busybox/examples/bootfloppy/syslinux.cfg @@ -0,0 +1,7 @@ +display display.txt +default linux +timeout 10 +prompt 1 +label linux + kernel linux + append initrd=rootfs.gz root=/dev/ram0 diff --git a/busybox/examples/busybox.spec b/busybox/examples/busybox.spec new file mode 100644 index 000000000..339311770 --- /dev/null +++ b/busybox/examples/busybox.spec @@ -0,0 +1,44 @@ +%define name busybox +%define epoch 0 +%define version 0.60.0 +%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.lineo.com/ +Source: ftp://oss.lineo.com/busybox/%{name}-%{version}.tar.gz +Buildroot: /var/tmp/%{name}-%{version} +Packager : Erik Andersen + +%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 index 000000000..e65f07b68 --- /dev/null +++ b/busybox/examples/depmod.pl @@ -0,0 +1,227 @@ +#!/usr/bin/perl -w +# vi: set ts=4: +# Copyright (c) 2001 David Schleef +# Copyright (c) 2001 Erik Andersen +# Copyright (c) 2001 Stuart Hughes +# 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=1; +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", + "\t-h --help\t\tShow this help screen\n", + "\t-b --basedir\t\tModules base directory (defaults to /lib/modules)\n", + "\t-k --kernel\t\tKernel binary for the target\n", + "\t-F --kernelsyms\t\tKernel symbol file\n", + "\t-n --stdout\t\tWrite to stdout instead of modules.dep\n", + "\t-v --verbose\t\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($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 +foreach $module ( keys %$mod ) { + print "$module:\t"; + @sorted = sort bydep keys %{$mod->{$module}}; + print join(" \\\n\t",@sorted); +# foreach $m (@sorted ) { +# print "\t$m\n"; +# } + print "\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]... [FILE]... + +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 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 +Copyright (c) 2001 Erik Andersen +Copyright (c) 2001 Stuart Hughes +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + +=head1 AUTHOR + +David Schleef + +=cut + +# $Id: depmod.pl,v 1.1 2001/07/30 19:32:03 andersen Exp $ + diff --git a/busybox/examples/inittab b/busybox/examples/inittab new file mode 100644 index 000000000..8e7e945b3 --- /dev/null +++ b/busybox/examples/inittab @@ -0,0 +1,86 @@ +# /etc/inittab init(8) configuration for BusyBox +# +# Copyright (C) 1999 by Lineo, inc. Written by Erik Andersen +# , +# +# +# 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: ::: +# +# : 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. +# +# : The runlevels field is completely ignored. +# +# : Valid actions include: sysinit, respawn, askfirst, wait, once, +# 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. +# +# : 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 +# 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 before rebooting +::ctrlaltdel:/sbin/reboot +::shutdown:/bin/umount -a -r +::shutdown:/sbin/swapoff -a + diff --git a/busybox/examples/kernel-patches/Will_devps_GoIntoTheKernel b/busybox/examples/kernel-patches/Will_devps_GoIntoTheKernel new file mode 100644 index 000000000..33ee8b47e --- /dev/null +++ b/busybox/examples/kernel-patches/Will_devps_GoIntoTheKernel @@ -0,0 +1,104 @@ +I have been asked several times whether the devps patch will go into the +mainline Linux kernel. The following emails from Alan Cox and Linux Torvalds +make it clear that it is not going to happen. This does not mean this patch +had no value -- it does. It just means that those that like it get to apply it +themselves... + + -Erik + + +------------------------------- + +From alan@lxorguk.ukuu.org.uk Thu Apr 13 08:07:22 2000 +Return-Path: +Delivered-To: andersen@dillweed.dsl.xmission.com +Received: from localhost (dillweed.dsl.xmission.com [10.0.0.1]) + by dillweed.dsl.xmission.com (Postfix) with ESMTP id 1D57A11A4F5 + for ; Thu, 13 Apr 2000 08:07:22 -0600 (MDT) +Envelope-to: andersen@xmission.com +Received: from mail.xmission.com + by localhost with IMAP (fetchmail-5.3.3) + for andersen@dillweed.dsl.xmission.com (single-drop); Thu, 13 Apr 2000 08:07:22 -0600 (MDT) +Received: from [194.168.151.1] (helo=the-village.bc.nu) + by mail.xmission.com with esmtp (Exim 3.03 #3) + id 12fhQk-0002OZ-00 + for andersen@xmission.com; Thu, 13 Apr 2000 05:05:03 -0600 +Received: from alan by the-village.bc.nu with local (Exim 2.12 #1) + id 12fhQ9-0002nD-00 + for andersen@xmission.com; Thu, 13 Apr 2000 12:04:25 +0100 +Subject: Re: kernel ps drivers [Was: vm locking question] +To: andersen@xmission.com +Date: Thu, 13 Apr 2000 12:04:23 +0100 (BST) +In-Reply-To: <20000412224130.A2748@xmission.com> from "Erik Andersen" at Apr 12, 2000 10:41:30 PM +X-Mailer: ELM [version 2.5 PL1] +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +Message-Id: +From: Alan Cox +Status: RO +X-Status: A +Content-Length: 242 +Lines: 6 + +> On the subject of ps, would you be willing to accept my /dev/ps +> patch into the kernel? If no, any suggestions on what should +> be done differently (if anything) to make it worthy of inclusion? + +For 2.2.x no, for 2.3.x ask Linus not me + + +From torvalds@transmeta.com Thu Apr 13 09:18:16 2000 +Return-Path: +Delivered-To: andersen@dillweed.dsl.xmission.com +Received: from localhost (dillweed.dsl.xmission.com [10.0.0.1]) + by dillweed.dsl.xmission.com (Postfix) with ESMTP id 3776411A3DF + for ; Thu, 13 Apr 2000 09:18:16 -0600 (MDT) +Envelope-to: andersen@xmission.com +Received: from mail.xmission.com + by localhost with IMAP (fetchmail-5.3.3) + for andersen@dillweed.dsl.xmission.com (single-drop); Thu, 13 Apr 2000 09:18:16 -0600 (MDT) +Received: from [209.10.217.66] (helo=neon-gw.transmeta.com) + by mail.xmission.com with esmtp (Exim 3.03 #3) + id 12flK2-0004dd-00 + for andersen@xmission.com; Thu, 13 Apr 2000 09:14:22 -0600 +Received: (from root@localhost) + by neon-gw.transmeta.com (8.9.3/8.9.3) id IAA18635; + Thu, 13 Apr 2000 08:10:51 -0700 +Received: from mailhost.transmeta.com(10.1.1.15) by neon-gw.transmeta.com via smap (V2.1) + id xma018629; Thu, 13 Apr 00 08:10:25 -0700 +Received: from penguin.transmeta.com (root@penguin.transmeta.com [10.1.2.202]) + by deepthought.transmeta.com (8.9.3/8.9.3) with ESMTP id IAA12264; + Thu, 13 Apr 2000 08:13:53 -0700 (PDT) +Received: from localhost (torvalds@localhost) by penguin.transmeta.com (8.9.3/8.7.3) with ESMTP id IAA02051; Thu, 13 Apr 2000 08:13:53 -0700 +X-Authentication-Warning: penguin.transmeta.com: torvalds owned process doing -bs +Date: Thu, 13 Apr 2000 08:13:53 -0700 (PDT) +From: Linus Torvalds +To: Erik Andersen +Cc: Alan Cox +Subject: Re: kernel ps drivers [Was: vm locking question] +In-Reply-To: <20000413083127.A976@xmission.com> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=US-ASCII +Status: RO +Content-Length: 659 +Lines: 16 + + + +On Thu, 13 Apr 2000, Erik Andersen wrote: +> +> For 2.3.x would you be willing to accept my /dev/ps driver into the kernel? +> (Assuming I remove the /dev/modules driver (since it was pointed out that there +> is a perfectly good syscall providing that interface). If no, is there anything +> that could be done differently (if anything) to make it worthy of inclusion? + +I do dislike /dev/ps mightily. If the problem is that /proc is too large, +then the right solution is to just clean up /proc. Which is getting done. +And yes, /proc will be larger than /dev/ps, but I still find that +preferable to having two incompatible ways to do the same thing. + + Linus + + diff --git a/busybox/examples/kernel-patches/devps.patch.9_25_2000 b/busybox/examples/kernel-patches/devps.patch.9_25_2000 new file mode 100644 index 000000000..d74a26a99 --- /dev/null +++ b/busybox/examples/kernel-patches/devps.patch.9_25_2000 @@ -0,0 +1,1478 @@ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/drivers/char/Config.in linux/drivers/char/Config.in +--- linux-2.2.18-9.virgin/drivers/char/Config.in Mon Sep 18 15:02:30 2000 ++++ linux/drivers/char/Config.in Mon Sep 25 16:33:16 2000 +@@ -56,6 +56,8 @@ + dep_tristate 'Microgate SyncLink card support' CONFIG_SYNCLINK m + dep_tristate 'HDLC line discipline support' CONFIG_N_HDLC m + fi ++tristate 'Devps support' CONFIG_DEVPS ++tristate 'Devmounts support' CONFIG_DEVMTAB + bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS + if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/drivers/char/Makefile linux/drivers/char/Makefile +--- linux-2.2.18-9.virgin/drivers/char/Makefile Mon Sep 18 15:02:30 2000 ++++ linux/drivers/char/Makefile Mon Sep 25 18:01:12 2000 +@@ -697,6 +697,22 @@ + M_OBJS += $(sort $(filter $(module-list), $(obj-m))) + + ++ifeq ($(CONFIG_DEVPS),y) ++O_OBJS += devps.o ++else ++ ifeq ($(CONFIG_DEVPS),m) ++ M_OBJS += devps.o ++ endif ++endif ++ ++ifeq ($(CONFIG_DEVMTAB),y) ++O_OBJS += devmtab.o ++else ++ ifeq ($(CONFIG_DEVMTAB),m) ++ M_OBJS += devmtab.o ++ endif ++endif ++ + include $(TOPDIR)/Rules.make + + fastdep: +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/drivers/char/devmtab.c linux/drivers/char/devmtab.c +--- linux-2.2.18-9.virgin/drivers/char/devmtab.c Wed Dec 31 17:00:00 1969 ++++ linux/drivers/char/devmtab.c Mon Sep 25 17:00:57 2000 +@@ -0,0 +1,295 @@ ++/* vi: set sw=8 ts=8: */ ++/* ++ * linux/drivers/char/devmtab.c ++ * ++ * Copyright (C) 2000 Erik Andersen ++ * ++ * May be copied or modified under the terms of the GNU General Public License. ++ * See linux/COPYING for more information. ++ * ++ * This driver implements an interface whereby programs such as mount(8), ++ * umount(8), and df(1) may obtain all the process information they need to do ++ * their jobs without needing to use /proc. This driver another step in my ++ * evil plan to completely dismantle /proc. Muhahahaha! ++ * ++ * Suggestions are welcome. Patches that work are more welcome though. ;-) ++ * ++ * ++ * When using this driver, running: ++ * mknod /dev/mtab c 10 22 ++ * could be considered helpful. ++ * ++ * ---------------------------------- ++ * 1.00 Mar 07, 2000 -- Initial version. ++ * ++ *************************************************************************/ ++ ++#define DEVMTAB_VERSION "1.00" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* Some variables used by this device */ ++static struct wait_queue *devmtab_waitq = NULL; ++static int devmtab_already_opened = 0; ++static char *mtabfile = NULL; ++static int mtab_ptr; ++static int mtab_size; ++ ++ ++ ++/**************************************************************************** ++ * Handle opening and closing of the device ++ */ ++ ++static long long ++devmtab_lseek (struct file *file, long long offset, int orig) ++{ ++ return -ESPIPE; ++} ++ ++static ssize_t ++devmtab_read (struct file *file, char *buf, size_t count, loff_t * ppos) ++{ ++ int n = mtab_size - mtab_ptr; ++ ++ if (mtab_size == 0) { ++ /* Make some space */ ++ if (!(mtabfile = (char *) get_free_page (GFP_KERNEL))) ++ return -ENOMEM; ++ /* Grab the mtab */ ++ get_filesystem_info (mtabfile); ++ mtab_ptr = 0; ++ mtab_size = strlen (mtabfile); ++ n = mtab_size - mtab_ptr; ++ } ++ ++ if (n > count) ++ n = count; ++ if (n <= 0) ++ return 0; ++ ++ if (copy_to_user (buf, mtabfile, n)) ++ return -EFAULT; ++ mtab_ptr += n; ++ return n; ++} ++ ++static int devmtab_open (struct inode *ip, struct file *fp) ++{ ++ /* Only let one process use us at any time -- putting other ++ * processes to sleep. Those opening us O_NONBLOCK will ++ * get an EAGAIN error */ ++ if ((fp->f_flags & O_NONBLOCK) && devmtab_already_opened) ++ return -EAGAIN; ++ ++ MOD_INC_USE_COUNT; ++ ++ while (devmtab_already_opened) { ++ int i, got_signal=0; ++ ++ /* Go to sleep until we get woken up ++ * by devmtab_close or we receive a signal */ ++ module_interruptible_sleep_on(&devmtab_waitq); ++ ++ for(i=0; i<_NSIG_WORDS && !got_signal; i++) ++ got_signal = current->signal.sig[i] & ~current->blocked.sig[i]; ++ ++ /* If we got a signal, decrement the use count ++ * and return to user space */ ++ if (got_signal) { ++ MOD_DEC_USE_COUNT; ++ return -EINTR; ++ } ++ } ++ ++ /* Since we got here, then devmtab_already_opened must equal 0 */ ++ devmtab_already_opened=1; ++ mtab_ptr = 0; ++ mtab_size = 0; ++ ++ return 0; ++} ++ ++static int devmtab_release (struct inode *ip, struct file *fp) ++{ ++ /* Clean up */ ++ if (mtabfile != NULL) { ++ free_page ((unsigned long) mtabfile); ++ mtabfile = NULL; ++ } ++ ++ /* Zero out the reference counter */ ++ devmtab_already_opened=0; ++ ++ /* Wake up anybody that is waiting to access this device */ ++ module_wake_up(&devmtab_waitq); ++ ++ MOD_DEC_USE_COUNT; ++ ++ return 0; ++} ++ ++static int ++devmtab_ioctl (struct inode *ip, struct file *fp, ++ unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case DEVMTAB_COUNT_FILESYSTEMS:{ ++ return(count_kfstypes()); ++ } ++ ++ case DEVMTAB_GET_FILESYSTEMS:{ ++ int stat, count; ++ struct k_fstype* fstypelist; ++ ++ /* How many are there? */ ++ count = count_kfstypes(); ++ ++ /* Make some space */ ++ fstypelist = (struct k_fstype *)kmalloc(sizeof(struct k_fstype) * count, GFP_KERNEL); ++ if (!fstypelist) ++ return -ENOMEM; ++ memset(fstypelist, 0, sizeof(struct k_fstype) * count); ++ ++ /* Grab the mtab entries */ ++ get_kfstype_list(count, fstypelist); ++ ++ /* Make sure there is enough room */ ++ stat = verify_area (VERIFY_WRITE, (struct k_fstype *) arg, ++ sizeof(struct k_fstype) * count); ++ if (stat) { ++ printk (KERN_INFO ++ "devmtab: Insufficient space was provided.\n"); ++ return stat; ++ } ++ ++ /* Send it to user space */ ++ copy_to_user_ret ((struct k_fstype *) arg, fstypelist, ++ sizeof(struct k_fstype) * count, ++ -EFAULT); ++ ++ /* Clean up */ ++ kfree( fstypelist); ++ return 0; ++ } ++ ++ case DEVMTAB_COUNT_MOUNTS:{ ++ return(count_mounted_filesystems()); ++ } ++ ++ case DEVMTAB_GET_MOUNTS:{ ++ int stat, count; ++ struct k_mntent* mntentlist; ++ ++ /* How many are there? */ ++ count = count_mounted_filesystems(); ++ ++ /* Make some space */ ++ mntentlist = (struct k_mntent *)kmalloc(sizeof(struct k_mntent) * count, GFP_KERNEL); ++ if (!mntentlist) ++ return -ENOMEM; ++ memset(mntentlist, 0, sizeof(struct k_mntent) * count); ++ ++ /* Grab the mtab entries */ ++ get_mtab_entries (count, mntentlist); ++ ++ /* Make sure there is enough room */ ++ stat = verify_area (VERIFY_WRITE, (void*) arg, ++ sizeof(struct k_mntent) * count); ++ if (stat) { ++ printk (KERN_INFO ++ "devmtab: Insufficient space was provided.\n"); ++ return stat; ++ } ++ ++ /* Send it to user space */ ++ copy_to_user_ret ((struct k_mntent *) arg, mntentlist, ++ sizeof(struct k_mntent) * count, ++ -EFAULT); ++ ++ /* Clean up */ ++ kfree( mntentlist); ++ return 0; ++ } ++ ++ default: ++ return -EINVAL; ++ ++ } ++ return 0; ++} ++ ++ ++ ++/**************************************************************************** ++ * Set up the file operations devmtab will support ++ */ ++static struct file_operations devmtab_fops = { ++ devmtab_lseek, ++ devmtab_read, ++ NULL, /* No write */ ++ NULL, /* No readdir */ ++ NULL, /* No poll */ ++ devmtab_ioctl, ++ NULL, /* No mmap */ ++ devmtab_open, ++ NULL, /* flush */ ++ devmtab_release, ++ NULL, /* fsync */ ++ NULL, /* fasync */ ++ NULL, /* check_media_change */ ++ NULL /* revalidate */ ++}; ++ ++static struct miscdevice devmtab_misc_dev = { ++ DEVMTAB_MINOR, ++ "devmtab", ++ &devmtab_fops ++}; ++ ++/* The real driver initialization function */ ++extern int devmtab_init (void) ++{ ++ printk (KERN_INFO "devmtab: driver %s loaded\n", DEVMTAB_VERSION); ++ ++ if (misc_register (&devmtab_misc_dev)) { ++ printk ("devmtab: can't register misc device %d\n", ++ DEVMTAB_MINOR); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++#ifdef MODULE ++ ++MODULE_AUTHOR ("Erik Andersen "); ++MODULE_DESCRIPTION ++ ("devmtab driver -- exports filesystem and mount information to user space"); ++ ++/* Stub driver initialization function */ ++int init_module (void) ++{ ++ return (devmtab_init ()); ++} ++ ++void cleanup_module (void) ++{ ++ printk (KERN_INFO "devmtab: driver unloaded\n"); ++ misc_deregister (&devmtab_misc_dev); ++} ++ ++#endif /* MODULE */ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/drivers/char/devps.c linux/drivers/char/devps.c +--- linux-2.2.18-9.virgin/drivers/char/devps.c Wed Dec 31 17:00:00 1969 ++++ linux/drivers/char/devps.c Mon Sep 25 17:00:57 2000 +@@ -0,0 +1,518 @@ ++/* vi: set sw=8 ts=8: */ ++/* ++ * linux/drivers/char/devps.c ++ * ++ * Copyright (C) 2000 Erik Andersen ++ * ++ * May be copied or modified under the terms of the GNU General Public License. ++ * See linux/COPYING for more information. ++ * ++ * This driver implements an interface whereby programs such as ps(1), top(1), ++ * killall(1) and the like may obtain all the process information they need to ++ * do their jobs. Now you may ask, "Why not use /proc? BSD uses /proc.". ++ * Thanks for asking. /proc is designed as a virtual filesystem. As such it ++ * presents all of its information in a nice, human readable format. But not ++ * human readable enough that mere mortals can actually look at the /proc ++ * information and know what is happening on their computer (which is why we ++ * have nice programs like ps(1) to help us out. Additionally, for ps (using ++ * /proc) to do its job, it has to do something like: ++ * ++ * dir = opendir("/proc"); ++ * while ((entry = readdir(dir)) != NULL) { ++ * if (!isdigit(*entry->d_name)) ++ * continue; ++ * open_lots_of files(); ++ * parse_lots_of_strings(); ++ * close_lots_of_files(); ++ * print_stuff(); ++ * } ++ * ++ * ++ * This is bad, because: ++ * ++ * 1) opening and closing lots of files is slow, ++ * ++ * 2) parsing lots of strings is slow, ++ * ++ * 3) every one of those strings had to be carefully printed out and formatted ++ * by the kernel, which is slow, ++ * ++ * 4) using a virtual filesystem is not the traditional UN*X solution to ++ * getting information from the kernel out to userspace (ioctls and syscalls ++ * are the established way to do this), and worst of all ++ * ++ * 5) having a virtual filesystem around has been so inviting that everyone has ++ * put their own weird little files into it, causing /proc to become a ++ * cluttered rubbish heap of 64 flavors of strange that takes a ridiculous ++ * amount of memory. ++ * ++ * This driver is the first step in my evil plan to completely ++ * dismantle /proc. Muhahahaha! ++ * ++ * Suggestions are welcome. Patches that work are more welcome though. ;-) ++ * ++ * When using this driver, running: ++ * mknod /dev/ps c 10 21 ++ * could be considered helpful. ++ * ++ * ---------------------------------- ++ * 1.00 Mar 07, 2000 -- Initial version. ++ * ++ * ++ * TODO ++ * ---------------------------------- ++ * ++ * * Right now none of the vm or fd info is being returned to user space. ++ * * There is probably other stuff that should be exported to user space. ++ * ++ * ++ *************************************************************************/ ++ ++#define DEVPS_VERSION "1.00" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Some variables used by this device */ ++static struct wait_queue *devps_waitq = NULL; ++static int devps_already_opened = 0; ++ ++/**************************************************************************** ++ * Handle opening and closing of the device ++ */ ++ ++static int devps_open (struct inode *ip, struct file *fp) ++{ ++ /* Only let one process use us at any time -- putting other ++ * processes to sleep. Those opening us O_NONBLOCK will ++ * get an EAGAIN error */ ++ if ((fp->f_flags & O_NONBLOCK) && devps_already_opened) ++ return -EAGAIN; ++ ++ MOD_INC_USE_COUNT; ++ ++ while (devps_already_opened) { ++ int i, got_signal=0; ++ ++ /* Go to sleep until we get woken up ++ * by devps_close or we receive a signal */ ++ module_interruptible_sleep_on(&devps_waitq); ++ ++ for(i=0; i<_NSIG_WORDS && !got_signal; i++) ++ got_signal = current->signal.sig[i] & ~current->blocked.sig[i]; ++ ++ /* If we got a signal, decrement the use count ++ * and return to user space */ ++ if (got_signal) { ++ MOD_DEC_USE_COUNT; ++ return -EINTR; ++ } ++ } ++ ++ /* Since we got here, then device_already_opened must equal 0 */ ++ devps_already_opened=1; ++ return 0; ++} ++ ++static int devps_release (struct inode *ip, struct file *fp) ++{ ++ /* Zero out the reference counter */ ++ devps_already_opened=0; ++ ++ /* Wake up anybody that is waiting to access this device */ ++ module_wake_up(&devps_waitq); ++ ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++ ++/* ++ * This pretty-prints the pathname of a dentry, ++ * clarifying sockets etc. ++ */ ++static int ++get_name_from_dentry (struct dentry *dentry, char *buffer, int buflen) ++{ ++ struct inode *inode; ++ char *tmp = (char *) __get_free_page (GFP_KERNEL), *path, *pattern; ++ int len; ++ ++ if (tmp == NULL) ++ return -ENOMEM; ++ ++ /* Check for special dentries.. */ ++ pattern = NULL; ++ inode = dentry->d_inode; ++ if (inode && dentry->d_parent == dentry) { ++ if (S_ISSOCK (inode->i_mode)) ++ pattern = "socket:[%lu]"; ++ if (S_ISFIFO (inode->i_mode)) ++ pattern = "pipe:[%lu]"; ++ } ++ ++ if (pattern) { ++ len = sprintf (tmp, pattern, inode->i_ino); ++ path = tmp; ++ } else { ++ path = d_path (dentry, tmp, PAGE_SIZE); ++ len = tmp + PAGE_SIZE - 1 - path; ++ } ++ ++ if (len < buflen) ++ buflen = len; ++ dput (dentry); ++ strncpy (buffer, path, buflen); ++ free_page ((unsigned long) tmp); ++ return buflen; ++} ++ ++static unsigned long get_phys_addr (struct task_struct *p, ++ unsigned long ptr) ++{ ++ pgd_t *page_dir; ++ pmd_t *page_middle; ++ pte_t pte; ++ ++ if (!p || !p->mm || ptr >= TASK_SIZE) ++ return 0; ++ /* Check for NULL pgd .. shouldn't happen! */ ++ if (!p->mm->pgd) { ++ printk ("get_phys_addr: pid %d has NULL pgd!\n", p->pid); ++ return 0; ++ } ++ ++ page_dir = pgd_offset (p->mm, ptr); ++ if (pgd_none (*page_dir)) ++ return 0; ++ if (pgd_bad (*page_dir)) { ++ printk ("bad page directory entry %08lx\n", ++ pgd_val (*page_dir)); ++ pgd_clear (page_dir); ++ return 0; ++ } ++ page_middle = pmd_offset (page_dir, ptr); ++ if (pmd_none (*page_middle)) ++ return 0; ++ if (pmd_bad (*page_middle)) { ++ printk ("bad page middle entry %08lx\n", ++ pmd_val (*page_middle)); ++ pmd_clear (page_middle); ++ return 0; ++ } ++ pte = *pte_offset (page_middle, ptr); ++ if (!pte_present (pte)) ++ return 0; ++ return pte_page (pte) + (ptr & ~PAGE_MASK); ++} ++ ++static int get_array (struct task_struct *p, unsigned long start, ++ unsigned long end, char *buffer) ++{ ++ unsigned long addr; ++ int size = 0, result = 0; ++ char c; ++ ++ if (start >= end) ++ return result; ++ for (;;) { ++ addr = get_phys_addr (p, start); ++ if (!addr) ++ return result; ++ do { ++ c = *(char *) addr; ++ if (!c) ++ result = size; ++ if (size < PAGE_SIZE) ++ buffer[size++] = c; ++ else ++ return result; ++ addr++; ++ start++; ++ if (!c && start >= end) ++ return result; ++ } while (addr & ~PAGE_MASK); ++ } ++ return result; ++} ++ ++static int ++devps_ioctl (struct inode *ip, struct file *fp, ++ unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ ++ /* Count up the total number of processes, ++ * and then return that total */ ++ case DEVPS_GET_NUM_PIDS:{ ++ struct task_struct *p; ++ pid_t num_pids = 0; ++ ++ read_lock (&tasklist_lock); ++ for_each_task (p) { ++ if (!p->pid) ++ continue; ++ num_pids++; ++ } ++ read_unlock (&tasklist_lock); ++ ++ copy_to_user_ret ((pid_t *) arg, &num_pids, ++ sizeof (num_pids), -EFAULT); ++ return 0; ++ } ++ ++ /* Returns an array containing all current pids, where ++ pidlist[0]=number of PIDs in the array. pidlist[0] also ++ specifies the size of the array for the kernel to ++ fill... */ ++ case DEVPS_GET_PID_LIST:{ ++ struct task_struct *p; ++ pid_t *pid_array = NULL; ++ pid_t num_pids; ++ int stat; ++ ++ /* Grab the first element to see how many * entries ++ they want us to fill */ ++ stat = verify_area (VERIFY_READ, (char *) arg, ++ sizeof (pid_t)); ++ if (stat) { ++ printk (KERN_INFO ++ "devps: can't tell how many " ++ "to pid's to write.\n"); ++ return stat; ++ } ++ ++ copy_from_user (&num_pids, (void *) arg, ++ sizeof (num_pids)); ++ ++ /* Now make sure we can write the specified amount ++ of stuff into the array. If we can't we might ++ as well quit now and save ourselves the bother. */ ++ stat = verify_area (VERIFY_WRITE, (char *) arg, ++ sizeof (pid_t) * num_pids); ++ if (stat) { ++ printk (KERN_INFO ++ "devps: Insufficient space was " ++ "provided to write %d pid's.\n", ++ num_pids); ++ return stat; ++ } ++ ++ /* Allocate some memory to hold this stuff in before ++ * we copy it out to user-space */ ++ pid_array = (pid_t *) kmalloc (num_pids * ++ sizeof (pid_t), ++ GFP_KERNEL); ++ if (pid_array == NULL) ++ return -ENOMEM; ++ ++ /* Now march through the PID list */ ++ pid_array[0] = 0; ++ read_lock (&tasklist_lock); ++ for_each_task (p) { ++ if (!p->pid) ++ continue; ++ (pid_array[0])++; ++ if (pid_array[0] >= (num_pids - 1)) ++ continue; ++ pid_array[pid_array[0]] = p->pid; ++ } ++ read_unlock (&tasklist_lock); ++ ++ /* Copy out to the user the number we actually filled ++ */ ++ copy_to_user_ret ((void *) arg, pid_array, ++ sizeof (pid_t) * pid_array[0], ++ -EFAULT); ++ kfree (pid_array); ++ ++ return 0; ++ } ++ ++ /* Find the details on a particular pid, and fill out a ++ struct with all the gory details. */ ++ case DEVPS_GET_PID_INFO:{ ++ struct task_struct *p; ++ struct pid_info mypidinfo; ++ unsigned int state; ++ /* 'R' running */ ++ /* 'S' sleeping */ ++ /* 'D' disk sleep */ ++ /* 'Z' zombie */ ++ /* 'T' stopped */ ++ /* 'W' paging */ ++ const char *state_string = "RSDZTW"; ++ ++ copy_from_user_ret (&mypidinfo, ++ (struct pid_info *) arg, ++ sizeof (mypidinfo), -EFAULT); ++ ++ read_lock (&tasklist_lock); ++ p = find_task_by_pid (mypidinfo.pid); ++ read_unlock (&tasklist_lock); ++ ++ /* Now copy all this crap so we can tell user space ++ all about it. ick. */ ++ memset (mypidinfo.name, 0, ++ sizeof (mypidinfo.name)); ++ strcpy (mypidinfo.name, p->comm); ++ mypidinfo.flags = p->flags; ++ mypidinfo.pgrp = p->pgrp; ++ mypidinfo.tms_cutime = p->times.tms_cutime; ++ mypidinfo.tms_cstime = p->times.tms_cstime; ++ mypidinfo.tms_utime = p->times.tms_utime; ++ mypidinfo.tms_stime = p->times.tms_stime; ++ mypidinfo.min_flt = p->min_flt; ++ mypidinfo.cmin_flt = p->cmin_flt; ++ mypidinfo.maj_flt = p->maj_flt; ++ mypidinfo.cmaj_flt = p->cmaj_flt; ++ mypidinfo.session = p->session; ++ mypidinfo.pid = p->pid; ++ mypidinfo.ppid = p->p_pptr->pid; ++ mypidinfo.tty = ++ p->tty ? kdev_t_to_nr (p->tty->device) : 0; ++ mypidinfo.start_time = p->start_time; ++ mypidinfo.uid = p->uid; ++ mypidinfo.euid = p->euid; ++ mypidinfo.suid = p->suid; ++ mypidinfo.fsuid = p->fsuid; ++ mypidinfo.gid = p->gid; ++ mypidinfo.egid = p->egid; ++ mypidinfo.sgid = p->sgid; ++ mypidinfo.fsgid = p->fsgid; ++ mypidinfo.priority = 20 - (p->counter * 10 + ++ DEF_PRIORITY / 2) / DEF_PRIORITY; ++ mypidinfo.nice = 20 - (mypidinfo.priority * 20 + ++ DEF_PRIORITY / 2) / DEF_PRIORITY; ++ state = p-> state & (TASK_RUNNING | TASK_INTERRUPTIBLE ++ | TASK_UNINTERRUPTIBLE | ++ TASK_ZOMBIE | TASK_STOPPED | ++ TASK_SWAPPING); ++ while (state) { ++ state_string++; ++ state >>= 1; ++ } ++ mypidinfo.state = *state_string; ++ mypidinfo.processor = p->processor; ++ mypidinfo.nswap = p->nswap; ++ mypidinfo.cnswap = p->cnswap; ++ if (p && p->mm) { ++ char *page = NULL; ++ ++ /* Look for some elbow room */ ++ if (!(page = (char*)get_free_page (GFP_KERNEL))) ++ return -ENOMEM; ++ /* Grab the command line */ ++ get_array (p, p->mm->arg_start, ++ p->mm->arg_end, page); ++ memcpy( mypidinfo.command_line, page, sizeof( mypidinfo.command_line)); ++ mypidinfo.command_line[sizeof( mypidinfo.command_line)-1]='\0'; ++ ++ /* Grab the environment */ ++ memset (page, 0, PAGE_SIZE); ++ get_array (p, p->mm->env_start, ++ p->mm->env_end, page); ++ memcpy( mypidinfo.environment, page, sizeof( mypidinfo.environment)); ++ mypidinfo.command_line[sizeof( mypidinfo.environment)-1]='\0'; ++ free_page ((unsigned long) page); ++ } ++ memset (mypidinfo.cwd, 0, sizeof (mypidinfo.cwd)); ++ get_name_from_dentry (dget (p->fs->pwd), mypidinfo.cwd, ++ sizeof (mypidinfo.cwd)); ++ memset (mypidinfo.root, 0, sizeof (mypidinfo.root)); ++ get_name_from_dentry (dget (p->fs->root), ++ mypidinfo.root, ++ sizeof (mypidinfo.root)); ++ ++ copy_to_user_ret ((struct pid_info *) arg, ++ &mypidinfo, sizeof (mypidinfo), ++ -EFAULT); ++ ++ return 0; ++ } ++ ++ /* Return the PID of the current process */ ++ case DEVPS_GET_CURRENT_PID:{ ++ return current->pid; ++ } ++ ++ default: ++ return -EINVAL; ++ ++ } ++ return 0; ++} ++ ++ ++ ++/**************************************************************************** ++ * Set up the file operations devps will support ++ */ ++static struct file_operations devps_fops = { ++ NULL, /* No lseek */ ++ NULL, /* No read */ ++ NULL, /* No write */ ++ NULL, /* No readdir */ ++ NULL, /* No poll */ ++ devps_ioctl, ++ NULL, /* No mmap */ ++ devps_open, ++ NULL, /* flush */ ++ devps_release, ++ NULL, /* fsync */ ++ NULL, /* fasync */ ++ NULL, /* check_media_change */ ++ NULL /* revalidate */ ++}; ++ ++static struct miscdevice devps_misc_dev = { ++ DEVPS_MINOR, ++ "devps", ++ &devps_fops ++}; ++ ++/* The real driver initialization function */ ++extern int devps_init (void) ++{ ++ printk (KERN_INFO "devps driver %s loaded\n", DEVPS_VERSION); ++ ++ if (misc_register (&devps_misc_dev)) { ++ printk ("devps: unable to get misc device %d\n", ++ DEVPS_MINOR); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++#ifdef MODULE ++ ++MODULE_AUTHOR ("Erik Andersen "); ++MODULE_DESCRIPTION ++ ("devps driver -- exports kernel process information to user space"); ++ ++ ++/* Stub driver initialization function */ ++int init_module (void) ++{ ++ return (devps_init ()); ++} ++ ++void cleanup_module (void) ++{ ++ printk (KERN_INFO "devps driver unloaded\n"); ++ misc_deregister (&devps_misc_dev); ++} ++ ++#endif /* endif MODULE */ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/drivers/char/makedevps.sh linux/drivers/char/makedevps.sh +--- linux-2.2.18-9.virgin/drivers/char/makedevps.sh Wed Dec 31 17:00:00 1969 ++++ linux/drivers/char/makedevps.sh Mon Sep 25 17:00:57 2000 +@@ -0,0 +1,5 @@ ++#!/bin/sh -x ++ ++gcc -Wall -g -I /usr/src/linux/include ps-devps.c -o ps-devps ++gcc -Wall -g -I /usr/src/linux/include mounts.c -o mounts ++ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/drivers/char/misc.c linux/drivers/char/misc.c +--- linux-2.2.18-9.virgin/drivers/char/misc.c Mon Sep 18 15:02:31 2000 ++++ linux/drivers/char/misc.c Mon Sep 25 17:00:57 2000 +@@ -85,6 +85,8 @@ + extern int pmu_device_init(void); + extern int tosh_init(void); + extern int rng_init(void); ++extern int devps_init(void); ++extern int devmtab_init(void); + + static int misc_read_proc(char *buf, char **start, off_t offset, + int len, int *eof, void *private) +@@ -268,6 +270,12 @@ + #endif + #ifdef CONFIG_PMAC_PBOOK + pmu_device_init(); ++#endif ++#ifdef CONFIG_DEVPS ++ devps_init(); ++#endif ++#ifdef CONFIG_DEVMTAB ++ devmtab_init(); + #endif + #ifdef CONFIG_SGI_NEWPORT_GFX + gfx_register (); +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/drivers/char/mounts.c linux/drivers/char/mounts.c +--- linux-2.2.18-9.virgin/drivers/char/mounts.c Wed Dec 31 17:00:00 1969 ++++ linux/drivers/char/mounts.c Mon Sep 25 17:00:57 2000 +@@ -0,0 +1,116 @@ ++/* vi: set sw=4 ts=4: */ ++/* ++ * devmtab tester ++ * ++ * ++ * Copyright (C) 2000 by 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++int main (int argc, char **argv) ++{ ++ char device[80] = "/dev/mtab"; ++ int fd; /* file descriptor for devmtab device */ ++ int i, numfilesystems; ++ struct k_fstype *fslist; ++ struct k_mntent *mntentlist; ++ ++ if (argc > 1 && **(argv + 1) == '-') { ++ fprintf(stderr, "Usage: mounts\n\nReport mounted stuff\n\nThis version of mounts accepts no options.\n\n"); ++ exit(1); ++ } ++ ++ /* open device */ ++ if ((fd = open(device, O_RDONLY)) < 0) { ++ fprintf (stderr, "open failed for `%s': %s\n", ++ device, strerror (errno)); ++ exit (1); ++ } ++ ++ /* How many filesystems? We need to know to allocate ++ * enough space for later... */ ++ numfilesystems = ioctl (fd, DEVMTAB_COUNT_FILESYSTEMS); ++ if (numfilesystems<0) { ++ fprintf (stderr, "\nDEVMTAB_COUNT_FILESYSTEMS: %s\n", ++ strerror (errno)); ++ exit (1); ++ } ++ fslist = (struct k_fstype *) calloc ( numfilesystems, sizeof(struct k_fstype)); ++ ++ /* Grab the list of available filesystems */ ++ if (ioctl (fd, DEVMTAB_GET_FILESYSTEMS, fslist)<0) { ++ fprintf (stderr, "\nDEVMTAB_GET_FILESYSTEMS: %s\n", ++ strerror (errno)); ++ exit (1); ++ } ++ fprintf( stdout, "\nEquivalent of /proc/filesystems:\n"); ++ for( i = 0 ; i < numfilesystems ; i++) { ++ fprintf( stdout, "%s%s\n", fslist[i].mnt_type, ++ (fslist[i].mnt_nodev)? " nodev" : ""); ++ } ++ ++ ++ /* How many mounted filesystems? We need to know to ++ * allocate enough space for later... */ ++ numfilesystems = ioctl (fd, DEVMTAB_COUNT_MOUNTS); ++ if (numfilesystems<0) { ++ fprintf (stderr, "\nDEVMTAB_COUNT_MOUNTS: %s\n", ++ strerror (errno)); ++ exit (1); ++ } ++ mntentlist = (struct k_mntent *) calloc ( numfilesystems, sizeof(struct k_mntent)); ++ ++ /* Grab the list of mounted filesystems */ ++ if (ioctl (fd, DEVMTAB_GET_MOUNTS, mntentlist)<0) { ++ fprintf (stderr, "\nDEVMTAB_GET_MOUNTS: %s\n", ++ strerror (errno)); ++ exit (1); ++ } ++ ++ fprintf( stdout, "\nEquivalent of /proc/mounts:\n"); ++ for( i = 0 ; i < numfilesystems ; i++) { ++ fprintf( stdout, "%s %s %s %s %d %d\n", mntentlist[i].mnt_fsname, ++ mntentlist[i].mnt_dir, mntentlist[i].mnt_type, ++ mntentlist[i].mnt_opts, mntentlist[i].mnt_freq, ++ mntentlist[i].mnt_passno); ++ } ++ ++ ++ /* clean up */ ++ free( fslist); ++ free( mntentlist); ++ if (close (fd) != 0) { ++ fprintf (stderr, "close failed for `%s': %s\n", ++ device, strerror (errno)); ++ exit (1); ++ } ++ ++ exit (0); ++} ++ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/drivers/char/ps-devps.c linux/drivers/char/ps-devps.c +--- linux-2.2.18-9.virgin/drivers/char/ps-devps.c Wed Dec 31 17:00:00 1969 ++++ linux/drivers/char/ps-devps.c Mon Sep 25 17:32:19 2000 +@@ -0,0 +1,148 @@ ++/* vi: set sw=4 ts=4: */ ++/* ++ * Mini ps implementation for use with the Linux devps driver ++ * ++ * ++ * Copyright (C) 2000 by 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define MAX_COLUMN 79 ++ ++int ++main (int argc, char **argv) ++{ ++ char device[80] = "/dev/ps"; ++ int i, j, len; ++ int fd; /* file descriptor for devps device */ ++ int status; /* return status for system calls */ ++ pid_t num_pids; ++ pid_t* pid_array = NULL; ++ struct pid_info info; ++ struct passwd *pwd; ++ struct group *grp; ++ char uidName[10] = ""; ++ char groupName[10] = ""; ++ ++ if (argc > 1 && **(argv + 1) == '-') { ++ fprintf(stderr, "Usage: ps-devps\n\nReport process status\n\nThis version of ps accepts no options.\n\n"); ++ exit(1); ++ } ++ ++ /* open device */ ++ //fd = open(device, O_RDWR); ++ fd = open(device, O_RDONLY); ++ if (fd < 0) { ++ fprintf (stderr, "open failed for `%s': %s\n", ++ device, strerror (errno)); ++ goto error; ++ } ++ ++ /* Find out how many processes there are */ ++ status = ioctl (fd, DEVPS_GET_NUM_PIDS, &num_pids); ++ if (status<0) { ++ fprintf (stderr, "\nDEVPS_GET_PID_LIST: %s\n", ++ strerror (errno)); ++ goto error; ++ } ++ ++ /* Allocate some memory -- grab a few extras just in case ++ * some new processes start up while we wait. The kernel will ++ * just ignore any extras if we give it too many, and will trunc. ++ * the list if we give it too few. */ ++ pid_array = (pid_t*) calloc( num_pids+10, sizeof(pid_t)); ++ pid_array[0] = num_pids+10; ++ ++ /* Now grab the pid list */ ++ status = ioctl (fd, DEVPS_GET_PID_LIST, pid_array); ++ if (status<0) { ++ fprintf (stderr, "\nDEVPS_GET_PID_LIST: %s\n", ++ strerror (errno)); ++ goto error; ++ } ++ ++ /* Print up a ps listing */ ++ fprintf(stdout, "%5s %-8s %-3s %5s %s\n", "PID", "Uid", "Gid", ++ "State", "Command"); ++ ++ for (i=1; ipw_name); ++ grp = getgrgid(info.egid); ++ if (grp == NULL) ++ sprintf(groupName, "%lu", info.egid); ++ else ++ sprintf(groupName, "%s", grp->gr_name); ++ ++ len = fprintf(stdout, "%5d %-8s %-8s %c ", info.pid, uidName, groupName, info.state); ++ ++ if (strlen(info.command_line) > 1) { ++ for( j=0; j<(sizeof(info.command_line)-1) && j < (MAX_COLUMN-len); j++) { ++ if (*(info.command_line+j) == '\0' && *(info.command_line+j+1) != '\0') { ++ *(info.command_line+j) = ' '; ++ } ++ } ++ *(info.command_line+j) = '\0'; ++ fprintf(stdout, "%s\n", info.command_line); ++ } else { ++ fprintf(stdout, "[%s]\n", info.name); ++ } ++ } ++ ++ /* Free memory */ ++ free( pid_array); ++ ++ /* close device */ ++ status = close (fd); ++ if (status != 0) { ++ fprintf (stderr, "close failed for `%s': %s\n", ++ device, strerror (errno)); ++ goto error; ++ } ++ ++ exit (0); ++error: ++ fflush(stdout); ++ fflush(stderr); ++ exit (1); ++} ++ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/fs/Makefile linux/fs/Makefile +--- linux-2.2.18-9.virgin/fs/Makefile Wed Aug 25 18:29:49 1999 ++++ linux/fs/Makefile Mon Sep 25 16:33:16 2000 +@@ -11,9 +11,10 @@ + L_OBJS = $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o)) + O_TARGET := fs.o + O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ +- super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ ++ block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ + ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \ + dcache.o inode.o attr.o bad_inode.o file.o $(BINFMTS) ++OX_OBJS = super.o + + MOD_LIST_NAME := FS_MODULES + ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/fs/super.c linux/fs/super.c +--- linux-2.2.18-9.virgin/fs/super.c Mon Sep 18 15:02:34 2000 ++++ linux/fs/super.c Mon Sep 25 17:00:57 2000 +@@ -18,6 +18,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -25,6 +26,7 @@ + #include + #include + #include ++#include + + #include + +@@ -311,7 +313,57 @@ + { 0, NULL } + }; + +-int get_filesystem_info( char *buf ) ++ ++extern int count_mounted_filesystems() ++{ ++ struct vfsmount *tmp = vfsmntlist; ++ int count = 0; ++ ++ while (tmp) ++ { ++ tmp = tmp->mnt_next; ++ count++; ++ } ++ ++ return count; ++} ++EXPORT_SYMBOL(count_mounted_filesystems); ++ ++ ++extern void get_mtab_entries( int count, struct k_mntent* mntentlist) ++{ ++ struct vfsmount *tmp = vfsmntlist; ++ struct proc_fs_info *fs_infop; ++ int i = 0, len; ++ ++ while (tmp && i < count) ++ { ++ strncpy(mntentlist[i].mnt_fsname, tmp->mnt_devname, ++ sizeof(mntentlist[i].mnt_fsname)); ++ strncpy(mntentlist[i].mnt_dir, tmp->mnt_dirname, ++ sizeof(mntentlist[i].mnt_dir)); ++ strncpy(mntentlist[i].mnt_type, tmp->mnt_sb->s_type->name, ++ sizeof(mntentlist[i].mnt_type)); ++ len = 0; ++ len+=sprintf(mntentlist[i].mnt_opts, "%s", ++ tmp->mnt_flags & MS_RDONLY ? "ro" : "rw"); ++ for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { ++ if (tmp->mnt_flags & fs_infop->flag) { ++ strncpy(mntentlist[i].mnt_opts+len, fs_infop->str, ++ sizeof(mntentlist[i].mnt_opts)-len); ++ len += strlen(fs_infop->str); ++ } ++ } ++ ++ /* TODO -- add in NFS stuff */ ++ ++ tmp = tmp->mnt_next; ++ i++; ++ } ++} ++EXPORT_SYMBOL(get_mtab_entries); ++ ++extern int get_filesystem_info( char *buf ) + { + struct vfsmount *tmp = vfsmntlist; + struct proc_fs_info *fs_infop; +@@ -383,8 +435,37 @@ + + return len; + } ++EXPORT_SYMBOL(get_filesystem_info); ++ ++extern int count_kfstypes() ++{ ++ struct file_system_type * tmp = file_systems; ++ int count = 0; ++ ++ while (tmp) { ++ count++; ++ tmp = tmp->next; ++ } ++ ++ return count; ++} ++EXPORT_SYMBOL(count_kfstypes); ++ ++extern void get_kfstype_list(int count, struct k_fstype* fstypelist) ++{ ++ int i = 0; ++ struct file_system_type * tmp = file_systems; ++ ++ while (tmp && i < count) { ++ strncpy(fstypelist[i].mnt_type, tmp->name, sizeof(fstypelist[i].mnt_type)); ++ fstypelist[i].mnt_nodev = (tmp->fs_flags & FS_REQUIRES_DEV)? 0 : 1; ++ tmp = tmp->next; ++ i++; ++ } ++} ++EXPORT_SYMBOL(get_kfstype_list); + +-int get_filesystem_list(char * buf) ++extern int get_filesystem_list(char * buf) + { + int len = 0; + struct file_system_type * tmp; +@@ -398,6 +479,7 @@ + } + return len; + } ++EXPORT_SYMBOL(get_filesystem_list); + + struct file_system_type *get_fs_type(const char *name) + { +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/include/linux/devmtab.h linux/include/linux/devmtab.h +--- linux-2.2.18-9.virgin/include/linux/devmtab.h Wed Dec 31 17:00:00 1969 ++++ linux/include/linux/devmtab.h Mon Sep 25 17:00:57 2000 +@@ -0,0 +1,55 @@ ++/* vi: set sw=8 ts=8: */ ++/* ++ * -- ++ * ++ * Copyright (C) 2000 Erik Andersen ++ * ++ * May be copied or modified under the terms of the GNU General Public License. ++ * See linux/COPYING for more information. ++ * ++ * This driver implements an interface whereby programs such as mount(8), ++ * umount(8), and df(1) may obtain all the process information they need to do ++ * their jobs without needing to use /proc. ++ * ++ */ ++ ++#ifndef _LINUX_DEVMTAB_H ++#define _LINUX_DEVMTAB_H ++ ++ ++/******************************************************* ++ * The list of all ioctl(2) commands suported by devmtab. ++ * For the devmtab ioctls, I have commandeered some of the ++ * higher bits of byte 0xeb. ++ *******************************************************/ ++#define DEVMTAB_COUNT_FILESYSTEMS 0xebaa /* Get a list of all fs */ ++#define DEVMTAB_GET_FILESYSTEMS 0xebab /* Get a list of all fs */ ++#define DEVMTAB_COUNT_MOUNTS 0xebac /* Returns number of mounted filesystems */ ++#define DEVMTAB_GET_MOUNTS 0xebad /* Get a list of a mounted fs */ ++#define DEVMTAB_SET_ROOTFS_DEVNAME 0xebae /* Replace /dev/root with real name */ ++ ++/******************************************************* ++ * devmtab ioctl(2) structures ++ *******************************************************/ ++ ++/* An array of these is returned by the DEVMTAB_GET_FILESYSTEMS ioctl. ++ */ ++struct k_fstype { ++ char mnt_type[255]; /* filesystem type: ext2, nfs, etc. */ ++ int mnt_nodev; /* Is this a device-less filesystem? */ ++}; ++ ++/* An array of these is returned by the DEVMTAB_GET_MOUNTS ioctl. ++ * This struct should be the same as what libc returns via the ++ * getmntent(3) function (excat this comes from the kernel, not ++ * from whatever noise is in /etc/mtab at the moment... */ ++struct k_mntent { ++ char mnt_fsname[255]; /* name of mounted file system */ ++ char mnt_dir[255]; /* path of file system mount point */ ++ char mnt_type[255]; /* filesystem type: ext2, nfs, etc. */ ++ char mnt_opts[255]; /* Comma-separated list of mount options */ ++ int mnt_freq; /* dump frequency in days */ ++ int mnt_passno; /* pass number for parallel fsck */ ++}; ++ ++#endif /* _LINUX_DEVMTAB_H */ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/include/linux/devps.h linux/include/linux/devps.h +--- linux-2.2.18-9.virgin/include/linux/devps.h Wed Dec 31 17:00:00 1969 ++++ linux/include/linux/devps.h Mon Sep 25 17:00:57 2000 +@@ -0,0 +1,78 @@ ++/* ++ * -- ++ * ++ * Copyright (C) 2000 Erik Andersen ++ * ++ * May be copied or modified under the terms of the GNU General Public License. ++ * See linux/COPYING for more information. ++ * ++ * This driver implements an interface whereby programs such as ps(1), top(1), ++ * killall(1) and the like may obtain all the process information they need to ++ * do their jobs. ++ * ++ */ ++ ++#ifndef _LINUX_DEVPS_H ++#define _LINUX_DEVPS_H ++ ++ ++/******************************************************* ++ * The list of all ioctl(2) commands suported by devps. ++ * For the devps ioctls, I have commandeered some of the ++ * higher bits of byte 0xeb. ++ *******************************************************/ ++#define DEVPS_GET_NUM_PIDS 0xeba1 /* Get a list of all PIDs */ ++#define DEVPS_GET_PID_LIST 0xeba2 /* Get a list of all PIDs */ ++#define DEVPS_GET_PID_INFO 0xeba3 /* Get info about a specific PID */ ++#define DEVPS_GET_CURRENT_PID 0xeba4 /* Get the current PID */ ++ ++/******************************************************* ++ * devps ioctl(2) structures ++ *******************************************************/ ++ ++ ++struct pid_info ++{ ++ char name[16]; ++ long flags; ++ pid_t pgrp; ++ clock_t tms_cutime; ++ clock_t tms_cstime; ++ clock_t tms_utime; ++ clock_t tms_stime; ++ unsigned long min_flt; ++ unsigned long cmin_flt; ++ unsigned long maj_flt; ++ unsigned long cmaj_flt; ++ pid_t session; ++ pid_t pid; ++ pid_t ppid; ++ int tty; ++ unsigned long start_time; ++ unsigned long uid,euid,suid,fsuid; ++ unsigned long gid,egid,sgid,fsgid; ++ long priority, nice; ++ char state; ++ int processor; ++ unsigned long nswap, cnswap; ++ char command_line[256]; ++ char environment[256]; ++ char root[256]; ++ char cwd[256]; ++#if 0 ++ /* TODO: Add in this (and probably more) stuff */ ++ ++ int resident; ++ int size; ++ int share; ++ unsigned long vsize; ++ char exe[MAX_PATH]; ++ unsigned long vm_total, vm_locked, vm_rss, vm_data, vm_stack, vm_exec, vm_lib; ++ unsigned long start_code, end_code, start_data, eip, esp; ++ unsigned long signal, blocked; ++#endif ++ ++ ++}; ++ ++#endif /* _LINUX_DEVPS_H */ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/include/linux/fs.h linux/include/linux/fs.h +--- linux-2.2.18-9.virgin/include/linux/fs.h Mon Sep 18 15:08:47 2000 ++++ linux/include/linux/fs.h Mon Sep 25 17:37:50 2000 +@@ -573,6 +573,16 @@ + struct semaphore s_vfs_rename_sem; /* Kludge */ + }; + ++/* fs/super.c */ ++#include ++ ++extern int count_kfstypes( void); ++extern void get_kfstype_list( int count, struct k_fstype* fstypelist); ++extern int count_mounted_filesystems( void); ++extern int get_filesystem_info(char *buf); ++extern int get_filesystem_list(char *buf); ++extern void get_mtab_entries( int count, struct k_mntent* mntentlist); ++ + /* + * VFS helper functions.. + */ +diff -urN --exclude=.version --exclude=.config* --exclude=.*depend linux-2.2.18-9.virgin/include/linux/miscdevice.h linux/include/linux/miscdevice.h +--- linux-2.2.18-9.virgin/include/linux/miscdevice.h Mon Aug 9 13:04:41 1999 ++++ linux/include/linux/miscdevice.h Mon Sep 25 16:33:17 2000 +@@ -11,6 +11,8 @@ + #define APOLLO_MOUSE_MINOR 7 + #define PC110PAD_MINOR 9 + #define MAC_MOUSE_MINOR 10 ++#define DEVPS_MINOR 21 ++#define DEVMTAB_MINOR 22 + #define WATCHDOG_MINOR 130 /* Watchdog timer */ + #define TEMP_MINOR 131 /* Temperature Sensor */ + #define RTC_MINOR 135 diff --git a/busybox/examples/mk2knr.pl b/busybox/examples/mk2knr.pl new file mode 100755 index 000000000..aaf4963b1 --- /dev/null +++ b/busybox/examples/mk2knr.pl @@ -0,0 +1,84 @@ +#!/usr/bin/perl -w +# +# @(#) mk2knr.pl - generates a perl script that converts lexemes to K&R-style +# +# How to use this script: +# - In the busybox directory type 'scripts/mk2knr.pl files-you-want-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/undeb b/busybox/examples/undeb new file mode 100644 index 000000000..a72e1e2ba --- /dev/null +++ b/busybox/examples/undeb @@ -0,0 +1,53 @@ +#!/bin/sh +# +# This should work with the GNU version of tar and gzip! +# This should work with the bash or ash shell! +# Requires the programs (ar, tar, gzip, and the pager more or less). +# +usage() { +echo "Usage: undeb -c package.deb " +echo " undeb -l package.deb " +echo " undeb -x package.deb /foo/boo " +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 index 000000000..376286a6f --- /dev/null +++ b/busybox/examples/unrpm @@ -0,0 +1,48 @@ +#!/bin/sh +# +# This should work with the GNU version of cpio and gzip! +# This should work with the bash or ash shell! +# Requires the programs (cpio, gzip, and the pager more or less). +# +usage() { +echo "Usage: unrpm -l package.rpm " +echo " unrpm -x package.rpm /foo/boo " +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/expr.c b/busybox/expr.c new file mode 100644 index 000000000..d6cc82e3e --- /dev/null +++ b/busybox/expr.c @@ -0,0 +1,535 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini expr implementation for busybox + * + * based on GNU expr Mike Parker. + * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc. + * + * Busybox modifications + * Copyright (c) 2000 Edward Betts . + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the gnu + * general public license for more details. + * + * you should have received a copy of the gnu general public 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 seperate 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 +#include +#include +#include +#include +#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) { + error_msg_and_die("too few arguments"); + } + + args = argv + 1; + + v = eval (); + if (*args) + 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 = strdup (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; + case string: + return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0; + default: + abort (); + } +} + +/* Coerce V to a string value (can't fail). */ + +static void tostring (VALUE *v) +{ + char *temp; + + if (v->type == integer) { + temp = xmalloc (4 * (sizeof (int) / sizeof (char))); + sprintf (temp, "%d", v->u.i); + v->u.s = temp; + v->type = string; + } +} + +/* Coerce V to an integer value. Return 1 on success, 0 on failure. */ + +static int toarith (VALUE *v) +{ + int i; + + switch (v->type) { + case integer: + return 1; + case string: + i = 0; + /* Don't interpret the empty string as an integer. */ + if (v->u.s == 0) + return 0; + i = atoi(v->u.s); + free (v->u.s); + v->u.i = i; + v->type = integer; + return 1; + default: + abort (); + } +} + +/* 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. */ + +#define cmpf(name, rel) \ +static int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (l->type == string || r->type == string) { \ + tostring (l); \ + tostring (r); \ + return strcmp (l->u.s, r->u.s) rel 0; \ + } \ + else \ + return l->u.i rel r->u.i; \ +} + cmpf (less_than, <) + cmpf (less_equal, <=) + cmpf (equal, ==) + cmpf (not_equal, !=) + cmpf (greater_equal, >=) + cmpf (greater_than, >) + +#undef cmpf + +/* The arithmetic operator handling functions. */ + +#define arithf(name, op) \ +static \ +int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (!toarith (l) || !toarith (r)) \ + error_msg_and_die ("non-numeric argument"); \ + return l->u.i op r->u.i; \ +} + +#define arithdivf(name, op) \ +static int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (!toarith (l) || !toarith (r)) \ + error_msg_and_die ( "non-numeric argument"); \ + if (r->u.i == 0) \ + error_msg_and_die ( "division by zero"); \ + return l->u.i op r->u.i; \ +} + + arithf (plus, +) + arithf (minus, -) + arithf (multiply, *) + arithdivf (divide, /) + arithdivf (mod, %) + +#undef arithf +#undef arithdivf + +/* 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) { + 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) + error_msg_and_die ( "syntax error"); + + if (nextarg ("(")) { + args++; + v = eval (); + if (!nextarg (")")) + error_msg_and_die ( "syntax error"); + args++; + return v; + } + + if (nextarg (")")) + 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) + 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 = strncpy ((char *) xmalloc (i2->u.i + 1), + l->u.s + i1->u.i - 1, i2->u.i); + v->u.s[i2->u.i] = 0; + } + 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 (*fxn) (), val; + + l = eval5 (); + while (1) { + if (nextarg ("*")) + fxn = multiply; + else if (nextarg ("/")) + fxn = divide; + else if (nextarg ("%")) + fxn = mod; + else + return l; + args++; + r = eval5 (); + val = (*fxn) (l, r); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle +, - operators. */ + +static VALUE *eval3 (void) +{ + VALUE *l, *r; + int (*fxn) (), val; + + l = eval4 (); + while (1) { + if (nextarg ("+")) + fxn = plus; + else if (nextarg ("-")) + fxn = minus; + else + return l; + args++; + r = eval4 (); + val = (*fxn) (l, r); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle comparisons. */ + +static VALUE *eval2 (void) +{ + VALUE *l, *r; + int (*fxn) (), val; + + l = eval3 (); + while (1) { + if (nextarg ("<")) + fxn = less_than; + else if (nextarg ("<=")) + fxn = less_equal; + else if (nextarg ("=") || nextarg ("==")) + fxn = equal; + else if (nextarg ("!=")) + fxn = not_equal; + else if (nextarg (">=")) + fxn = greater_equal; + else if (nextarg (">")) + fxn = greater_than; + else + return l; + args++; + r = eval3 (); + toarith (l); + toarith (r); + val = (*fxn) (l, r); + 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/fbset.c b/busybox/fbset.c new file mode 100644 index 000000000..41c7f9796 --- /dev/null +++ b/busybox/fbset.c @@ -0,0 +1,424 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fbset implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define DEFAULTFBDEV "/dev/fb0" +#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 BB_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; +#define __u32 unsigned int +struct fb_bitfield { + __u32 offset; /* beginning of bitfield */ + __u32 length; /* length of bitfield */ + __u32 msb_right; /* != 0 : Most significant bit is */ + /* right */ +}; +struct fb_var_screeninfo { + __u32 xres; /* visible resolution */ + __u32 yres; + __u32 xres_virtual; /* virtual resolution */ + __u32 yres_virtual; + __u32 xoffset; /* offset from virtual to visible */ + __u32 yoffset; /* resolution */ + + __u32 bits_per_pixel; /* guess what */ + __u32 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 */ + + __u32 nonstd; /* != 0 Non standard pixel format */ + + __u32 activate; /* see FB_ACTIVATE_* */ + + __u32 height; /* height of picture in mm */ + __u32 width; /* width of picture in mm */ + + __u32 accel_flags; /* acceleration flags (hints) */ + + /* Timing: All values in pixclocks, except pixclock (of course) */ + __u32 pixclock; /* pixel clock in ps (pico seconds) */ + __u32 left_margin; /* time from sync to picture */ + __u32 right_margin; /* time from picture to sync */ + __u32 upper_margin; /* time from sync to picture */ + __u32 lower_margin; + __u32 hsync_len; /* length of horizontal sync */ + __u32 vsync_len; /* length of vertical sync */ + __u32 sync; /* see FB_SYNC_* */ + __u32 vmode; /* see FB_VMODE_* */ + __u32 reserved[6]; /* Reserved for future compatibility */ +}; + + +static struct cmdoptions_t { + char *name; + unsigned char param_count; + 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 BB_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 BB_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 BB_FEATURE_FBSET_READMODE + FILE *f; + char buf[256]; + char *p = buf; + + f = 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 + error_msg( "mode reading not compiled in"); +#endif + return 0; +} + +static 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 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", v->xres, v->yres, (int) (vrate + 0.5)); +#ifdef BB_FEATURE_FBSET_FANCY + printf("\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n", drate / 1e6, + hrate / 1e3, vrate); +#endif + printf("\tgeometry %u %u %u %u %u\n", v->xres, v->yres, + v->xres_virtual, v->yres_virtual, v->bits_per_pixel); + printf("\ttimings %u %u %u %u %u %u %u\n", v->pixclock, v->left_margin, + v->right_margin, v->upper_margin, v->lower_margin, v->hsync_len, + v->vsync_len); + printf("\taccel %s\n", (v->accel_flags > 0 ? "true" : "false")); + printf("\trgba %u/%u,%u/%u,%u/%u,%u/%u\n", v->red.length, + v->red.offset, v->green.length, v->green.offset, v->blue.length, + v->blue.offset, v->transp.length, v->transp.offset); + printf("endmode\n\n"); +} + +#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) + 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 BB_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; +#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 { + show_usage(); + } + } + } + + if ((fh = open(fbdev, O_RDONLY)) < 0) + perror_msg_and_die("fbset(open)"); + if (ioctl(fh, FBIOGET_VSCREENINFO, &var)) + perror_msg_and_die("fbset(ioctl)"); + if (g_options & OPT_READMODE) { + if (!readmode(&var, modefile, mode)) { + error_msg("Unknown video mode `%s'", mode); + return EXIT_FAILURE; + } + } + + setmode(&var, &varset); + if (g_options & OPT_CHANGE) + if (ioctl(fh, FBIOPUT_VSCREENINFO, &var)) + 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/fdflush.c b/busybox/fdflush.c new file mode 100644 index 000000000..28f5cb68a --- /dev/null +++ b/busybox/fdflush.c @@ -0,0 +1,47 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fdflush implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +/* From */ +#define FDFLUSH _IO(2,0x4b) + +extern int fdflush_main(int argc, char **argv) +{ + int fd; + + if (argc <= 1 || **(++argv) == '-') + show_usage(); + + if ((fd = open(*argv, 0)) < 0) + perror_msg_and_die("%s", *argv); + + if (ioctl(fd, FDFLUSH, 0)) + perror_msg_and_die("%s", *argv); + + return EXIT_SUCCESS; +} diff --git a/busybox/find.c b/busybox/find.c new file mode 100644 index 000000000..e814c97b9 --- /dev/null +++ b/busybox/find.c @@ -0,0 +1,200 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini find implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * Reworked by David Douthitt and + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +static char *pattern; + +#ifdef BB_FEATURE_FIND_TYPE +static int type_mask = 0; +#endif + +#ifdef BB_FEATURE_FIND_PERM +static char perm_char = 0; +static int perm_mask = 0; +#endif + +#ifdef BB_FEATURE_FIND_MTIME +static char mtime_char; +static int mtime_days; +#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 BB_FEATURE_FIND_TYPE + if (type_mask != 0) { + if (!((statbuf->st_mode & S_IFMT) == type_mask)) + goto no_match; + } +#endif +#ifdef BB_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 BB_FEATURE_FIND_MTIME + if (mtime_days != 0) { + time_t file_age = time(NULL) - statbuf->st_mtime; + time_t mtime_secs = mtime_days * 24 * 60 * 60; + if (!((isdigit(mtime_char) && mtime_secs >= file_age && + mtime_secs < file_age + 24 * 60 * 60) || + (mtime_char == '+' && mtime_secs >= file_age) || + (mtime_char == '-' && mtime_secs < file_age))) + goto no_match; + } +#endif + puts(fileName); +no_match: + return (TRUE); +} + +#ifdef BB_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') + error_msg_and_die("invalid argument `%s' to `-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) + error_msg_and_die("option `-name' requires an argument"); + pattern = argv[i]; +#ifdef BB_FEATURE_FIND_TYPE + } else if (strcmp(argv[i], "-type") == 0) { + if (++i == argc) + error_msg_and_die("option `-type' requires an argument"); + type_mask = find_type(argv[i]); +#endif +#ifdef BB_FEATURE_FIND_PERM + } else if (strcmp(argv[i], "-perm") == 0) { + char *end; + if (++i == argc) + error_msg_and_die("option `-perm' requires an argument"); + perm_mask = strtol(argv[i], &end, 8); + if (end[0] != '\0') + error_msg_and_die("invalid argument `%s' to `-perm'", argv[i]); + if (perm_mask > 07777) + error_msg_and_die("invalid argument `%s' to `-perm'", argv[i]); + if ((perm_char = argv[i][0]) == '-') + perm_mask = -perm_mask; +#endif +#ifdef BB_FEATURE_FIND_MTIME + } else if (strcmp(argv[i], "-mtime") == 0) { + char *end; + if (++i == argc) + error_msg_and_die("option `-mtime' requires an argument"); + mtime_days = strtol(argv[i], &end, 10); + if (end[0] != '\0') + error_msg_and_die("invalid argument `%s' to `-mtime'", argv[i]); + if ((mtime_char = argv[i][0]) == '-') + mtime_days = -mtime_days; +#endif + } else + show_usage(); + } + + if (firstopt == 1) { + if (recursive_action(".", TRUE, dereference, FALSE, fileAction, + fileAction, NULL) == FALSE) + status = EXIT_FAILURE; + } else { + for (i = 1; i < firstopt; i++) { + if (recursive_action(argv[i], TRUE, dereference, FALSE, fileAction, + fileAction, NULL) == FALSE) + status = EXIT_FAILURE; + } + } + + return status; +} diff --git a/busybox/findutils/find.c b/busybox/findutils/find.c new file mode 100644 index 000000000..e814c97b9 --- /dev/null +++ b/busybox/findutils/find.c @@ -0,0 +1,200 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini find implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * Reworked by David Douthitt and + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +static char *pattern; + +#ifdef BB_FEATURE_FIND_TYPE +static int type_mask = 0; +#endif + +#ifdef BB_FEATURE_FIND_PERM +static char perm_char = 0; +static int perm_mask = 0; +#endif + +#ifdef BB_FEATURE_FIND_MTIME +static char mtime_char; +static int mtime_days; +#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 BB_FEATURE_FIND_TYPE + if (type_mask != 0) { + if (!((statbuf->st_mode & S_IFMT) == type_mask)) + goto no_match; + } +#endif +#ifdef BB_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 BB_FEATURE_FIND_MTIME + if (mtime_days != 0) { + time_t file_age = time(NULL) - statbuf->st_mtime; + time_t mtime_secs = mtime_days * 24 * 60 * 60; + if (!((isdigit(mtime_char) && mtime_secs >= file_age && + mtime_secs < file_age + 24 * 60 * 60) || + (mtime_char == '+' && mtime_secs >= file_age) || + (mtime_char == '-' && mtime_secs < file_age))) + goto no_match; + } +#endif + puts(fileName); +no_match: + return (TRUE); +} + +#ifdef BB_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') + error_msg_and_die("invalid argument `%s' to `-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) + error_msg_and_die("option `-name' requires an argument"); + pattern = argv[i]; +#ifdef BB_FEATURE_FIND_TYPE + } else if (strcmp(argv[i], "-type") == 0) { + if (++i == argc) + error_msg_and_die("option `-type' requires an argument"); + type_mask = find_type(argv[i]); +#endif +#ifdef BB_FEATURE_FIND_PERM + } else if (strcmp(argv[i], "-perm") == 0) { + char *end; + if (++i == argc) + error_msg_and_die("option `-perm' requires an argument"); + perm_mask = strtol(argv[i], &end, 8); + if (end[0] != '\0') + error_msg_and_die("invalid argument `%s' to `-perm'", argv[i]); + if (perm_mask > 07777) + error_msg_and_die("invalid argument `%s' to `-perm'", argv[i]); + if ((perm_char = argv[i][0]) == '-') + perm_mask = -perm_mask; +#endif +#ifdef BB_FEATURE_FIND_MTIME + } else if (strcmp(argv[i], "-mtime") == 0) { + char *end; + if (++i == argc) + error_msg_and_die("option `-mtime' requires an argument"); + mtime_days = strtol(argv[i], &end, 10); + if (end[0] != '\0') + error_msg_and_die("invalid argument `%s' to `-mtime'", argv[i]); + if ((mtime_char = argv[i][0]) == '-') + mtime_days = -mtime_days; +#endif + } else + show_usage(); + } + + if (firstopt == 1) { + if (recursive_action(".", TRUE, dereference, FALSE, fileAction, + fileAction, NULL) == FALSE) + status = EXIT_FAILURE; + } else { + for (i = 1; i < firstopt; i++) { + if (recursive_action(argv[i], TRUE, dereference, FALSE, fileAction, + fileAction, NULL) == FALSE) + status = EXIT_FAILURE; + } + } + + return status; +} diff --git a/busybox/findutils/grep.c b/busybox/findutils/grep.c new file mode 100644 index 000000000..3254868be --- /dev/null +++ b/busybox/findutils/grep.c @@ -0,0 +1,359 @@ +/* + * Mini grep implementation for busybox using libc regex. + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include /* for strerror() */ +#include +#include "busybox.h" + + +extern int optind; /* in unistd.h */ +extern int errno; /* for use with strerror() */ +extern void xregcomp(regex_t *preg, const char *regex, int cflags); /* in busybox.h */ + +/* options */ +static int reflags = REG_NOSUB; +static int print_filename = 0; +static int print_line_num = 0; +static int print_match_counts = 0; +static int be_quiet = 0; +static int invert_search = 0; +static int suppress_err_msgs = 0; +static int print_files_with_matches = 0; + +#ifdef BB_FEATURE_GREP_CONTEXT +extern char *optarg; /* in getopt.h */ +static int lines_before = 0; +static int lines_after = 0; +static char **before_buf = NULL; +static int last_line_printed = 0; +#endif /* BB_FEATURE_GREP_CONTEXT */ + +/* globals used internally */ +static regex_t *regexes = NULL; /* growable array of compiled regular expressions */ +static int nregexes = 0; /* number of elements in above arrary */ +static int matched; /* keeps track of whether we ever matched */ +static char *cur_file = NULL; /* the current file we are reading */ + + +static void print_line(const char *line, int linenum, char decoration) +{ +#ifdef BB_FEATURE_GREP_CONTEXT + /* possibly print the little '--' seperator */ + 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); +} + + +static void grep_file(FILE *file) +{ + char *line = NULL; + int ret; + int linenum = 0; + int nmatches = 0; + int i; +#ifdef BB_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 /* BB_FEATURE_GREP_CONTEXT */ + + while ((line = get_line_from_file(file)) != NULL) { + chomp(line); + linenum++; + + for (i = 0; i < nregexes; i++) { + /* + * 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) + */ + ret = regexec(®exes[i], line, 0, NULL, 0); + if ((ret == 0 && !invert_search) || (ret == REG_NOMATCH && invert_search)) { + + /* if we found a match but were told to be quiet, stop here and + * return success */ + if (be_quiet) + exit(0); + + /* 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 BB_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 /* BB_FEATURE_GREP_CONTEXT */ + print_line(line, linenum, ':'); + } + } +#ifdef BB_FEATURE_GREP_CONTEXT + else { /* no match */ + /* Add the line to the circular 'before' buffer */ + if(lines_before) { + if(before_buf[curpos]) + free(before_buf[curpos]); + before_buf[curpos] = strdup(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 /* BB_FEATURE_GREP_CONTEXT */ + } /* for */ + 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); + if (print_files_with_matches && nmatches > 0) + printf("1\n"); + else + 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); + } + + + /* remember if we matched */ + if (nmatches != 0) + matched = 1; +} + + +static void add_regex(const char *restr) +{ + regexes = xrealloc(regexes, sizeof(regex_t) * (++nregexes)); + xregcomp(®exes[nregexes-1], restr, reflags); +} + + +static void load_regexes_from_file(const char *filename) +{ + char *line; + FILE *f = xfopen(filename, "r"); + while ((line = get_line_from_file(f)) != NULL) { + chomp(line); + add_regex(line); + free(line); + } +} + + +#ifdef BB_FEATURE_CLEAN_UP +static void destroy_regexes() +{ + if (regexes == NULL) + return; + + /* destroy all the elments in the array */ + while (--nregexes >= 0) { + regfree(®exes[nregexes]); + free(®exes[nregexes]); + } +} +#endif + + +extern int grep_main(int argc, char **argv) +{ + int opt; +#ifdef BB_FEATURE_GREP_CONTEXT + char *junk; +#endif + +#ifdef BB_FEATURE_CLEAN_UP + /* destroy command strings on exit */ + if (atexit(destroy_regexes) == -1) + perror_msg_and_die("atexit"); +#endif + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "iHhlnqvsce:f:" +#ifdef BB_FEATURE_GREP_CONTEXT +"A:B:C:" +#endif +)) > 0) { + switch (opt) { + case 'i': + reflags |= REG_ICASE; + break; + case 'l': + print_files_with_matches++; + break; + case 'H': + print_filename++; + break; + case 'h': + print_filename--; + break; + case 'n': + print_line_num++; + break; + case 'q': + be_quiet++; + break; + case 'v': + invert_search++; + break; + case 's': + suppress_err_msgs++; + break; + case 'c': + print_match_counts++; + break; + case 'e': + add_regex(optarg); + break; + case 'f': + load_regexes_from_file(optarg); + break; +#ifdef BB_FEATURE_GREP_CONTEXT + case 'A': + lines_after = strtoul(optarg, &junk, 10); + if(*junk != '\0') + error_msg_and_die("invalid context length argument"); + break; + case 'B': + lines_before = strtoul(optarg, &junk, 10); + if(*junk != '\0') + error_msg_and_die("invalid context length argument"); + before_buf = (char **)calloc(lines_before, sizeof(char *)); + break; + case 'C': + lines_after = lines_before = strtoul(optarg, &junk, 10); + if(*junk != '\0') + error_msg_and_die("invalid context length argument"); + before_buf = (char **)calloc(lines_before, sizeof(char *)); + break; +#endif /* BB_FEATURE_GREP_CONTEXT */ + default: + 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 (nregexes == 0) { + if (argv[optind] == NULL) + show_usage(); + else { + add_regex(argv[optind]); + optind++; + } + } + + /* sanity checks */ + if (print_match_counts || be_quiet || print_files_with_matches) { + print_line_num = 0; +#ifdef BB_FEATURE_GREP_CONTEXT + lines_before = 0; + lines_after = 0; +#endif + } + + /* 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) - (optind) > 0) + print_filename++; + + /* If no files were specified, or '-' was specified, take input from + * stdin. Otherwise, we grep through all the files specified. */ + if (argv[optind] == NULL || (strcmp(argv[optind], "-") == 0)) { + grep_file(stdin); + } + else { + int i; + FILE *file; + for (i = optind; i < argc; i++) { + cur_file = argv[i]; + file = fopen(cur_file, "r"); + if (file == NULL) { + if (!suppress_err_msgs) + perror_msg("%s", cur_file); + } + else { + grep_file(file); + fclose(file); + } + } + } + + return !matched; /* invert return value 0 = success, 1 = failed */ +} diff --git a/busybox/findutils/which.c b/busybox/findutils/which.c new file mode 100644 index 000000000..c460ffdd1 --- /dev/null +++ b/busybox/findutils/which.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Which implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ +#include +#include +#include +#include "busybox.h" + +extern int which_main(int argc, char **argv) +{ + char *path_list, *path_n; + struct stat filestat; + int i, count=1, found, status = EXIT_SUCCESS; + + if (argc <= 1 || **(argv + 1) == '-') + show_usage(); + argc--; + + path_list = getenv("PATH"); + if (!path_list) + path_list = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"; + + /* Replace colons with zeros in path_parsed and count them */ + for(i=strlen(path_list); i > 0; i--) + if (path_list[i]==':') { + path_list[i]=0; + count++; + } + + while(argc-- > 0) { + path_n = path_list; + argv++; + found = 0; + for (i = 0; i < count; i++) { + char *buf; + buf = concat_path_file(path_n, *argv); + if (stat (buf, &filestat) == 0 + && filestat.st_mode & S_IXUSR) + { + puts(buf); + found = 1; + break; + } + free(buf); + path_n += (strlen(path_n) + 1); + } + if (!found) + status = EXIT_FAILURE; + } + return status; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/findutils/xargs.c b/busybox/findutils/xargs.c new file mode 100644 index 000000000..48adae90a --- /dev/null +++ b/busybox/findutils/xargs.c @@ -0,0 +1,105 @@ +/* + * Mini xargs implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * Remixed by Mark Whitley , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +int xargs_main(int argc, char **argv) +{ + char *cmd_to_be_executed = NULL; + char *file_to_act_on = NULL; + + /* + * No options are supported in this version of xargs; no getopt. + * + * Re: The missing -t flag: Most programs that produce output also print + * the filename, so xargs doesn't really need to do it again. Supporting + * the -t flag =greatly= bloats up the size of this app and the memory it + * uses because you have to buffer all the input file strings in memory. If + * you really want to see the filenames that xargs will act on, just run it + * once with no args and xargs will echo the filename. Simple. + */ + + /* Store the command to be executed (taken from the command line) */ + if (argc == 1) { + /* default behavior is to echo all the filenames */ + cmd_to_be_executed = xstrdup("/bin/echo "); + } else { + /* concatenate all the arguments passed to xargs together */ + int i; + int len = 1; /* for the '\0' */ + for (i = 1; i < argc; i++) { + len += strlen(argv[i]); + len += 1; /* for the space between the args */ + cmd_to_be_executed = xrealloc(cmd_to_be_executed, len); + strcat(cmd_to_be_executed, argv[i]); + strcat(cmd_to_be_executed, " "); + } + } + + /* Now, read in one line at a time from stdin, and store this + * line to be used later as an argument to the command */ + while ((file_to_act_on = get_line_from_file(stdin)) !=NULL) { + + FILE *cmd_output = NULL; + char *output_line = NULL; + char *execstr = NULL; + + /* eat the newline off the filename. */ + chomp(file_to_act_on); + + /* eat blank lines */ + if (strlen(file_to_act_on) == 0) + continue; + + /* assemble the command and execute it */ + execstr = xcalloc(strlen(cmd_to_be_executed) + + strlen(file_to_act_on) + 1, sizeof(char)); + strcat(execstr, cmd_to_be_executed); + strcat(execstr, file_to_act_on); + cmd_output = popen(execstr, "r"); + if (cmd_output == NULL) + perror_msg_and_die("popen"); + + /* harvest the output */ + while ((output_line = get_line_from_file(cmd_output)) != NULL) { + fputs(output_line, stdout); + free(output_line); + } + + /* clean up */ + pclose(cmd_output); + free(execstr); + free(file_to_act_on); + } + +#ifdef BB_FEATURE_CLEAN_UP + free(cmd_to_be_executed); +#endif + + return 0; +} + +/* vi: set sw=4 ts=4: */ diff --git a/busybox/free.c b/busybox/free.c new file mode 100644 index 000000000..2e34a972c --- /dev/null +++ b/busybox/free.c @@ -0,0 +1,69 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini free implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ + +#include +#include +#include +#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; + } + 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; + info.totalswap/=info.mem_unit; + info.freeswap/=info.mem_unit; + info.sharedram/=info.mem_unit; + info.bufferram/=info.mem_unit; + + if (argc > 1 && **(argv + 1) == '-') + 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); + + 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); + return EXIT_SUCCESS; +} + + diff --git a/busybox/freeramdisk.c b/busybox/freeramdisk.c new file mode 100644 index 000000000..cf25fae6a --- /dev/null +++ b/busybox/freeramdisk.c @@ -0,0 +1,65 @@ +/* vi: set sw=4 ts=4: */ +/* + * freeramdisk implementation for busybox + * + * Copyright (C) 2000 and written by Emanuele Caratti + * Adjusted a bit by 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +/* From linux/fs.h */ +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ + +extern int +freeramdisk_main(int argc, char **argv) +{ + int f; + + if (argc != 2 || *argv[1] == '-') { + show_usage(); + } + + if ((f = open(argv[1], O_RDWR)) == -1) { + perror_msg_and_die("cannot open %s", argv[1]); + } + if (ioctl(f, BLKFLSBUF) < 0) { + perror_msg_and_die("failed ioctl on %s", argv[1]); + } + /* Don't bother closing. Exit does + * that, so we can save a few bytes */ + /* close(f); */ + return EXIT_SUCCESS; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + diff --git a/busybox/fsck_minix.c b/busybox/fsck_minix.c new file mode 100644 index 000000000..a2421fc34 --- /dev/null +++ b/busybox/fsck_minix.c @@ -0,0 +1,1484 @@ +/* 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 rudimetary 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 systemindependent. + * (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 . + * + * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by + * Andreas Schwab. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz + * - 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). + * + * Usuage: 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + + typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + + +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 { + u16 i_mode; + u16 i_uid; + u32 i_size; + u32 i_time; + u8 i_gid; + u8 i_nlinks; + u16 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 { + u16 i_mode; + u16 i_nlinks; + u16 i_uid; + u16 i_gid; + u32 i_size; + u32 i_atime; + u32 i_mtime; + u32 i_ctime; + u32 i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + u16 s_ninodes; + u16 s_nzones; + u16 s_imap_blocks; + u16 s_zmap_blocks; + u16 s_firstdatazone; + u16 s_log_zone_size; + u32 s_max_size; + u16 s_magic; + u16 s_state; + u32 s_zones; +}; + +struct minix_dir_entry { + u16 inode; + char name[0]; +}; + +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<> 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) +{ + 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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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++) { + if (name_list[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 BB_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 BB_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) + 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: + show_usage(); + } + } + if (!device_name) + 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", 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 BB_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/getopt.c b/busybox/getopt.c new file mode 100644 index 000000000..95ecba6e6 --- /dev/null +++ b/busybox/getopt.c @@ -0,0 +1,402 @@ +/* + * getopt.c - Enhanced implementation of BSD getopt(1) + * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 Mikiewicz + * ) + * Ported to Busybox - Alfred M. Szmidt + * Removed --version/-V and --help/-h in + * Removed prase_error(), using error_msg() from Busybox instead + * Replaced our_malloc with xmalloc and our_realloc with xrealloc + * + */ + +#include +#include +#include +#include +#include +#include + +#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; + + if (BUFFER != NULL) + free(BUFFER); + + if (!quote) { /* Just copy arg */ + BUFFER=xmalloc(strlen(arg)+1); + + strcpy(BUFFER,arg); + return BUFFER; + } + + /* Each character in arg may take upto 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 \ */ + *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) +{ + char *tmp; + 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; + tmp = xmalloc(strlen(name)+1); + strcpy(tmp,name); + long_options[long_options_nr-1].name=tmp; + } + 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) + 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 + error_msg("unknown shell after -s or --shell argument"); +} + + +/* Exit codes: + * 0) No errors, succesful 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[]) +{ + char *optstr=NULL; + 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"); + exit(0); + } else + 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]; + exit(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': + if (optstr) + free(optstr); + optstr=xmalloc(strlen(optarg)+1); + strcpy(optstr,optarg); + break; + case 'l': + add_long_options(optarg); + break; + case 'n': + if (name) + free(name); + name=xmalloc(strlen(optarg)+1); + strcpy(name,optarg); + break; + case 'q': + quiet_errors=1; + break; + case 'Q': + quiet_output=1; + break; + case 's': + set_shell(optarg); + break; + case 'T': + exit(4); + case 'u': + quote=0; + break; + default: + show_usage(); + } + + if (!optstr) { + if (optind >= argc) + error_msg_and_die("missing optstring argument"); + else { + optstr=xmalloc(strlen(argv[optind])+1); + strcpy(optstr,argv[optind]); + optind++; + } + } + if (name) + argv[optind-1]=name; + else + argv[optind-1]=argv[0]; + exit(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/grep.c b/busybox/grep.c new file mode 100644 index 000000000..3254868be --- /dev/null +++ b/busybox/grep.c @@ -0,0 +1,359 @@ +/* + * Mini grep implementation for busybox using libc regex. + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include /* for strerror() */ +#include +#include "busybox.h" + + +extern int optind; /* in unistd.h */ +extern int errno; /* for use with strerror() */ +extern void xregcomp(regex_t *preg, const char *regex, int cflags); /* in busybox.h */ + +/* options */ +static int reflags = REG_NOSUB; +static int print_filename = 0; +static int print_line_num = 0; +static int print_match_counts = 0; +static int be_quiet = 0; +static int invert_search = 0; +static int suppress_err_msgs = 0; +static int print_files_with_matches = 0; + +#ifdef BB_FEATURE_GREP_CONTEXT +extern char *optarg; /* in getopt.h */ +static int lines_before = 0; +static int lines_after = 0; +static char **before_buf = NULL; +static int last_line_printed = 0; +#endif /* BB_FEATURE_GREP_CONTEXT */ + +/* globals used internally */ +static regex_t *regexes = NULL; /* growable array of compiled regular expressions */ +static int nregexes = 0; /* number of elements in above arrary */ +static int matched; /* keeps track of whether we ever matched */ +static char *cur_file = NULL; /* the current file we are reading */ + + +static void print_line(const char *line, int linenum, char decoration) +{ +#ifdef BB_FEATURE_GREP_CONTEXT + /* possibly print the little '--' seperator */ + 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); +} + + +static void grep_file(FILE *file) +{ + char *line = NULL; + int ret; + int linenum = 0; + int nmatches = 0; + int i; +#ifdef BB_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 /* BB_FEATURE_GREP_CONTEXT */ + + while ((line = get_line_from_file(file)) != NULL) { + chomp(line); + linenum++; + + for (i = 0; i < nregexes; i++) { + /* + * 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) + */ + ret = regexec(®exes[i], line, 0, NULL, 0); + if ((ret == 0 && !invert_search) || (ret == REG_NOMATCH && invert_search)) { + + /* if we found a match but were told to be quiet, stop here and + * return success */ + if (be_quiet) + exit(0); + + /* 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 BB_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 /* BB_FEATURE_GREP_CONTEXT */ + print_line(line, linenum, ':'); + } + } +#ifdef BB_FEATURE_GREP_CONTEXT + else { /* no match */ + /* Add the line to the circular 'before' buffer */ + if(lines_before) { + if(before_buf[curpos]) + free(before_buf[curpos]); + before_buf[curpos] = strdup(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 /* BB_FEATURE_GREP_CONTEXT */ + } /* for */ + 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); + if (print_files_with_matches && nmatches > 0) + printf("1\n"); + else + 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); + } + + + /* remember if we matched */ + if (nmatches != 0) + matched = 1; +} + + +static void add_regex(const char *restr) +{ + regexes = xrealloc(regexes, sizeof(regex_t) * (++nregexes)); + xregcomp(®exes[nregexes-1], restr, reflags); +} + + +static void load_regexes_from_file(const char *filename) +{ + char *line; + FILE *f = xfopen(filename, "r"); + while ((line = get_line_from_file(f)) != NULL) { + chomp(line); + add_regex(line); + free(line); + } +} + + +#ifdef BB_FEATURE_CLEAN_UP +static void destroy_regexes() +{ + if (regexes == NULL) + return; + + /* destroy all the elments in the array */ + while (--nregexes >= 0) { + regfree(®exes[nregexes]); + free(®exes[nregexes]); + } +} +#endif + + +extern int grep_main(int argc, char **argv) +{ + int opt; +#ifdef BB_FEATURE_GREP_CONTEXT + char *junk; +#endif + +#ifdef BB_FEATURE_CLEAN_UP + /* destroy command strings on exit */ + if (atexit(destroy_regexes) == -1) + perror_msg_and_die("atexit"); +#endif + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "iHhlnqvsce:f:" +#ifdef BB_FEATURE_GREP_CONTEXT +"A:B:C:" +#endif +)) > 0) { + switch (opt) { + case 'i': + reflags |= REG_ICASE; + break; + case 'l': + print_files_with_matches++; + break; + case 'H': + print_filename++; + break; + case 'h': + print_filename--; + break; + case 'n': + print_line_num++; + break; + case 'q': + be_quiet++; + break; + case 'v': + invert_search++; + break; + case 's': + suppress_err_msgs++; + break; + case 'c': + print_match_counts++; + break; + case 'e': + add_regex(optarg); + break; + case 'f': + load_regexes_from_file(optarg); + break; +#ifdef BB_FEATURE_GREP_CONTEXT + case 'A': + lines_after = strtoul(optarg, &junk, 10); + if(*junk != '\0') + error_msg_and_die("invalid context length argument"); + break; + case 'B': + lines_before = strtoul(optarg, &junk, 10); + if(*junk != '\0') + error_msg_and_die("invalid context length argument"); + before_buf = (char **)calloc(lines_before, sizeof(char *)); + break; + case 'C': + lines_after = lines_before = strtoul(optarg, &junk, 10); + if(*junk != '\0') + error_msg_and_die("invalid context length argument"); + before_buf = (char **)calloc(lines_before, sizeof(char *)); + break; +#endif /* BB_FEATURE_GREP_CONTEXT */ + default: + 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 (nregexes == 0) { + if (argv[optind] == NULL) + show_usage(); + else { + add_regex(argv[optind]); + optind++; + } + } + + /* sanity checks */ + if (print_match_counts || be_quiet || print_files_with_matches) { + print_line_num = 0; +#ifdef BB_FEATURE_GREP_CONTEXT + lines_before = 0; + lines_after = 0; +#endif + } + + /* 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) - (optind) > 0) + print_filename++; + + /* If no files were specified, or '-' was specified, take input from + * stdin. Otherwise, we grep through all the files specified. */ + if (argv[optind] == NULL || (strcmp(argv[optind], "-") == 0)) { + grep_file(stdin); + } + else { + int i; + FILE *file; + for (i = optind; i < argc; i++) { + cur_file = argv[i]; + file = fopen(cur_file, "r"); + if (file == NULL) { + if (!suppress_err_msgs) + perror_msg("%s", cur_file); + } + else { + grep_file(file); + fclose(file); + } + } + } + + return !matched; /* invert return value 0 = success, 1 = failed */ +} diff --git a/busybox/gunzip.c b/busybox/gunzip.c new file mode 100644 index 000000000..430bc630e --- /dev/null +++ b/busybox/gunzip.c @@ -0,0 +1,183 @@ +/* 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 + * based on gzip sources + * + * Adjusted further by Erik Andersen , + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +extern int gunzip_main(int argc, char **argv) +{ + FILE *in_file = stdin; + FILE *out_file = NULL; + struct stat stat_buf; + + char *if_name = NULL; + char *of_name = NULL; + char *delete_file_name = NULL; + + const int gunzip_to_stdout = 1; + const int gunzip_force = 2; + const int gunzip_test = 4; + + int flags = 0; + int opt = 0; + int delete_old_file = FALSE; + + /* if called as zcat */ + if (strcmp(applet_name, "zcat") == 0) + flags |= gunzip_to_stdout; + + while ((opt = getopt(argc, argv, "ctfhdq")) != -1) { + switch (opt) { + case 'c': + flags |= gunzip_to_stdout; + break; + case 'f': + flags |= gunzip_force; + break; + case 't': + flags |= gunzip_test; + break; + case 'd': /* Used to convert gzip to gunzip. */ + break; + case 'q': + error_msg("-q option not supported, ignored"); + break; + case 'h': + default: + show_usage(); /* exit's inside usage */ + } + } + + /* Set input filename and number */ + if (argv[optind] == NULL || strcmp(argv[optind], "-") == 0) { + flags |= gunzip_to_stdout; + } else { + if_name = strdup(argv[optind]); + /* Open input file */ + in_file = xfopen(if_name, "r"); + + /* set the buffer size */ + setvbuf(in_file, NULL, _IOFBF, 0x8000); + + /* Get the time stamp on the input file. */ + if (stat(if_name, &stat_buf) < 0) { + error_msg_and_die("Couldn't stat file %s", if_name); + } + } + + /* Check that the input is sane. */ + if (isatty(fileno(in_file)) && (flags & gunzip_force) == 0) + error_msg_and_die("compressed data not read from terminal. Use -f to force it."); + + /* Set output filename and number */ + if (flags & gunzip_test) { + out_file = xfopen("/dev/null", "w"); /* why does test use filenum 2 ? */ + } else if (flags & gunzip_to_stdout) { + out_file = stdout; + } else { + char *extension; + int length = strlen(if_name); + + delete_old_file = TRUE; + extension = strrchr(if_name, '.'); + if (extension && strcmp(extension, ".gz") == 0) { + length -= 3; + } else if (extension && strcmp(extension, ".tgz") == 0) { + length -= 4; + } else { + error_msg_and_die("Invalid extension"); + } + of_name = (char *) xcalloc(sizeof(char), length + 1); + strncpy(of_name, if_name, length); + + /* Open output file */ + out_file = xfopen(of_name, "w"); + + /* Set permissions on the file */ + chmod(of_name, stat_buf.st_mode); + } + + /* do the decompression, and cleanup */ + if (unzip(in_file, out_file) == 0) { + /* Success, remove .gz file */ + delete_file_name = if_name; + } else { + /* remove failed attempt */ + delete_file_name = of_name; + } + + fclose(out_file); + fclose(in_file); + + if (delete_old_file == TRUE) { + if (unlink(delete_file_name) < 0) { + error_msg_and_die("Couldnt remove %s", delete_file_name); + } + } + + free(of_name); + + return(EXIT_SUCCESS); +} diff --git a/busybox/gzip.c b/busybox/gzip.c new file mode 100644 index 000000000..54bb72745 --- /dev/null +++ b/busybox/gzip.c @@ -0,0 +1,2550 @@ +/* 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 + * "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 , + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +static int method; /* compression method */ + +/* 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*)calloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \ + if (array == NULL) error_msg(memory_exhausted); \ + } +# define FREE(array) {if (array != NULL) 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 16 bit value, lsb first */ +#define put_short(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)); \ + } \ +} + +/* Output a 32 bit value to the bit stream, lsb first */ +#define put_long(n) { \ + put_short((n) & 0xffff); \ + put_short(((ulg)(n)) >> 16); \ +} + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) 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 crc_table_empty = 1; + +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 char ifname[MAX_PATH_LEN]; /* input file name */ +static char ofname[MAX_PATH_LEN]; /* output file name */ +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 */ + +/* ======================================================================== + * 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_error_msg() +{ + fprintf(stderr, "\n"); + perror(""); + 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_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_table_empty) { + unsigned long csr; /* crc shift register */ + unsigned long e=0; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static const int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* Make exclusive-or pattern from polynomial (0xedb88320) */ + for (i = 0; i < sizeof(p)/sizeof(int); i++) + e |= 1L << (31 - p[i]); + + /* Compute and print table of CRC's, five per line */ + crc_32_tab[0] = 0x00000000L; + 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. + * + * ACKNOWLEDGEMENTS + * + * 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-1 +# error cannot overlay head with tab_prefix1 +#endif +#define HASH_SIZE (unsigned)(1<= 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)<= 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) { + fprintf(stderr, + " start %d, match %d, length %d\n", start, match, length); + error_msg("invalid match"); + } + if (verbose > 1) { + fprintf(stderr, "\\[%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 main (argc, argv) +// int argc; +// char **argv; +int gzip_main(int argc, char **argv) +{ + int result; + int inFileNum; + int outFileNum; + struct stat statBuf; + char *delFileName; + int tostdout = 0; + int fromstdin = 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 BB_GUNZIP + case 'd': + optind = 1; + return gunzip_main(argc, argv); +#endif + default: + show_usage(); + } + } + if ((optind == argc) || (strcmp(argv[optind], "-") == 0)) { + fromstdin = 1; + tostdout = 1; + } + + if (isatty(fileno(stdout)) && tostdout==1 && force==0) + error_msg_and_die( "compressed data not written to terminal. Use -f to force it."); + + 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); + + if (fromstdin == 1) { + strcpy(ofname, "stdin"); + + inFileNum = fileno(stdin); + time_stamp = 0; /* time unknown by default */ + ifile_size = -1L; /* convention for unknown size */ + } else { + /* Open up the input file */ + strncpy(ifname, argv[optind], MAX_PATH_LEN); + + /* Open input file */ + inFileNum = open(ifname, O_RDONLY); + if (inFileNum < 0) + perror_msg_and_die("%s", ifname); + /* Get the time stamp on the input file. */ + if (stat(ifname, &statBuf) < 0) + perror_msg_and_die("%s", ifname); + time_stamp = statBuf.st_ctime; + ifile_size = statBuf.st_size; + } + + + if (tostdout == 1) { + /* And get to work */ + strcpy(ofname, "stdout"); + outFileNum = fileno(stdout); + + clear_bufs(); /* clear input and output buffers */ + part_nb = 0; + + /* Actually do the compression/decompression. */ + zip(inFileNum, outFileNum); + + } else { + + /* And get to work */ + strncpy(ofname, ifname, MAX_PATH_LEN - 4); + strcat(ofname, ".gz"); + + + /* Open output fille */ +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) + outFileNum = open(ofname, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW); +#else + outFileNum = open(ofname, O_RDWR | O_CREAT | O_EXCL); +#endif + if (outFileNum < 0) + perror_msg_and_die("%s", ofname); + /* Set permissions on the file */ + fchmod(outFileNum, statBuf.st_mode); + + clear_bufs(); /* clear input and output buffers */ + part_nb = 0; + + /* Actually do the compression/decompression. */ + result = zip(inFileNum, outFileNum); + close(outFileNum); + close(inFileNum); + /* Delete the original file */ + if (result == OK) + delFileName = ifname; + else + delFileName = ofname; + + if (unlink(delFileName) < 0) + perror_msg_and_die("%s", delFileName); + } + + 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 */ + + +static const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = { 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 }; + +static const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = { 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 }; + +static const int extra_blbits[BL_CODES] /* extra bits for each bit length code */ += { 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 int *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) fprintf(stderr,"\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 int *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) { + fprintf(stderr, "\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) + 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) { + 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 */ + +/* =========================================================================== + * 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_byte(GZIP_MAGIC[0]); /* magic header */ + put_byte(GZIP_MAGIC[1]); + put_byte(DEFLATED); /* compression method */ + + put_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/halt.c b/busybox/halt.c new file mode 100644 index 000000000..10dcb4225 --- /dev/null +++ b/busybox/halt.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini halt implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +extern int halt_main(int argc, char **argv) +{ +#ifdef BB_FEATURE_LINUXRC + /* don't assume init's pid == 1 */ + pid_t *pid = find_pid_by_name("init"); + if (!pid || *pid<=0) + error_msg_and_die("no process killed"); + return(kill(*pid, SIGUSR1)); +#else + return(kill(1, SIGUSR1)); +#endif +} diff --git a/busybox/head.c b/busybox/head.c new file mode 100644 index 000000000..0c8ef3d59 --- /dev/null +++ b/busybox/head.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini head implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "busybox.h" + +static int head(int len, FILE *fp) +{ + int i; + char *input; + + for (i = 0; i < len; i++) { + if ((input = get_line_from_file(fp)) == NULL) + break; + fputs(input, stdout); + free(input); + } + return 0; +} + +/* BusyBoxed head(1) */ +int head_main(int argc, char **argv) +{ + FILE *fp; + int need_headers, opt, len = 10, status = EXIT_SUCCESS; + + /* parse argv[] */ + while ((opt = getopt(argc, argv, "n:")) > 0) { + switch (opt) { + case 'n': + len = atoi(optarg); + if (len >= 1) + break; + /* fallthrough */ + default: + show_usage(); + } + } + + /* get rest of argv[] or stdin if nothing's left */ + if (argv[optind] == NULL) { + head(len, stdin); + return status; + } + + need_headers = optind != (argc - 1); + while (argv[optind]) { + if (strcmp(argv[optind], "-") == 0) { + fp = stdin; + argv[optind] = "standard input"; + } else { + if ((fp = wfopen(argv[optind], "r")) == NULL) + status = EXIT_FAILURE; + } + if (fp) { + if (need_headers) { + printf("==> %s <==\n", argv[optind]); + } + head(len, fp); + if (errno) { + perror_msg("%s", argv[optind]); + status = EXIT_FAILURE; + errno = 0; + } + if (optind < argc - 1) + putchar('\n'); + if (fp != stdin) + fclose(fp); + } + optind++; + } + + return status; +} diff --git a/busybox/hostid.c b/busybox/hostid.c new file mode 100644 index 000000000..68a2cc659 --- /dev/null +++ b/busybox/hostid.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini hostid implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int hostid_main(int argc, char **argv) +{ + printf("%lx\n", gethostid()); + return EXIT_SUCCESS; +} diff --git a/busybox/hostname.c b/busybox/hostname.c new file mode 100644 index 000000000..d87851509 --- /dev/null +++ b/busybox/hostname.c @@ -0,0 +1,128 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: hostname.c,v 1.30 2001/06/26 02:06:08 bug1 Exp $ + * Mini hostname implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * adjusted by Erik Andersen 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 +#include +#include +#include +#include +#include +#include +#include "busybox.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) + error_msg_and_die("you must be root to change the hostname"); + else + perror_msg_and_die("sethostname"); + } + } else { + f = xfopen(s, "r"); + fgets(buf, 255, f); +#ifdef BB_FEATURE_CLEAN_UP + fclose(f); +#endif + chomp(buf); + do_sethostname(buf, 0); + } +} + +int hostname_main(int argc, char **argv) +{ + int opt_short = 0; + int opt_domain = 0; + int opt_ip = 0; + struct hostent *h; + char *filename = NULL; + char buf[255]; + char *s = NULL; + + if (argc < 1) + show_usage(); + + while (--argc > 0 && **(++argv) == '-') { + while (*(++(*argv))) { + switch (**argv) { + case 's': + opt_short = 1; + break; + case 'i': + opt_ip = 1; + break; + case 'd': + opt_domain = 1; + break; + case 'F': + if (--argc == 0) { + show_usage(); + } + filename = *(++argv); + break; + case '-': + if (strcmp(++(*argv), "file") || --argc ==0 ) { + show_usage(); + } + filename = *(++argv); + break; + default: + show_usage(); + } + if (filename != NULL) + break; + } + } + + if (argc >= 1) { + do_sethostname(*argv, 0); + } else if (filename != NULL) { + do_sethostname(filename, 1); + } else { + gethostname(buf, 255); + if (opt_short) { + s = strchr(buf, '.'); + if (!s) + s = buf; + *s = 0; + puts(buf); + } else if (opt_domain) { + s = strchr(buf, '.'); + puts(s ? s + 1 : ""); + } else if (opt_ip) { + h = xgethostbyname(buf); + puts(inet_ntoa(*(struct in_addr *) (h->h_addr))); + } else { + puts(buf); + } + } + return(0); +} diff --git a/busybox/hush.c b/busybox/hush.c new file mode 100644 index 000000000..0e619f80e --- /dev/null +++ b/busybox/hush.c @@ -0,0 +1,2692 @@ +/* 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 + * + * 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) 2000 by Lineo, Inc., and + * written by Erik Andersen , . + * 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 /* isalpha, isdigit */ +#include /* getpid */ +#include /* getenv, atoi */ +#include /* strchr */ +#include /* popen etc. */ +#include /* glob, of course */ +#include /* va_list */ +#include +#include +#include /* should be pretty obvious */ + +#include /* ulimit */ +#include +#include +#include + +/* #include */ +/* #define DEBUG_SHELL */ + +#ifdef BB_VER +#include "busybox.h" +#include "cmdedit.h" +#else +#define applet_name "hush" +#include "standalone.h" +#define hush_main main +#undef BB_FEATURE_SH_FANCY_PROMPT +#endif + +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_SNTX = 12 +} reserved_style; +#define FLAG_END (1<, 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) { + 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_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(); +/* "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 void lookup_param(o_string *dest, struct p_context *ctx, o_string *src); +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); +static int parse_string_outer(const char *s); +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 *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_not_written}, + {"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==unknown) + cwd = NULL; /* xgetcwd(arg) called free(arg) */ + cwd = xgetcwd((char *)cwd); + if (!cwd) + cwd = unknown; + return cwd; +} + + +/* built-in 'cd ' 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) + 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) { + error_msg("%s: no current job", child->argv[0]); + return EXIT_FAILURE; + } + } else { + if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { + 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) { + 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 { + 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 && nargv[1] == NULL) + return EXIT_FAILURE; + + /* XXX search through $PATH is missing */ + input = fopen(child->argv[1], "r"); + if (!input) { + 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); + if (o->data != NULL) 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 BB_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 BB_FEATURE_SH_FANCY_PROMPT + /* Set up the prompt */ + if (promptmode == 1) { + if (PS1) + 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 BB_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) + 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; + 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!) */ + 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; + 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]); + putenv(strdup(child->argv[i])); + } + 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 BB_FEATURE_SH_STANDALONE_SHELL + { + int argc_l; + char** argv_l=child->argv; + char *name = child->argv[0]; + +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + /* Following discussions from November 2000 on the busybox mailing + * list, the default configuration, (without + * get_last_path_component()) lets the user force use of an + * external command by specifying the full (with slashes) filename. + * If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then applets + * _aways_ override external commands, so if you want to run + * /bin/cat, it will use BusyBox cat even if /bin/cat exists on the + * filesystem and is _not_ busybox. Some systems may want this, + * most do not. */ + name = get_last_path_component(name); +#endif + /* 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); + 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) + perror_msg("waitpid"); + + /* move the shell to the foreground */ + //if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) + // 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; + + 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 = 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); + set_local_var(child->argv[i], export_me); + } + return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ + } + 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); + for (i=0; is_assignment(child->argv[i]); i++) { + putenv(strdup(child->argv[i])); + } + 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) perror_msg_and_die("pipe"); + nextout = pipefds[1]; + } else { + nextout=1; + pipefds[0] = -1; + } + + /* XXX test for failed fork()? */ + if (!(child->pid = fork())) { + /* 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) +{ + int rcode=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; + for (;pi;pi=pi->next) { + 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) continue; + 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) continue; + if (pi->num_progs == 0) continue; + 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) + perror_msg("tcsetpgrp-3"); + rcode = checkjobs(pi); + /* move the shell to the foreground */ + if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) + perror_msg("tcsetpgrp-4"); + } else { + rcode = checkjobs(pi); + } + debug_printf("checkjobs returned %d\n",rcode); + } + last_return_code=rcode; + if ( rmode == RES_IF || rmode == RES_ELIF ) + next_if_code=rcode; /* can be overwritten a number of times */ + 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; inum_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; igl_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) + error_msg_and_die("out of memory during glob"); + if (gr != 0) { /* GLOB_ABORTED ? */ + 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) { + 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) { + 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; + 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_DO | FLAG_START }, + { "while", RES_WHILE, FLAG_DO | FLAG_START }, + { "until", RES_UNTIL, FLAG_DO | FLAG_START }, + { "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) + rdata, 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"); + *new = *ctx; /* physical copy */ + initialize_context(ctx); + ctx->stack=new; + } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<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"); + 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) { + 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) { + error_msg("ambiguous redirect"); + return 1; + } + } else { + child->argv = glob_target->gl_pathv; + } + 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; + + ctx->child=prog; + /* 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; + + 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; numlength; 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) perror_msg_and_die("pipe"); + pid=fork(); + if (pid<0) { + 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 void lookup_param(o_string *dest, struct p_context *ctx, o_string *src) +{ + const char *p=NULL; + if (src->data) { + p = getenv(src->data); + if (!p) + p = get_local_var(src->data); + } + if (p) parse_string(dest, ctx, p); /* recursion */ + b_free(src); +} + +/* 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; + o_string alt=NULL_O_STRING; + char sep[]=" "; + int ch = input->peek(input); /* first character after the $ */ + debug_printf("handle_dollar: ch=%c\n",ch); + if (isalpha(ch)) { + while(ch=b_peek(input),isalnum(ch) || ch=='_') { + b_getch(input); + b_addchr(&alt,ch); + } + lookup_param(dest, ctx, &alt); + } else if (isdigit(ch)) { + i = ch-'0'; /* XXX is $0 special? */ + if (i 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_getch(input); + /* XXX maybe someone will try to escape the '}' */ + while(ch=b_getch(input),ch!=EOF && ch!='}') { + b_addchr(&alt,ch); + } + if (ch != '}') { + syntax(); + return 1; + } + lookup_param(dest, ctx, &alt); + break; + case '(': + b_getch(input); + process_command_subs(dest, ctx, input, ')'); + break; + case '*': + sep[0]=ifs[0]; + for (i=1; iquote); + } + /* 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 */ + done_word(dest, ctx); + /* 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 exeception is + * from builtin_source() */ +int parse_stream_outer(struct in_str *inp) +{ + + struct p_context ctx; + o_string temp=NULL_O_STRING; + int rcode; + do { + initialize_context(&ctx); + update_ifs_map(); + inp->promptmode=1; + rcode = parse_stream(&temp, &ctx, inp, '\n'); + done_word(&temp, &ctx); + done_pipe(&ctx,PIPE_SEQ); + run_list(ctx.list_head); + b_free(&temp); + } while (rcode != -1); /* loop on syntax errors, return on EOF */ + return 0; +} + +static int parse_string_outer(const char *s) +{ + struct in_str input; + setup_string_in_str(&input, s); + return parse_stream_outer(&input); +} + +static int parse_file_outer(FILE *f) +{ + int rcode; + struct in_str input; + setup_file_in_str(&input, f); + rcode = parse_stream_outer(&input); + 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() +{ + 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 BB_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); + 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 + 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(fileno(stdin)) && isatty(fileno(stdout))) { + interactive++; + } + + debug_printf("\ninteractive=%d\n", interactive); + if (interactive) { + /* Looks like they want an interactive shell */ + fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n"); + 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 = xfopen(argv[optind], "r"); + opt = parse_file_outer(input); + +#ifdef BB_FEATURE_CLEAN_UP + fclose(input); + if (cwd && cwd != 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); +} diff --git a/busybox/id.c b/busybox/id.c new file mode 100644 index 000000000..85b288c0c --- /dev/null +++ b/busybox/id.c @@ -0,0 +1,97 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini id implementation for busybox + * + * Copyright (C) 2000 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include + +extern int id_main(int argc, char **argv) +{ + int no_user = 0, no_group = 0, print_real = 0; + int name_not_number = 0; + char user[9], group[9]; + long gid; + long pwnam, grnam; + int opt; + + gid = 0; + + while ((opt = getopt(argc, argv, "ugrn")) > 0) { + switch (opt) { + case 'u': + no_group++; + break; + case 'g': + no_user++; + break; + case 'r': + print_real++; + break; + case 'n': + name_not_number++; + break; + default: + show_usage(); + } + } + + if (no_user && no_group) show_usage(); + + if (argv[optind] == NULL) { + if (print_real) { + my_getpwuid(user, getuid()); + my_getgrgid(group, getgid()); + } else { + my_getpwuid(user, geteuid()); + my_getgrgid(group, getegid()); + } + } else { + strncpy(user, argv[optind], 8); + user[8] = '\0'; + gid = my_getpwnamegid(user); + my_getgrgid(group, gid); + } + + pwnam=my_getpwnam(user); + grnam=my_getgrnam(group); + + if (no_group) { + if(name_not_number && user) + puts(user); + else + printf("%ld\n", pwnam); + } else if (no_user) { + if(name_not_number && group) + puts(group); + else + printf("%ld\n", grnam); + } else { + printf("uid=%ld(%s) gid=%ld(%s)\n", pwnam, user, grnam, group); + } + return(0); +} + + +/* END CODE */ diff --git a/busybox/ifconfig.c b/busybox/ifconfig.c new file mode 100644 index 000000000..7f3978a4a --- /dev/null +++ b/busybox/ifconfig.c @@ -0,0 +1,504 @@ +/* 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, + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the 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.11 2001/07/07 05:19:52 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. + */ + +#include +#include +#include // strcmp and friends +#include // isdigit and friends +#include /* offsetof */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#ifdef BB_FEATURE_IFCONFIG_SLIP +#include +#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 + +/* + * 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. */ +#define A_HOSTNAME 0x100 /* Set if it is ip addr. */ +#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */ + +/* + * 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_USHORT) +#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_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) + + +/* + * 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; + const unsigned char flags; + const unsigned int arg_flags; + 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 BB_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 BB_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. */ + {"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 BB_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 BB_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 + {"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 BB_FEATURE_IFCONFIG_HW +static int in_ether(char *bufp, struct sockaddr *sap); +#endif + +#ifdef BB_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; + struct sockaddr_in sai_hostname, sai_netmask; +#ifdef BB_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; + char *p; + char host[128]; + unsigned int mask; + unsigned int did_flags; + + goterr = 0; + did_flags = 0; + + /* skip argv[0] */ + ++argv; + --argc; + +#ifdef BB_FEATURE_IFCONFIG_STATUS + if ((argc > 0) && (strcmp(*argv,"-a") == 0)) { + interface_opt_a = 1; + --argc; + ++argv; + } +#endif + + if(argc <= 1) { +#ifdef BB_FEATURE_IFCONFIG_STATUS + return display_interfaces(argc ? *argv : NULL); +#else + 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) { + 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) { + show_usage(); + } + if (*++argv == NULL) { + if (mask & A_ARG_REQ) { + 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 BB_FEATURE_IFCONFIG_HW + if (mask & A_CAST_RESOLVE) { +#endif + safe_strncpy(host, *argv, (sizeof host)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + if (!strcmp(host, "default")) { + /* Default is special, meaning 0.0.0.0. */ + sai.sin_addr.s_addr = INADDR_ANY; + } else if ((!strcmp(host, "+")) && (mask & A_BROADCAST) && + (did_flags & (A_NETMASK|A_HOSTNAME))) { + /* + is special, meaning broadcast is derived. */ + sai.sin_addr.s_addr = (~sai_netmask.sin_addr.s_addr) | + (sai_hostname.sin_addr.s_addr & sai_netmask.sin_addr.s_addr); + } else if (inet_aton(host, &sai.sin_addr) == 0) { + /* It's not a dotted quad. */ + ++goterr; + continue; + } + if(mask & A_HOSTNAME) + sai_hostname = sai; + if(mask & A_NETMASK) + sai_netmask = sai; + p = (char *) &sai; +#ifdef BB_FEATURE_IFCONFIG_HW + } else { /* A_CAST_HOST_COPY_IN_ETHER */ + /* This is the "hw" arg case. */ + if (strcmp("ether", *argv) || (*++argv == NULL)) { + show_usage(); + } + safe_strncpy(host, *argv, (sizeof host)); + if (in_ether(host, &sa)) { + fprintf(stderr, "invalid hw-addr %s\n", 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 BB_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: + } /* end of while-loop */ + + return goterr; +} + +#ifdef BB_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; + + for (i = 0 ; i < ETH_ALEN ; i++) { + val = 0; + + /* We might get a semicolon here - not required. */ + if (i && (*bufp == ':')) { + bufp++; + } + + for (j=0 ; j<2 ; j++) { + c = *bufp; + if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'a' && c <= 'f') { + c -= ('a' - 10); + } else if (c >= 'A' && c <= 'F') { + c -= ('A' - 10); + } else if (j && (c == ':' || c == 0)) { + break; + } else { + return -1; + } + ++bufp; + val <<= 4; + val += c; + } + *ptr++ = val; + } + + return (int) (*bufp); /* Error if we don't end at end of string. */ +} +#endif diff --git a/busybox/include/applets.h b/busybox/include/applets.h new file mode 100644 index 000000000..a26a06e21 --- /dev/null +++ b/busybox/include/applets.h @@ -0,0 +1,481 @@ +/* + * 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) extern int b(int argc, char **argv); + #define APPLET_NOUSAGE(a,b,c) extern int b(int argc, char **argv); + #define APPLET_ODDNAME(a,b,c,d) extern int b(int argc, char **argv); + extern const char usage_messages[]; +#elif defined(MAKE_USAGE) + #ifdef BB_FEATURE_VERBOSE_USAGE + #define APPLET(a,b,c) a##_trivial_usage "\n\n" a##_full_usage "\0" + #define APPLET_NOUSAGE(a,b,c) "\0" + #define APPLET_ODDNAME(a,b,c,d) d##_trivial_usage "\n\n" d##_full_usage "\0" + #else + #define APPLET(a,b,c) a##_trivial_usage "\0" + #define APPLET_NOUSAGE(a,b,c) "\0" + #define APPLET_ODDNAME(a,b,c,d) d##_trivial_usage "\0" + #endif +#elif defined(MAKE_LINKS) +# define APPLET(a,b,c) LINK c a +# define APPLET_NOUSAGE(a,b,c) LINK c a +# define APPLET_ODDNAME(a,b,c,d) LINK c a +#else + const struct BB_applet applets[] = { + #define APPLET(a,b,c) {#a,b,c}, + #define APPLET_NOUSAGE(a,b,c) {a,b,c}, + #define APPLET_ODDNAME(a,b,c,d) {a,b,c}, +#endif + + + +#ifdef BB_TEST + APPLET_NOUSAGE("[", test_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_ADJTIMEX + APPLET(adjtimex, adjtimex_main, _BB_DIR_SBIN) +#endif +#ifdef BB_AR + APPLET(ar, ar_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_ASH + APPLET_NOUSAGE("ash", ash_main, _BB_DIR_BIN) +#endif +#ifdef BB_BASENAME + APPLET(basename, basename_main, _BB_DIR_USR_BIN) +#endif + APPLET_NOUSAGE("busybox", busybox_main, _BB_DIR_BIN) +#ifdef BB_CAT + APPLET(cat, cat_main, _BB_DIR_BIN) +#endif +#ifdef BB_CHGRP + APPLET(chgrp, chgrp_main, _BB_DIR_BIN) +#endif +#ifdef BB_CHMOD + APPLET(chmod, chmod_main, _BB_DIR_BIN) +#endif +#ifdef BB_CHOWN + APPLET(chown, chown_main, _BB_DIR_BIN) +#endif +#ifdef BB_CHROOT + APPLET(chroot, chroot_main, _BB_DIR_USR_SBIN) +#endif +#ifdef BB_CHVT + APPLET(chvt, chvt_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_CLEAR + APPLET(clear, clear_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_CMP + APPLET(cmp, cmp_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_CP + APPLET(cp, cp_main, _BB_DIR_BIN) +#endif +#ifdef BB_CPIO + APPLET(cpio, cpio_main, _BB_DIR_BIN) +#endif +#ifdef BB_CUT + APPLET(cut, cut_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DATE + APPLET(date, date_main, _BB_DIR_BIN) +#endif +#ifdef BB_DC + APPLET(dc, dc_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DD + APPLET(dd, dd_main, _BB_DIR_BIN) +#endif +#ifdef BB_DEALLOCVT + APPLET(deallocvt, deallocvt_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DF + APPLET(df, df_main, _BB_DIR_BIN) +#endif +#ifdef BB_DIRNAME + APPLET(dirname, dirname_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DMESG + APPLET(dmesg, dmesg_main, _BB_DIR_BIN) +#endif +#ifdef BB_DOS2UNIX + APPLET(dos2unix, dos2unix_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DPKG + APPLET(dpkg, dpkg_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DPKG_DEB + APPLET_ODDNAME("dpkg-deb", dpkg_deb_main, _BB_DIR_USR_BIN, dpkg_deb) +#endif +#ifdef BB_DU + APPLET(du, du_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_DUMPKMAP + APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN) +#endif +#ifdef BB_DUTMP + APPLET(dutmp, dutmp_main, _BB_DIR_USR_SBIN) +#endif +#ifdef BB_ECHO + APPLET(echo, echo_main, _BB_DIR_BIN) +#endif +#if defined(BB_FEATURE_GREP_EGREP_ALIAS) && defined(BB_GREP) + APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN) +#endif +#ifdef BB_ENV + APPLET(env, env_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_EXPR + APPLET(expr, expr_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TRUE_FALSE + APPLET(false, false_main, _BB_DIR_BIN) +#endif +#ifdef BB_FBSET + APPLET(fbset, fbset_main, _BB_DIR_USR_SBIN) +#endif +#ifdef BB_FDFLUSH + APPLET(fdflush, fdflush_main, _BB_DIR_BIN) +#endif +#ifdef BB_FIND + APPLET(find, find_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_FREE + APPLET(free, free_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_FREERAMDISK + APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN) +#endif +#ifdef BB_FSCK_MINIX + APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, fsck_minix) +#endif +#ifdef BB_GETOPT + APPLET(getopt, getopt_main, _BB_DIR_BIN) +#endif +#ifdef BB_GREP + APPLET(grep, grep_main, _BB_DIR_BIN) +#endif +#ifdef BB_GUNZIP + APPLET(gunzip, gunzip_main, _BB_DIR_BIN) +#endif +#ifdef BB_GZIP + APPLET(gzip, gzip_main, _BB_DIR_BIN) +#endif +#ifdef BB_HALT + APPLET(halt, halt_main, _BB_DIR_SBIN) +#endif +#ifdef BB_HEAD + APPLET(head, head_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_HOSTID + APPLET(hostid, hostid_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_HOSTNAME + APPLET(hostname, hostname_main, _BB_DIR_BIN) +#endif +#ifdef BB_HUSH + APPLET_NOUSAGE("hush", hush_main, _BB_DIR_BIN) +#endif +#ifdef BB_ID + APPLET(id, id_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_IFCONFIG + APPLET(ifconfig, ifconfig_main, _BB_DIR_SBIN) +#endif +#ifdef BB_INIT + APPLET(init, init_main, _BB_DIR_SBIN) +#endif +#ifdef BB_INSMOD + APPLET(insmod, insmod_main, _BB_DIR_SBIN) +#endif +#ifdef BB_KILL + APPLET(kill, kill_main, _BB_DIR_BIN) +#endif +#ifdef BB_KILLALL + APPLET(killall, kill_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_KLOGD + APPLET(klogd, klogd_main, _BB_DIR_SBIN) +#endif +#ifdef BB_LASH + APPLET(lash, lash_main, _BB_DIR_BIN) +#endif +#ifdef BB_LENGTH + APPLET(length, length_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_FEATURE_LINUXRC + APPLET_NOUSAGE("linuxrc", init_main, _BB_DIR_ROOT) +#endif +#ifdef BB_LN + APPLET(ln, ln_main, _BB_DIR_BIN) +#endif +#ifdef BB_LOADACM + APPLET(loadacm, loadacm_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_LOADFONT + APPLET(loadfont, loadfont_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_LOADKMAP + APPLET(loadkmap, loadkmap_main, _BB_DIR_SBIN) +#endif +#ifdef BB_LOGGER + APPLET(logger, logger_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_LOGNAME + APPLET(logname, logname_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_LOGREAD + APPLET(logread, logread_main, _BB_DIR_SBIN) +#endif +#ifdef BB_LS + APPLET(ls, ls_main, _BB_DIR_BIN) +#endif +#ifdef BB_LSMOD + APPLET(lsmod, lsmod_main, _BB_DIR_SBIN) +#endif +#ifdef BB_MAKEDEVS + APPLET(makedevs, makedevs_main, _BB_DIR_SBIN) +#endif +#ifdef BB_MD5SUM + APPLET(md5sum, md5sum_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_MKDIR + APPLET(mkdir, mkdir_main, _BB_DIR_BIN) +#endif +#ifdef BB_MKFIFO + APPLET(mkfifo, mkfifo_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_MKFS_MINIX + APPLET_ODDNAME("mkfs.minix", mkfs_minix_main, _BB_DIR_SBIN, mkfs_minix) +#endif +#ifdef BB_MKNOD + APPLET(mknod, mknod_main, _BB_DIR_BIN) +#endif +#ifdef BB_MKSWAP + APPLET(mkswap, mkswap_main, _BB_DIR_SBIN) +#endif +#ifdef BB_MKTEMP + APPLET(mktemp, mktemp_main, _BB_DIR_BIN) +#endif +#ifdef BB_MODPROBE + APPLET(modprobe, modprobe_main, _BB_DIR_SBIN) +#endif +#ifdef BB_MORE + APPLET(more, more_main, _BB_DIR_BIN) +#endif +#ifdef BB_MOUNT + APPLET(mount, mount_main, _BB_DIR_BIN) +#endif +#ifdef BB_MSH + APPLET_NOUSAGE("msh", msh_main, _BB_DIR_BIN) +#endif +#ifdef BB_MT + APPLET(mt, mt_main, _BB_DIR_BIN) +#endif +#ifdef BB_MV + APPLET(mv, mv_main, _BB_DIR_BIN) +#endif +#ifdef BB_NC + APPLET(nc, nc_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_NSLOOKUP + APPLET(nslookup, nslookup_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_PIDOF + APPLET(pidof, pidof_main, _BB_DIR_BIN) +#endif +#ifdef BB_PING + APPLET(ping, ping_main, _BB_DIR_BIN) +#endif +#ifdef BB_PIVOT_ROOT + APPLET(pivot_root, pivot_root_main, _BB_DIR_SBIN) +#endif +#ifdef BB_POWEROFF + APPLET(poweroff, poweroff_main, _BB_DIR_SBIN) +#endif +#ifdef BB_PRINTF + APPLET(printf, printf_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_PS + APPLET(ps, ps_main, _BB_DIR_BIN) +#endif +#ifdef BB_PWD + APPLET(pwd, pwd_main, _BB_DIR_BIN) +#endif +#ifdef BB_RDATE + APPLET(rdate, rdate_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_READLINK + APPLET(readlink, readlink_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_REBOOT + APPLET(reboot, reboot_main, _BB_DIR_SBIN) +#endif +#ifdef BB_RENICE + APPLET(renice, renice_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_RESET + APPLET(reset, reset_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_RM + APPLET(rm, rm_main, _BB_DIR_BIN) +#endif +#ifdef BB_RMDIR + APPLET(rmdir, rmdir_main, _BB_DIR_BIN) +#endif +#ifdef BB_RMMOD + APPLET(rmmod, rmmod_main, _BB_DIR_SBIN) +#endif +#ifdef BB_ROUTE + APPLET(route, route_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_RPM2CPIO + APPLET(rpm2cpio, rpm2cpio_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_RPMUNPACK + APPLET(rpmunpack, rpmunpack_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_SED + APPLET(sed, sed_main, _BB_DIR_BIN) +#endif +#ifdef BB_SETKEYCODES + APPLET(setkeycodes, setkeycodes_main, _BB_DIR_USR_BIN) +#endif +#if defined(BB_FEATURE_SH_IS_ASH) && defined(BB_ASH) + APPLET_NOUSAGE("sh", ash_main, _BB_DIR_BIN) +#elif defined(BB_FEATURE_SH_IS_HUSH) && defined(BB_HUSH) + APPLET_NOUSAGE("sh", hush_main, _BB_DIR_BIN) +#elif defined(BB_FEATURE_SH_IS_LASH) && defined(BB_LASH) + APPLET_NOUSAGE("sh", lash_main, _BB_DIR_BIN) +#elif defined(BB_FEATURE_SH_IS_MSH) && defined(BB_MSH) + APPLET_NOUSAGE("sh", msh_main, _BB_DIR_BIN) +#endif +#ifdef BB_SLEEP + APPLET(sleep, sleep_main, _BB_DIR_BIN) +#endif +#ifdef BB_SORT + APPLET(sort, sort_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_STTY + APPLET(stty, stty_main, _BB_DIR_BIN) +#endif +#ifdef BB_SWAPONOFF + APPLET(swapoff, swap_on_off_main, _BB_DIR_SBIN) +#endif +#ifdef BB_SWAPONOFF + APPLET(swapon, swap_on_off_main, _BB_DIR_SBIN) +#endif +#ifdef BB_SYNC + APPLET(sync, sync_main, _BB_DIR_BIN) +#endif +#ifdef BB_SYSLOGD + APPLET(syslogd, syslogd_main, _BB_DIR_SBIN) +#endif +#ifdef BB_TAIL + APPLET(tail, tail_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TAR + APPLET(tar, tar_main, _BB_DIR_BIN) +#endif +#ifdef BB_TEE + APPLET(tee, tee_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TELNET + APPLET(telnet, telnet_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TEST + APPLET(test, test_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TFTP + APPLET(tftp, tftp_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TOUCH + APPLET(touch, touch_main, _BB_DIR_BIN) +#endif +#ifdef BB_TR + APPLET(tr, tr_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TRACEROUTE + APPLET(traceroute, traceroute_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_TRUE_FALSE + APPLET(true, true_main, _BB_DIR_BIN) +#endif +#ifdef BB_TTY + APPLET(tty, tty_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_UMOUNT + APPLET(umount, umount_main, _BB_DIR_BIN) +#endif +#ifdef BB_UNAME + APPLET(uname, uname_main, _BB_DIR_BIN) +#endif +#ifdef BB_UNIQ + APPLET(uniq, uniq_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_UNIX2DOS + APPLET(unix2dos, dos2unix_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_UPDATE + APPLET(update, update_main, _BB_DIR_SBIN) +#endif +#ifdef BB_UPTIME + APPLET(uptime, uptime_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_USLEEP + APPLET(usleep, usleep_main, _BB_DIR_BIN) +#endif +#ifdef BB_UUDECODE + APPLET(uudecode, uudecode_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_UUENCODE + APPLET(uuencode, uuencode_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_VI + APPLET(vi, vi_main, _BB_DIR_BIN) +#endif +#ifdef BB_WATCHDOG + APPLET(watchdog, watchdog_main, _BB_DIR_SBIN) +#endif +#ifdef BB_WC + APPLET(wc, wc_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_WGET + APPLET(wget, wget_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_WHICH + APPLET(which, which_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_WHOAMI + APPLET(whoami, whoami_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_XARGS + APPLET(xargs, xargs_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_YES + APPLET(yes, yes_main, _BB_DIR_USR_BIN) +#endif +#ifdef BB_GUNZIP + APPLET(zcat, gunzip_main, _BB_DIR_BIN) +#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 index 000000000..f79dac8c8 --- /dev/null +++ b/busybox/include/busybox.h @@ -0,0 +1,106 @@ +/* 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 +#include +#include +#include + +#define BB_BANNER "BusyBox v" BB_VER " (" BB_BT ")" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#include + + +enum Location { + _BB_DIR_ROOT = 0, + _BB_DIR_BIN, + _BB_DIR_SBIN, + _BB_DIR_USR_BIN, + _BB_DIR_USR_SBIN +}; + +struct BB_applet { + const char* name; + int (*main)(int argc, char** argv); + enum Location location; +}; +/* 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 BB_FEATURE_BUFFERS_GO_ON_STACK +#define RESERVE_BB_BUFFER(buffer,len) char buffer[len] +#define RESERVE_BB_UBUFFER(buffer,len) unsigned char buffer[len] +#define RELEASE_BB_BUFFER(buffer) ((void)0) +#else +#ifdef BB_FEATURE_BUFFERS_GO_IN_BSS +#define RESERVE_BB_BUFFER(buffer,len) static char buffer[len] +#define RESERVE_BB_UBUFFER(buffer,len) static unsigned char buffer[len] +#define RELEASE_BB_BUFFER(buffer) ((void)0) +#else +#define RESERVE_BB_BUFFER(buffer,len) char *buffer=xmalloc(len) +#define RESERVE_BB_UBUFFER(buffer,len) unsigned char *buffer=xmalloc(len) +#define RELEASE_BB_BUFFER(buffer) free (buffer) +#endif +#endif + + +/* Bit map related macros -- libc5 doens't provide these... sigh. */ +#ifndef setbit +#define NBBY CHAR_BIT +#define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY)) +#define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) +#define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY))) +#define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) +#endif + +#ifndef RB_POWER_OFF +/* Stop system and switch power off if possible. */ +#define RB_POWER_OFF 0x4321fedc +#endif + + +/* Pull in the utility routines from libbb */ +#include "libbb/libbb.h" + + + +#endif /* _BB_INTERNAL_H_ */ diff --git a/busybox/include/grp.h b/busybox/include/grp.h new file mode 100644 index 000000000..87d4115ce --- /dev/null +++ b/busybox/include/grp.h @@ -0,0 +1,37 @@ +#ifndef __BB_GRP_H +#define __BB_GRP_H + +#if defined USE_SYSTEM_PWD_GRP +#include +#else + +#include +#include +#include + +/* 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. */ +}; + +extern void setgrent __P ((void)); +extern void endgrent __P ((void)); +extern struct group * getgrent __P ((void)); + +extern struct group * getgrgid __P ((__const gid_t gid)); +extern struct group * getgrnam __P ((__const char * name)); + +extern struct group * fgetgrent __P ((FILE * file)); + +extern int setgroups __P ((size_t n, __const gid_t * groups)); +extern int initgroups __P ((__const char * user, gid_t gid)); + +extern struct group * __getgrent __P ((int grp_fd)); + +#endif /* USE_SYSTEM_PWD_GRP */ +#endif /* __BB_GRP_H */ + diff --git a/busybox/include/libbb.h b/busybox/include/libbb.h new file mode 100644 index 000000000..04ed2ae82 --- /dev/null +++ b/busybox/include/libbb.h @@ -0,0 +1,325 @@ +/* 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 __LIBBB_H__ +#define __LIBBB_H__ 1 + +#include +#include +#include +#include + +#include + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#include + +#ifndef _BB_INTERNAL_H_ +#include "../busybox.h" +#endif + +#if (__GNU_LIBRARY__ < 5) && (!defined __dietlibc__) +/* libc5 doesn't define socklen_t */ +typedef unsigned int socklen_t; +/* libc5 doesn't implement BSD 4.4 daemon() */ +extern int daemon (int nochdir, int noclose); +/* libc5 doesn't implement strtok_r */ +char *strtok_r(char *s, const char *delim, char **ptrptr); +#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 + +static inline int is_decimal(int ch) { return ((ch >= '0') && (ch <= '9')); } +static inline int is_octal(int ch) { return ((ch >= '0') && (ch <= '7')); } + +/* 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 show_usage(void) __attribute__ ((noreturn)); +extern void error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); +extern void error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))); +extern void perror_msg(const char *s, ...); +extern void perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn)); +extern void vherror_msg(const char *s, va_list p); +extern void herror_msg(const char *s, ...); +extern void herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn)); + +/* These two are used internally -- you shouldn't need to use them */ +extern void verror_msg(const char *s, va_list p); +extern void vperror_msg(const char *s, va_list p); + +const char *mode_string(int mode); +const char *time_string(time_t timeVal); +int is_directory(const char *name, int followLinks, struct stat *statBuf); +int isDevice(const char *name); + +int remove_file(const char *path, int flags); +int copy_file(const char *source, const char *dest, int flags); +int copy_file_chunk(FILE *src_file, FILE *dst_file, unsigned long long chunksize); +char *buildName(const char *dirName, const char *fileName); +int makeString(int argc, const char **argv, char *buf, int bufLen); +char *getChunk(int size); +char *chunkstrdup(const char *str); +void freeChunks(void); +ssize_t safe_read(int fd, void *buf, size_t count); +int full_write(int fd, const char *buf, int len); +int full_read(int fd, char *buf, int len); +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 parse_mode( const char* s, mode_t* theMode); + +extern int get_kernel_revision(void); + +extern int get_console_fd(char* tty_name); +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 atoi_w_units (const char *cp); +extern pid_t* find_pid_by_name( char* pidName); +extern char *find_real_root_device_name(const char* name); +extern char *get_line_from_file(FILE *file); +extern void print_file(FILE *file); +extern int copyfd(int fd1, int fd2); +extern int print_file_by_name(char *filename); +extern char process_escape_sequence(const char **ptr); +extern char *get_last_path_component(char *path); +extern FILE *wfopen(const char *path, const char *mode); +extern FILE *xfopen(const char *path, const char *mode); +extern void chomp(char *s); +extern void trim(char *s); +extern struct BB_applet *find_applet_by_name(const char *name); +void run_applet_by_name(const char *name, int argc, char **argv); + +#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); +extern char *xstrdup (const char *s); +#endif +extern char *xstrndup (const char *s, int n); +extern char * safe_strncpy(char *dst, const char *src, size_t size); + +struct suffix_mult { + const char *suffix; + int mult; +}; + +extern unsigned long parse_number(const char *numstr, + const struct suffix_mult *suffixes); + + +/* 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 void my_getpwuid(char *name, long uid); +extern void my_getgrgid(char *group, long gid); +extern long my_getpwnamegid(const char *name); + +extern int device_open(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); + +void syslog_msg_with_name(const char *name, int facility, int pri, const char *msg); +void syslog_msg(int facility, int pri, const char *msg); + +/* 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 size, unsigned long block_size, unsigned long display_unit); + +int 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 *last_char_is(const char *s, int c); + +extern long arith (const char *startbuf, int *errcode); + +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; +file_header_t *get_header_ar(FILE *in_file); +file_header_t *get_header_cpio(FILE *src_stream); +file_header_t *get_header_tar(FILE *tar_stream); + +enum extract_functions_e { + extract_verbose_list = 1, + extract_list = 2, + extract_one_to_buffer = 4, + extract_to_stdout = 8, + extract_all_to_fs = 16, + extract_preserve_date = 32, + extract_data_tar_gz = 64, + extract_control_tar_gz = 128, + extract_unzip_only = 256, + extract_unconditional = 512, + extract_create_leading_dirs = 1024, + extract_quiet = 2048, + extract_exclude_list = 4096 +}; +char *unarchive(FILE *src_stream, FILE *out_stream, file_header_t *(*get_header)(FILE *), + const int extract_function, const char *prefix, char **extract_names); +char *deb_extract(const char *package_filename, FILE *out_stream, const int extract_function, + const char *prefix, const char *filename); +int read_package_field(const char *package_buffer, char **field_name, char **field_value); +char *fgets_str(FILE *file, const char *terminating_string); + +extern int unzip(FILE *l_in_file, FILE *l_out_file); +extern void gz_close(int gunzip_pid); +extern FILE *gz_open(FILE *compressed_file, int *pid); + +extern struct hostent *xgethostbyname(const char *name); +extern int create_icmp_socket(void); + +char *dirname (const char *path); + +int make_directory (char *path, long mode, int flags); + +const char *u_signal_names(const char *str_sig, int *signo, int startnum); + +#define CT_AUTO 0 +#define CT_UNIX2DOS 1 +#define CT_DOS2UNIX 2 +/* extern int convert(char *fn, int ConvType); */ + +enum { + FILEUTILS_PRESERVE_STATUS = 1, + FILEUTILS_PRESERVE_SYMLINKS = 2, + FILEUTILS_RECUR = 4, + FILEUTILS_FORCE = 8, + FILEUTILS_INTERACTIVE = 16 +}; + +extern const char *applet_name; +extern const char * const full_version; +extern const char * const name_too_long; +extern const char * const omitting_directory; +extern const char * const not_a_directory; +extern const char * const memory_exhausted; +extern const char * const invalid_date; +extern const char * const invalid_option; +extern const char * const io_error; +extern const char * const dash_dash_help; +extern const char * const write_error; +extern const char * const too_few_args; +extern const char * const name_longer_than_foo; +extern const char * const unknown; +extern const char * const can_not_create_raw_socket; + +#ifdef BB_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" +# define SC_0 "/dev/tts/0" +# define SC_1 "/dev/tts/1" +# define VC_FORMAT "/dev/vc/%d" +# define SC_FORMAT "/dev/tts/%d" +#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" +# define SC_0 "/dev/ttyS0" +# define SC_1 "/dev/ttyS1" +# define VC_FORMAT "/dev/tty%d" +# define SC_FORMAT "/dev/ttyS%d" +#endif + +/* The following devices are the same on devfs and non-devfs systems. */ +#define CURRENT_TTY "/dev/tty" +#define CONSOLE_DEV "/dev/console" + +#endif /* __LIBBB_H__ */ diff --git a/busybox/include/pwd.h b/busybox/include/pwd.h new file mode 100644 index 000000000..e603a96e3 --- /dev/null +++ b/busybox/include/pwd.h @@ -0,0 +1,40 @@ +#ifndef __BB_PWD_H +#define __BB_PWD_H + +#if defined USE_SYSTEM_PWD_GRP +#include +#else + +#include +#include +#include + +/* 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. */ +}; + +extern void setpwent __P ((void)); +extern void endpwent __P ((void)); +extern struct passwd * getpwent __P ((void)); + +extern int putpwent __P ((__const struct passwd * __p, FILE * __f)); +extern int getpw __P ((uid_t uid, char *buf)); + +extern struct passwd * fgetpwent __P ((FILE * file)); + +extern struct passwd * getpwuid __P ((__const uid_t)); +extern struct passwd * getpwnam __P ((__const char *)); + +extern struct passwd * __getpwent __P ((__const int passwd_fd)); + +#endif /* USE_SYSTEM_PWD_GRP */ +#endif /* __BB_PWD_H */ + diff --git a/busybox/include/usage.h b/busybox/include/usage.h new file mode 100644 index 000000000..13759d23f --- /dev/null +++ b/busybox/include/usage.h @@ -0,0 +1,1829 @@ +#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\n" + +#define ar_trivial_usage \ + "-[ov][ptx] 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\n" + +#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 cat_trivial_usage \ + "[FILE]..." +#define cat_full_usage \ + "Concatenates FILE(s) and prints them to stdout." +#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 \ + "FILE1 [FILE2]" +#define cmp_full_usage \ + "\t-s\tquiet mode - do not print\n" \ + "Compare files." + +#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-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" \ + "\tu\t\tunconditional overwrite\t" \ + "\tF\t\tinput from file\t" + +#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" + +#define date_trivial_usage \ + "[OPTION]... [+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\tdisplay time described by STRING, not `now'\n" \ + "\t-s\t\tSets time described by STRING\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: +, -, /, *, and, or, not, eor.\n" \ + "i.e., 'dc 2 2 add' -> 4, and 'dc 8 8 \\* 2 2 + /' -> 16" +#define dc_example_usage \ + "$ dc 2 2 +\n" \ + "4\n" \ + "$ dc 8 8 \* 2 2 + /\n" \ + "16\n" \ + "$ dc 0 1 and\n" \ + "0\n" \ + "$ dc 0 1 or\n" \ + "1\n" \ + "$ echo 72 9 div 8 mul | dc\n" \ + "64\n" + +#define dd_trivial_usage \ + "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \ + "\t [seek=N] [conv=notrunc|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=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" + + +#ifdef BB_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 \ + "-i package_file\n" + "[-CPru] package_name" +#define dpkg_full_usage \ + "\t-i\tInstall the package\n" \ + "\t-C\tConfigure an unpackaged package\n" \ + "\t-P\tPurge all files of a package\n" \ + "\t-r\tRemove all but the configuration files for a package\n" \ + "\t-u\tUnpack a package, but dont configure it\n" + +#define dpkg_deb_trivial_usage \ + "[-cefItxX] 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-I\tDisplay the control filenamed [argument]\n" \ + "\t-t\tExtract filesystem tree to stdout in tar format\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" + +#define du_trivial_usage \ + "[-ls" USAGE_HUMAN_READABLE("hm") USAGE_NOT_HUMAN_READABLE("") "k] [FILE]..." +#define du_full_usage \ + "Summarizes disk space used for each FILE and/or directory.\n" \ + "Disk space is printed in units of 1024 bytes.\n\n" \ + "Options:\n" \ + "\t-l\tcount sizes many times if hard linked\n" \ + "\t-s\tdisplay only a total for each argument" \ + 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 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 dutmp_trivial_usage \ + "[FILE]" +#define dutmp_full_usage \ + "Dump utmp file format (pipe delimited) from FILE\n" \ + "or stdin to stdout. (i.e., 'dutmp /var/run/utmp')" +#define dutmp_example_usage \ + "$ dutmp /var/run/utmp\n" \ + "8|7||si|||0|0|0|955637625|760097|0\n" \ + "2|0|~|~~|reboot||0|0|0|955637625|782235|0\n" \ + "1|20020|~|~~|runlevel||0|0|0|955637625|800089|0\n" \ + "8|125||l4|||0|0|0|955637629|998367|0\n" \ + "6|245|tty1|1|LOGIN||0|0|0|955637630|998974|0\n" \ + "6|246|tty2|2|LOGIN||0|0|0|955637630|999498|0\n" \ + "7|336|pts/0|vt00andersen|andersen|:0.0|0|0|0|955637763|0|0\n" + +#define echo_trivial_usage \ + "[-neE] [ARG ...]" +#define echo_full_usage \ + "Prints the specified ARGs to stdout\n\n" \ + "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" \ + "$ 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\n" + +#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" + +#ifdef BB_FEATURE_FIND_TYPE + #define USAGE_FIND_TYPE(a) a +#else + #define USAGE_FIND_TYPE(a) +#endif +#ifdef BB_FEATURE_FIND_PERM + #define USAGE_FIND_PERM(a) a +#else + #define USAGE_FIND_PERM(a) +#endif +#ifdef BB_FEATURE_FIND_MTIME + #define USAGE_FIND_MTIME(a) a +#else + #define USAGE_FIND_MTIME(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") +#define find_example_usage \ + "$ find / -name /etc/passwd\n" \ + "/etc/passwd\n" + +#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 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, --unqote 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 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-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 with maximum compression.\n" \ + "When FILE is '-', reads standard input. Implies -c.\n\n" \ + "Options:\n" \ + "\t-c\tWrite output to standard output instead of FILE.gz\n" \ + "\t-d\tdecompress" +#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 \ + "" +#define halt_full_usage \ + "Halt the system." + +#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" +#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 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\t\tShort\n" \ + "\t-i\t\tAddresses for the hostname\n" \ + "\t-d\t\tDNS domain name\n" \ + "\t-F, --file FILE\tUse the contents of FILE to specify the hostname" +#define hostname_example_usage \ + "$ hostname\n" \ + "sage \n" + +#define id_trivial_usage \ + "[OPTIONS]... [USERNAME]" +#define id_full_usage \ + "Print information for USERNAME or the current user\n\n" \ + "Options:\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 (with for -ug)\n" \ + "\t-r\tprints the real user ID instead of the effective ID (with -ug)" +#define id_example_usage \ + "$ id\n" \ + "uid=1000(andersen) gid=1000(andersen)\n" + +#ifdef BB_FEATURE_IFCONFIG_SLIP + #define USAGE_SIOCSKEEPALIVE(a) a +#else + #define USAGE_SIOCSKEEPALIVE(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + #define USAGE_IFCONFIG_MII(a) a +#else + #define USAGE_IFCONFIG_MII(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_HW + #define USAGE_IFCONFIG_HW(a) a +#else + #define USAGE_IFCONFIG_HW(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_STATUS + #define USAGE_IFCONFIG_OPT_A(a) a +#else + #define USAGE_IFCONFIG_OPT_A(a) +#endif + +#define ifconfig_trivial_usage \ + USAGE_IFCONFIG_OPT_A("[-a]") " [
]" +#define ifconfig_full_usage \ + "configure a network interface\n\n" \ + "Options:\n" \ + "\t[[-]broadcast [
]] [[-]pointopoint [
]]\n" \ + "\t[netmask
] [dstaddr
]\n" \ + USAGE_SIOCSKEEPALIVE("\t[outfill ] [keepalive ]\n") \ + "\t" USAGE_IFCONFIG_HW("[hw ether
] ") \ + "[metric ] [mtu ]\n" \ + "\t[[-]trailers] [[-]arp] [[-]allmulti]\n" \ + "\t[multicast] [[-]promisc] [txqueuelen ] [[-]dynamic]\n" \ + USAGE_IFCONFIG_MII("\t[mem_start ] [io_addr ] [irq ]\n") \ + "\t[up|down] ..." + +#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" \ +"\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" \ +" :::\n" \ +"\n" \ +" : \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" \ +" : \n" \ +"\n" \ +" The runlevels field is completely ignored.\n" \ +"\n" \ +" : \n" \ +"\n" \ +" Valid actions include: sysinit, respawn, askfirst, wait, \n" \ +" once, 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. 'ctrlaltdel'\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 reverse order from how they appear in /etc/inittab.\n" \ +"\n" \ +" : \n" \ +"\n" \ +" Specifies the process to be executed and it's 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 tty5\n" \ +" tty5::respawn:/sbin/getty 38400 tty6\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 before rebooting\n" \ +" ::ctrlaltdel:/sbin/reboot\n" \ +" ::shutdown:/bin/umount -a -r\n" \ +" ::shutdown:/sbin/swapoff -a\n" + +#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-L\tLock to prevent simultaneous loads of a module\n" \ + "\t-x\tdo not export externs" + +#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 \ + "[-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." +#define killall_example_usage \ + "$ killall apache\n" + +#define klogd_trivial_usage \ + "-n" +#define klogd_full_usage \ + "Kernel logger.\n"\ + "Options:\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 loadacm_trivial_usage \ + "< mapfile" +#define loadacm_full_usage \ + "Loads an acm from standard input." +#define loadacm_example_usage \ + "$ loadacm < /etc/i18n/acmname\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\tLog using the specified tag (defaults to user name).\n" \ + "\t-p\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 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 \ + "" + +#define logread_full_usage \ + "Shows the messages from syslogd (using circular buffer)." + +#ifdef BB_FEATURE_LS_TIMESTAMPS + #define USAGE_LS_TIMESTAMPS(a) a +#else + #define USAGE_LS_TIMESTAMPS(a) +#endif +#ifdef BB_FEATURE_LS_FILETYPES + #define USAGE_LS_FILETYPES(a) a +#else + #define USAGE_LS_FILETYPES(a) +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS + #define USAGE_LS_FOLLOWLINKS(a) a +#else + #define USAGE_LS_FOLLOWLINKS(a) +#endif +#ifdef BB_FEATURE_LS_RECURSIVE + #define USAGE_LS_RECURSIVE(a) a +#else + #define USAGE_LS_RECURSIVE(a) +#endif +#ifdef BB_FEATURE_LS_SORTFILES + #define USAGE_LS_SORTFILES(a) a +#else + #define USAGE_LS_SORTFILES(a) +#endif +#ifdef BB_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] [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" \ + "\t-k\tprint sizes in kilobytes(default)") USAGE_NOT_HUMAN_READABLE( \ + "\t-k\tprint sizes in kilobytes(compatibility)") + +#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" + +#define md5sum_trivial_usage \ + "[OPTION] [FILE]...\n" \ + "or: md5sum [OPTION] -c [FILE]" +#define md5sum_full_usage \ + "Print or check MD5 checksums.\n\n" \ + "Options:\n" \ + "With no FILE, or when FILE is -, read standard input.\n\n" \ + "\t-b\tread files in binary mode\n" \ + "\t-c\tcheck MD5 sums against given list\n" \ + "\t-t\tread files in text mode (default)\n" \ + "\t-g\tread a string\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 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 \ + "[-q] 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)." +#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 \ + "[FILE ...]" +#define modprobe_full_usage \ + "Used for hight level module loading and unloading." +#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 BB_FEATURE_MOUNT_LOOP + #define USAGE_MOUNT_LOOP(a) a +#else + #define USAGE_MOUNT_LOOP(a) +#endif +#ifdef BB_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\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 rew rewoffline seek setblk setdensity\n" \ + "setpart tell unload unlock weof wset" + +#define mv_trivial_usage \ + "SOURCE DEST\n" \ + "or: mv SOURCE... DIRECTORY" +#define mv_full_usage \ + "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY." +#define mv_example_usage \ + "$ mv /tmp/foo /bin/bar\n" + +#define nc_trivial_usage \ + "[IP] [port]" +#define nc_full_usage \ + "Netcat opens a pipe to IP:port" +#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 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 pidof_trivial_usage \ + "process-name [process-name ...]" +#define pidof_full_usage \ + "Lists the PIDs of all processes with names that match the names on the command line" +#define pidof_example_usage \ + "$ pidof init\n" \ + "1\n" + +#ifndef BB_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" + +#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 \ + "" +#define poweroff_full_usage \ + "Halt the system and request that the kernel shut off the power." + +#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" + +#define ps_trivial_usage \ + "" +#define ps_full_usage \ + "Report process status\n" \ + "\nThis version of ps accepts no options." +#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 \ + "[OPTION] 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 \ + "Read a symbolic link." + +#define reboot_trivial_usage \ + "" +#define reboot_full_usage \ + "Reboot the system." + +#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" \ + "\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\tTry to remove all unused kernel modules." +#define rmmod_example_usage \ + "$ rmmod tulip\n" + +#define route_trivial_usage \ + "[{add|del|flush}]" +#define route_full_usage \ + "Edit the kernel's routing tables" + +#define rpm2cpio_trivial_usage \ + "package.rpm" +#define rpm2cpio_full_usage \ + "Outputs a cpio archive of the rpm file." + +#define rpmunpack_trivial_usage \ + "< package.rpm | gunzip | cpio -idmuv" +#define rpmunpack_full_usage \ + "Extracts an rpm archive." + +#define sed_trivial_usage \ + "[-nef] pattern [files...]" +#define sed_full_usage \ + "Options:\n" \ + "\t-n\t\tsuppress automatic printing of pattern space\n" \ + "\t-e script\tadd the script to the commands to be executed\n" \ + "\t-f scriptfile\tadd the contents of script-file to the commands to be executed\n" \ + "\n" \ + "If no -e or -f is given, the first non-option argument is taken as the\n" \ + "sed script to interpret. All remaining arguments are names of input\n" \ + "files; if no input files are specified, then the standard input is read." +#define sed_example_usage \ + "$ echo "foo" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \ + "bar\n" + +#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 \ + "lash: 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 sleep_trivial_usage \ + "N" +#define sleep_full_usage \ + "Pause for N seconds." +#define sleep_example_usage \ + "$ sleep 2\n" \ + "[2 second delay results]\n" + + +#ifdef BB_FEATURE_SORT_UNIQUE + #define USAGE_SORT_UNIQUE(a) a +#else + #define USAGE_SORT_UNIQUE(a) +#endif +#ifdef BB_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 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 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 BB_FEATURE_REMOTE_LOG + #define USAGE_REMOTE_LOG(a) a +#else + #define USAGE_REMOTE_LOG(a) +#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 NUM\t\tInterval between MARK lines (default=20min, 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)" \ + 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)") +#define syslogd_example_usage \ + "$ syslogd -R masterlog:514\n" \ + "$ syslogd -R 192.168.1.1:601\n" + + +#ifndef BB_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 BB_FEATURE_TAR_CREATE + #define USAGE_TAR_CREATE(a) a +#else + #define USAGE_TAR_CREATE(a) +#endif +#ifdef BB_FEATURE_TAR_EXCLUDE + #define USAGE_TAR_EXCLUDE(a) a +#else + #define USAGE_TAR_EXCLUDE(a) +#endif +#define tar_trivial_usage \ + "-[" USAGE_TAR_CREATE("c") "xtvO] " \ + USAGE_TAR_EXCLUDE("[--exclude FILE] [-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" \ + "\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" +#define tee_example_usage \ + "$ echo "Hello" | tee /tmp/foo\n" \ + "$ cat /tmp/foo\n" \ + "Hello\n" + +#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." + +#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 BB_FEATURE_TFTP_GET + #define USAGE_TFTP_GET(a) a +#else + #define USAGE_TFTP_GET(a) +#endif +#ifdef BB_FEATURE_TFTP_PUT + #define USAGE_TFTP_PUT(a) a +#else + #define USAGE_TFTP_PUT(a) +#endif + +#define tftp_trivial_usage \ + "command SOURCE DEST" +#define tftp_full_usage \ + "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ + "Commands:\n" \ + USAGE_TFTP_GET( \ + "\tget\tGet file from server SOURCE and store to local DEST.\n" \ + ) \ + USAGE_TFTP_PUT( \ + "\tput\tPut local file SOURCE to server DEST.\n" \ + ) \ + "\nWhen naming a server, use the syntax \"server:file\"." + +#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\ + [-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" + +#ifdef BB_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.2.15pre13 #5 Tue Mar 14 16:03:50 MST 2000 i686 unknown\n" + +#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" +#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 update_trivial_usage \ + "[options]" +#define update_full_usage \ + "Periodically flushes filesystem buffers.\n\n" \ + "Options:\n" \ + "\t-S\tforce use of sync(2) instead of flushing\n" \ + "\t-s SECS\tcall sync this often (default 30)\n" \ + "\t-f SECS\tflush some buffers this often (default 5)" + +#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" \ + "\n" \ + "$ uudecode busybox busybox > busybox.uu\n" \ + "$\n" + +#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 watchdog_trivial_usage \ + "DEV" +#define watchdog_full_usage \ + "Periodically write to watchdog device DEV" + +#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[--header 'header: value'] [-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)" + +#define which_trivial_usage \ + "[COMMAND ...]" +#define which_full_usage \ + "Locates a COMMAND." +#define which_example_usage \ + "$ which login\n" \ + "/bin/login\n" + +#define whoami_trivial_usage \ + "" +#define whoami_full_usage \ + "Prints the user name associated with the current effective user id." + +#define xargs_trivial_usage \ + "[COMMAND] [ARGS...]" +#define xargs_full_usage \ + "Executes COMMAND on every item given by standard input." +#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." diff --git a/busybox/init.c b/busybox/init.c new file mode 100644 index 000000000..f397b7e0a --- /dev/null +++ b/busybox/init.c @@ -0,0 +1,1040 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini init implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#ifdef BB_SYSLOGD +# include +#endif + + +/* From */ +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 */ +struct serial_struct { + int type; + int line; + int port; + int irq; + int flags; + int xmit_fifo_size; + int custom_divisor; + int baud_base; + unsigned short close_delay; + char reserved_char[2]; + int hub6; + unsigned short closing_wait; /* time to wait before closing */ + unsigned short closing_wait2; /* no longer used... */ + int reserved[4]; +}; + + + +#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 + +#if (__GNU_LIBRARY__ > 5) || defined(__dietlibc__) + #include + #define init_reboot(magic) reboot(magic) +#else + #define init_reboot(magic) reboot(0xfee1dead, 672274793, magic) +#endif + +#ifndef _PATH_STDPATH +#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" +#endif + + +#if defined BB_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 +#include +#endif + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#if __GNU_LIBRARY__ > 5 + #include +#else + extern int bdflush (int func, long int data); +#endif + + +#define SHELL "/bin/sh" /* Default shell */ +#define LOGIN_SHELL "-" SHELL /* Default login shell */ +#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 */ +//static const int MAXENV = 16; /* Number of env. vars */ +static const int LOG = 0x1; +static const int CONSOLE = 0x2; + +/* Allowed init action types */ +typedef enum { + SYSINIT = 1, + RESPAWN, + ASKFIRST, + WAIT, + ONCE, + CTRLALTDEL, + SHUTDOWN +} initActionEnum; + +/* A mapping between "inittab" action name strings and action type codes. */ +typedef struct initActionType { + const char *name; + initActionEnum action; +} initActionType; + +static const struct initActionType actions[] = { + {"sysinit", SYSINIT}, + {"respawn", RESPAWN}, + {"askfirst", ASKFIRST}, + {"wait", WAIT}, + {"once", ONCE}, + {"ctrlaltdel", CTRLALTDEL}, + {"shutdown", SHUTDOWN}, + {0, 0} +}; + +/* Set up a linked list of initActions, to be read from inittab */ +typedef struct initActionTag initAction; +struct initActionTag { + pid_t pid; + char process[256]; + char console[256]; + initAction *nextPtr; + initActionEnum action; +}; +static initAction *initActionList = NULL; + + +static char *secondConsole = VC_2; +static char *thirdConsole = VC_3; +static char *fourthConsole = VC_4; +static char *log = VC_5; +static int kernelVersion = 0; +static char termType[32] = "TERM=linux"; +static char console[32] = _PATH_CONSOLE; + +static void delete_initAction(initAction * action); + +static void loop_forever() +{ + while (1) + sleep (1); +} + +/* Print a message to the specified device. + * Device may be bitwise-or'd from LOG | CONSOLE */ +static void message(int device, char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +static void message(int device, char *fmt, ...) +{ + va_list arguments; + int fd; + +#ifdef BB_SYSLOGD + + /* Log the message to syslogd */ + if (device & LOG) { + char msg[1024]; + + va_start(arguments, fmt); + vsnprintf(msg, sizeof(msg), fmt, arguments); + va_end(arguments); + openlog(applet_name, 0, LOG_USER); + syslog(LOG_USER|LOG_INFO, msg); + closelog(); + } +#else + static int log_fd = -1; + + /* Take full control of the log tty, and never close it. + * It's mine, all mine! Muhahahaha! */ + if (log_fd < 0) { + if (log == NULL) { + /* don't even try to log, because there is no such console */ + log_fd = -2; + /* log to main console instead */ + device = CONSOLE; + } else if ((log_fd = device_open(log, O_RDWR|O_NDELAY)) < 0) { + log_fd = -2; + fprintf(stderr, "Bummer, can't write to log on %s!\r\n", log); + log = NULL; + device = CONSOLE; + } + } + if ((device & LOG) && (log_fd >= 0)) { + va_start(arguments, fmt); + vdprintf(log_fd, fmt, arguments); + va_end(arguments); + } +#endif + + if (device & CONSOLE) { + /* Always send console messages to /dev/console so people will see them. */ + if ( + (fd = + device_open(_PATH_CONSOLE, + O_WRONLY | O_NOCTTY | O_NDELAY)) >= 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); + va_end(arguments); + } + } +} + +/* 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 int check_free_memory() +{ + struct sysinfo info; + unsigned int result, u, s=10; + + if (sysinfo(&info) != 0) { + 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); + result = result*u; + if (result < 0) result = INT_MAX; + return result; +} + +static void console_init() +{ + int fd; + int tried_devcons = 0; + int tried_vtprimary = 0; + struct vt_stat vt; + struct serial_struct sr; + char *s; + + if ((s = getenv("TERM")) != NULL) { + snprintf(termType, sizeof(termType) - 1, "TERM=%s", s); + } + + if ((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 */ + else if ((s = getenv("console")) != NULL) { + /* 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_devcons++; + } + } + + while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0) { + /* Can't open selected console -- try /dev/console */ + if (!tried_devcons) { + tried_devcons++; + safe_strncpy(console, _PATH_CONSOLE, sizeof(console)); + continue; + } + /* Can't open selected console -- try vt1 */ + if (!tried_vtprimary) { + tried_vtprimary++; + safe_strncpy(console, VC_1, sizeof(console)); + continue; + } + break; + } + if (fd < 0) { + /* Perhaps we should panic here? */ + safe_strncpy(console, "/dev/null", sizeof(console)); + } else { + /* check for serial console and disable logging to tty5 & running a + * shell to tty2-4 */ + if (ioctl(0, TIOCGSERIAL, &sr) == 0) { + log = NULL; + secondConsole = NULL; + thirdConsole = NULL; + fourthConsole = NULL; + /* Force the TERM setting to vt102 for serial console -- + * iff TERM is set to linux (the default) */ + if (strcmp( termType, "TERM=linux" ) == 0) + safe_strncpy(termType, "TERM=vt102", sizeof(termType)); + message(LOG | CONSOLE, + "serial console detected. Disabling virtual terminals.\r\n"); + } + close(fd); + } + message(LOG, "console=%s\n", 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); + strncpy(argv[0], new_argv0, len); + + /* 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(char *command, char *terminal, int get_enter) +{ + int i, j; + int fd; + pid_t pid; + char *tmpCmd, *s; + char *cmd[255], *cmdpath; + char buf[255]; + struct stat sb; + static const char press_enter[] = + +#ifdef CUSTOMIZED_BANNER +#include CUSTOMIZED_BANNER +#endif + + "\nPlease press Enter to activate this console. "; + char *environment[MAXENV+1] = { + termType, + "HOME=/", + "PATH=/usr/bin:/bin:/usr/sbin:/sbin", + "SHELL=" SHELL, + "USER=root", + NULL + }; + + /* inherit environment to the child, merging our values -andy */ + for (i=0; environ[i]; i++) { + for (j=0; environment[j]; j++) { + s = strchr(environment[j], '='); + if (!strncmp(environ[i], environment[j], s - environment[j])) + break; + } + if (!environment[j]) { + environment[j++] = environ[i]; + environment[j] = NULL; + } + } + + if ((pid = fork()) == 0) { + /* Clean up */ + ioctl(0, TIOCNOTTY, 0); + close(0); + close(1); + close(2); + setsid(); + + /* Reset signal handlers set for parent process */ + signal(SIGUSR1, SIG_DFL); + signal(SIGUSR2, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + + if ((fd = device_open(terminal, O_RDWR)) < 0) { + if (stat(terminal, &sb) != 0) { + message(LOG | CONSOLE, "device '%s' does not exist.\n", + terminal); + exit(1); + } + message(LOG | CONSOLE, "Bummer, can't open %s\r\n", terminal); + exit(1); + } + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + ioctl(0, TIOCSCTTY, 1); + tcsetpgrp(0, getpgrp()); + set_term(0); + + /* See if any special /bin/sh requiring characters are present */ + if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { + cmd[0] = SHELL; + cmd[1] = "-c"; + strcpy(buf, "exec "); + strncat(buf, command, sizeof(buf) - strlen(buf) - 1); + cmd[2] = buf; + cmd[3] = NULL; + } else { + /* Convert command (char*) into cmd (char**, one word per string) */ + for (tmpCmd = command, i = 0; + (tmpCmd = strsep(&command, " \t")) != NULL;) { + if (*tmpCmd != '\0') { + cmd[i] = tmpCmd; + 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 = get_last_path_component(cmdpath); + + /* make a new argv[0] */ + if ((cmd[0] = malloc(strlen(s)+2)) == NULL) { + message(LOG | CONSOLE, "malloc failed"); + cmd[0] = cmdpath; + } else { + cmd[0][0] = '-'; + strcpy(cmd[0]+1, s); + } + } + + if (get_enter == TRUE) { + /* + * 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. + */ +#ifdef DEBUG_INIT + message(LOG, "Waiting for enter to start '%s' (pid %d, console %s)\r\n", + cmd[0], getpid(), terminal); +#endif + write(fileno(stdout), press_enter, sizeof(press_enter) - 1); + getc(stdin); + } + +#ifdef DEBUG_INIT + /* Log the process name and args */ + message(LOG, "Starting pid %d, console %s: '%s'\r\n", + getpid(), terminal, command); +#endif + +#if defined BB_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. */ + execve(cmdpath, cmd, environment); + + /* We're still here? Some error happened. */ + message(LOG | CONSOLE, "Bummer, could not run '%s': %s\n", cmdpath, + strerror(errno)); + exit(-1); + } + return pid; +} + +static int waitfor(char *command, char *terminal, int get_enter) +{ + int status, wpid; + int pid = run(command, terminal, get_enter); + + while (1) { + wpid = wait(&status); + if (wpid > 0 && wpid != pid) { + continue; + } + if (wpid == pid) + break; + } + return wpid; +} + +/* 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() +{ + struct stat statBuf; + + if (check_free_memory() > 1000) + return; + + if (stat("/etc/fstab", &statBuf) == 0) { + /* swapon -a requires /proc typically */ + waitfor("mount proc /proc -t proc", console, FALSE); + /* Try to turn on swap */ + waitfor("swapon -a", console, FALSE); + if (check_free_memory() < 1000) + goto goodnight; + } else + goto goodnight; + return; + + goodnight: + message(CONSOLE, + "Sorry, your computer does not have enough memory.\r\n"); + loop_forever(); +} + +/* Run all commands to be run right before halt/reboot */ +static void run_actions(initActionEnum action) +{ + initAction *a, *tmp; + for (a = initActionList; a; a = tmp) { + tmp = a->nextPtr; + if (a->action == action) { + waitfor(a->process, a->console, FALSE); + delete_initAction(a); + } + } +} + + +#ifndef DEBUG_INIT +static void shutdown_system(void) +{ + + /* first disable our SIGHUP signal */ + signal(SIGHUP, SIG_DFL); + + /* Allow Ctrl-Alt-Del to reboot system. */ + init_reboot(RB_ENABLE_CAD); + + message(CONSOLE|LOG, "\r\nThe system is going down NOW !!\r\n"); + sync(); + + /* Send signals to every process _except_ pid 1 */ + message(CONSOLE|LOG, "Sending SIGTERM to all processes.\r\n"); + kill(-1, SIGTERM); + sleep(1); + sync(); + + message(CONSOLE|LOG, "Sending SIGKILL to all processes.\r\n"); + kill(-1, SIGKILL); + sleep(1); + + /* run everything to be run at "shutdown" */ + run_actions(SHUTDOWN); + + sync(); + if (kernelVersion > 0 && kernelVersion <= KERNEL_VERSION(2,2,11)) { + /* bdflush, kupdate not needed for kernels >2.2.11 */ + bdflush(1, 0); + sync(); + } +} + +static void halt_signal(int sig) +{ + shutdown_system(); + message(CONSOLE|LOG, + "The system is halted. Press %s or turn off power\r\n", + (secondConsole == NULL) /* serial console */ + ? "Reset" : "CTRL-ALT-DEL"); + sync(); + + /* allow time for last message to reach serial console */ + sleep(2); + + if (sig == SIGUSR2 && kernelVersion >= KERNEL_VERSION(2,2,0)) + 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.\r\n"); + 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); +} + +#endif /* ! DEBUG_INIT */ + +static void new_initAction(initActionEnum action, char *process, char *cons) +{ + initAction *newAction; + + if (*cons == '\0') + cons = console; + + /* If BusyBox detects that a serial console is in use, + * then entries not refering to the console or null devices will _not_ be run. + * The exception to this rule is the null device. + */ + if (secondConsole == NULL && strcmp(cons, console) + && strcmp(cons, "/dev/null")) + return; + + newAction = calloc((size_t) (1), sizeof(initAction)); + if (!newAction) { + message(LOG | CONSOLE, "Memory allocation failure\n"); + loop_forever(); + } + newAction->nextPtr = initActionList; + initActionList = newAction; + strncpy(newAction->process, process, 255); + newAction->action = action; + strncpy(newAction->console, cons, 255); + newAction->pid = 0; +// message(LOG|CONSOLE, "process='%s' action='%d' console='%s'\n", +// newAction->process, newAction->action, newAction->console); +} + +static void delete_initAction(initAction * action) +{ + initAction *a, *b = NULL; + + for (a = initActionList; a; b = a, a = a->nextPtr) { + if (a == action) { + if (b == NULL) { + initActionList = a->nextPtr; + } else { + b->nextPtr = a->nextPtr; + } + free(a); + break; + } + } +} + +/* NOTE that if BB_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 BB_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 BB_FEATURE_USE_INITTAB + FILE *file; + char buf[256], lineAsRead[256], tmpConsole[256]; + char *id, *runlev, *action, *process, *eol; + const struct initActionType *a = actions; + int foundIt; + + + file = fopen(INITTAB, "r"); + if (file == NULL) { + /* No inittab file -- set up some default behavior */ +#endif + /* Reboot on Ctrl-Alt-Del */ + new_initAction(CTRLALTDEL, "/sbin/reboot", console); + /* Swapoff on halt/reboot */ + new_initAction(SHUTDOWN, "/sbin/swapoff -a", console); + /* Umount all filesystems on halt/reboot */ + new_initAction(SHUTDOWN, "/bin/umount -a -r", console); + /* Askfirst shell on tty1 */ + new_initAction(ASKFIRST, LOGIN_SHELL, console); + /* Askfirst shell on tty2 */ + if (secondConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, secondConsole); + /* Askfirst shell on tty3 */ + if (thirdConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, thirdConsole); + /* Askfirst shell on tty4 */ + if (fourthConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, fourthConsole); + /* sysinit */ + new_initAction(SYSINIT, INIT_SCRIPT, console); + + return; +#ifdef BB_FEATURE_USE_INITTAB + } + + while (fgets(buf, 255, file) != NULL) { + foundIt = FALSE; + /* 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\n", 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\n", lineAsRead); + continue; + } else { + *action = '\0'; + ++action; + } + + /* Separate the action from the process */ + process = strchr(action, ':'); + if (process == NULL || *(process + 1) == '\0') { + message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); + continue; + } else { + *process = '\0'; + ++process; + } + + /* Ok, now process it */ + a = actions; + while (a->name != 0) { + if (strcmp(a->name, action) == 0) { + if (*id != '\0') { + strcpy(tmpConsole, "/dev/"); + strncat(tmpConsole, id, 200); + id = tmpConsole; + } + new_initAction(a->action, process, id); + foundIt = TRUE; + } + a++; + } + if (foundIt == TRUE) + continue; + else { + /* Choke on an unknown action */ + message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); + } + } + return; +#endif /* BB_FEATURE_USE_INITTAB */ +} + + + +extern int init_main(int argc, char **argv) +{ + initAction *a, *tmp; + pid_t wpid; + int status; + +#ifndef DEBUG_INIT + /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ + if (getpid() != 1 +#ifdef BB_FEATURE_LINUXRC + && strstr(applet_name, "linuxrc") == NULL +#endif + ) + { + show_usage(); + } + /* Set up sig handlers -- be sure to + * clear all of these in run() */ + signal(SIGUSR1, halt_signal); + signal(SIGUSR2, halt_signal); + signal(SIGINT, ctrlaltdel_signal); + signal(SIGTERM, reboot_signal); + + /* 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 what kernel this is running */ + kernelVersion = get_kernel_revision(); + + /* 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); + set_term(0); + chdir("/"); + setsid(); + + /* Make sure PATH is set to something sane */ + putenv("PATH="_PATH_STDPATH); + + /* Hello world */ +#ifndef DEBUG_INIT + message( +#if ! defined BB_FEATURE_EXTRA_QUIET + CONSOLE| +#endif + LOG, + "init started: %s\r\n", full_version); +#else + message( +#if ! defined BB_FEATURE_EXTRA_QUIET + CONSOLE| +#endif + LOG, + "init(%d) started: %s\r\n", getpid(), full_version); +#endif + + + /* 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"))) { + /* Ask first then start a shell on tty2-4 */ + if (secondConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, secondConsole); + if (thirdConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, thirdConsole); + if (fourthConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, fourthConsole); + /* Start a shell on tty1 */ + new_initAction(RESPAWN, LOGIN_SHELL, console); + } else { + /* Not in single user mode -- see what inittab says */ + + /* NOTE that if BB_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 */ + for (a = initActionList; a; a = tmp) { + tmp = a->nextPtr; + if (a->action == SYSINIT) { + waitfor(a->process, a->console, FALSE); + /* Now remove the "sysinit" entry from the list */ + delete_initAction(a); + } + } + /* Next run anything that wants to block */ + for (a = initActionList; a; a = tmp) { + tmp = a->nextPtr; + if (a->action == WAIT) { + waitfor(a->process, a->console, FALSE); + /* Now remove the "wait" entry from the list */ + delete_initAction(a); + } + } + /* Next run anything to be run only once */ + for (a = initActionList; a; a = tmp) { + tmp = a->nextPtr; + if (a->action == ONCE) { + run(a->process, a->console, FALSE); + /* Now remove the "once" entry from the list */ + delete_initAction(a); + } + } + /* If there is nothing else to do, stop */ + if (initActionList == NULL) { + message(LOG | CONSOLE, + "No more tasks for init -- sleeping forever.\n"); + loop_forever(); + } + + /* Now run the looping stuff for the rest of forever */ + while (1) { + for (a = initActionList; a; a = a->nextPtr) { + /* Only run stuff with pid==0. If they have + * a pid, that means they are still running */ + if (a->pid == 0) { + switch (a->action) { + case RESPAWN: + /* run the respawn stuff */ + a->pid = run(a->process, a->console, FALSE); + break; + case ASKFIRST: + /* run the askfirst stuff */ + a->pid = run(a->process, a->console, TRUE); + break; + /* silence the compiler's incessant whining */ + default: + break; + } + } + } + /* Wait for a child process to exit */ + wpid = wait(&status); + if (wpid > 0) { + /* Find out who died and clean up their corpse */ + for (a = initActionList; a; a = a->nextPtr) { + if (a->pid == wpid) { + a->pid = 0; + message(LOG, + "Process '%s' (pid %d) exited. Scheduling it for restart.\n", + a->process, wpid); + } + } + } + sleep(1); + } +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/init/halt.c b/busybox/init/halt.c new file mode 100644 index 000000000..10dcb4225 --- /dev/null +++ b/busybox/init/halt.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini halt implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +extern int halt_main(int argc, char **argv) +{ +#ifdef BB_FEATURE_LINUXRC + /* don't assume init's pid == 1 */ + pid_t *pid = find_pid_by_name("init"); + if (!pid || *pid<=0) + error_msg_and_die("no process killed"); + return(kill(*pid, SIGUSR1)); +#else + return(kill(1, SIGUSR1)); +#endif +} diff --git a/busybox/init/init.c b/busybox/init/init.c new file mode 100644 index 000000000..f397b7e0a --- /dev/null +++ b/busybox/init/init.c @@ -0,0 +1,1040 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini init implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#ifdef BB_SYSLOGD +# include +#endif + + +/* From */ +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 */ +struct serial_struct { + int type; + int line; + int port; + int irq; + int flags; + int xmit_fifo_size; + int custom_divisor; + int baud_base; + unsigned short close_delay; + char reserved_char[2]; + int hub6; + unsigned short closing_wait; /* time to wait before closing */ + unsigned short closing_wait2; /* no longer used... */ + int reserved[4]; +}; + + + +#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 + +#if (__GNU_LIBRARY__ > 5) || defined(__dietlibc__) + #include + #define init_reboot(magic) reboot(magic) +#else + #define init_reboot(magic) reboot(0xfee1dead, 672274793, magic) +#endif + +#ifndef _PATH_STDPATH +#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" +#endif + + +#if defined BB_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 +#include +#endif + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#if __GNU_LIBRARY__ > 5 + #include +#else + extern int bdflush (int func, long int data); +#endif + + +#define SHELL "/bin/sh" /* Default shell */ +#define LOGIN_SHELL "-" SHELL /* Default login shell */ +#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 */ +//static const int MAXENV = 16; /* Number of env. vars */ +static const int LOG = 0x1; +static const int CONSOLE = 0x2; + +/* Allowed init action types */ +typedef enum { + SYSINIT = 1, + RESPAWN, + ASKFIRST, + WAIT, + ONCE, + CTRLALTDEL, + SHUTDOWN +} initActionEnum; + +/* A mapping between "inittab" action name strings and action type codes. */ +typedef struct initActionType { + const char *name; + initActionEnum action; +} initActionType; + +static const struct initActionType actions[] = { + {"sysinit", SYSINIT}, + {"respawn", RESPAWN}, + {"askfirst", ASKFIRST}, + {"wait", WAIT}, + {"once", ONCE}, + {"ctrlaltdel", CTRLALTDEL}, + {"shutdown", SHUTDOWN}, + {0, 0} +}; + +/* Set up a linked list of initActions, to be read from inittab */ +typedef struct initActionTag initAction; +struct initActionTag { + pid_t pid; + char process[256]; + char console[256]; + initAction *nextPtr; + initActionEnum action; +}; +static initAction *initActionList = NULL; + + +static char *secondConsole = VC_2; +static char *thirdConsole = VC_3; +static char *fourthConsole = VC_4; +static char *log = VC_5; +static int kernelVersion = 0; +static char termType[32] = "TERM=linux"; +static char console[32] = _PATH_CONSOLE; + +static void delete_initAction(initAction * action); + +static void loop_forever() +{ + while (1) + sleep (1); +} + +/* Print a message to the specified device. + * Device may be bitwise-or'd from LOG | CONSOLE */ +static void message(int device, char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +static void message(int device, char *fmt, ...) +{ + va_list arguments; + int fd; + +#ifdef BB_SYSLOGD + + /* Log the message to syslogd */ + if (device & LOG) { + char msg[1024]; + + va_start(arguments, fmt); + vsnprintf(msg, sizeof(msg), fmt, arguments); + va_end(arguments); + openlog(applet_name, 0, LOG_USER); + syslog(LOG_USER|LOG_INFO, msg); + closelog(); + } +#else + static int log_fd = -1; + + /* Take full control of the log tty, and never close it. + * It's mine, all mine! Muhahahaha! */ + if (log_fd < 0) { + if (log == NULL) { + /* don't even try to log, because there is no such console */ + log_fd = -2; + /* log to main console instead */ + device = CONSOLE; + } else if ((log_fd = device_open(log, O_RDWR|O_NDELAY)) < 0) { + log_fd = -2; + fprintf(stderr, "Bummer, can't write to log on %s!\r\n", log); + log = NULL; + device = CONSOLE; + } + } + if ((device & LOG) && (log_fd >= 0)) { + va_start(arguments, fmt); + vdprintf(log_fd, fmt, arguments); + va_end(arguments); + } +#endif + + if (device & CONSOLE) { + /* Always send console messages to /dev/console so people will see them. */ + if ( + (fd = + device_open(_PATH_CONSOLE, + O_WRONLY | O_NOCTTY | O_NDELAY)) >= 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); + va_end(arguments); + } + } +} + +/* 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 int check_free_memory() +{ + struct sysinfo info; + unsigned int result, u, s=10; + + if (sysinfo(&info) != 0) { + 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); + result = result*u; + if (result < 0) result = INT_MAX; + return result; +} + +static void console_init() +{ + int fd; + int tried_devcons = 0; + int tried_vtprimary = 0; + struct vt_stat vt; + struct serial_struct sr; + char *s; + + if ((s = getenv("TERM")) != NULL) { + snprintf(termType, sizeof(termType) - 1, "TERM=%s", s); + } + + if ((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 */ + else if ((s = getenv("console")) != NULL) { + /* 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_devcons++; + } + } + + while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0) { + /* Can't open selected console -- try /dev/console */ + if (!tried_devcons) { + tried_devcons++; + safe_strncpy(console, _PATH_CONSOLE, sizeof(console)); + continue; + } + /* Can't open selected console -- try vt1 */ + if (!tried_vtprimary) { + tried_vtprimary++; + safe_strncpy(console, VC_1, sizeof(console)); + continue; + } + break; + } + if (fd < 0) { + /* Perhaps we should panic here? */ + safe_strncpy(console, "/dev/null", sizeof(console)); + } else { + /* check for serial console and disable logging to tty5 & running a + * shell to tty2-4 */ + if (ioctl(0, TIOCGSERIAL, &sr) == 0) { + log = NULL; + secondConsole = NULL; + thirdConsole = NULL; + fourthConsole = NULL; + /* Force the TERM setting to vt102 for serial console -- + * iff TERM is set to linux (the default) */ + if (strcmp( termType, "TERM=linux" ) == 0) + safe_strncpy(termType, "TERM=vt102", sizeof(termType)); + message(LOG | CONSOLE, + "serial console detected. Disabling virtual terminals.\r\n"); + } + close(fd); + } + message(LOG, "console=%s\n", 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); + strncpy(argv[0], new_argv0, len); + + /* 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(char *command, char *terminal, int get_enter) +{ + int i, j; + int fd; + pid_t pid; + char *tmpCmd, *s; + char *cmd[255], *cmdpath; + char buf[255]; + struct stat sb; + static const char press_enter[] = + +#ifdef CUSTOMIZED_BANNER +#include CUSTOMIZED_BANNER +#endif + + "\nPlease press Enter to activate this console. "; + char *environment[MAXENV+1] = { + termType, + "HOME=/", + "PATH=/usr/bin:/bin:/usr/sbin:/sbin", + "SHELL=" SHELL, + "USER=root", + NULL + }; + + /* inherit environment to the child, merging our values -andy */ + for (i=0; environ[i]; i++) { + for (j=0; environment[j]; j++) { + s = strchr(environment[j], '='); + if (!strncmp(environ[i], environment[j], s - environment[j])) + break; + } + if (!environment[j]) { + environment[j++] = environ[i]; + environment[j] = NULL; + } + } + + if ((pid = fork()) == 0) { + /* Clean up */ + ioctl(0, TIOCNOTTY, 0); + close(0); + close(1); + close(2); + setsid(); + + /* Reset signal handlers set for parent process */ + signal(SIGUSR1, SIG_DFL); + signal(SIGUSR2, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + + if ((fd = device_open(terminal, O_RDWR)) < 0) { + if (stat(terminal, &sb) != 0) { + message(LOG | CONSOLE, "device '%s' does not exist.\n", + terminal); + exit(1); + } + message(LOG | CONSOLE, "Bummer, can't open %s\r\n", terminal); + exit(1); + } + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + ioctl(0, TIOCSCTTY, 1); + tcsetpgrp(0, getpgrp()); + set_term(0); + + /* See if any special /bin/sh requiring characters are present */ + if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { + cmd[0] = SHELL; + cmd[1] = "-c"; + strcpy(buf, "exec "); + strncat(buf, command, sizeof(buf) - strlen(buf) - 1); + cmd[2] = buf; + cmd[3] = NULL; + } else { + /* Convert command (char*) into cmd (char**, one word per string) */ + for (tmpCmd = command, i = 0; + (tmpCmd = strsep(&command, " \t")) != NULL;) { + if (*tmpCmd != '\0') { + cmd[i] = tmpCmd; + 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 = get_last_path_component(cmdpath); + + /* make a new argv[0] */ + if ((cmd[0] = malloc(strlen(s)+2)) == NULL) { + message(LOG | CONSOLE, "malloc failed"); + cmd[0] = cmdpath; + } else { + cmd[0][0] = '-'; + strcpy(cmd[0]+1, s); + } + } + + if (get_enter == TRUE) { + /* + * 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. + */ +#ifdef DEBUG_INIT + message(LOG, "Waiting for enter to start '%s' (pid %d, console %s)\r\n", + cmd[0], getpid(), terminal); +#endif + write(fileno(stdout), press_enter, sizeof(press_enter) - 1); + getc(stdin); + } + +#ifdef DEBUG_INIT + /* Log the process name and args */ + message(LOG, "Starting pid %d, console %s: '%s'\r\n", + getpid(), terminal, command); +#endif + +#if defined BB_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. */ + execve(cmdpath, cmd, environment); + + /* We're still here? Some error happened. */ + message(LOG | CONSOLE, "Bummer, could not run '%s': %s\n", cmdpath, + strerror(errno)); + exit(-1); + } + return pid; +} + +static int waitfor(char *command, char *terminal, int get_enter) +{ + int status, wpid; + int pid = run(command, terminal, get_enter); + + while (1) { + wpid = wait(&status); + if (wpid > 0 && wpid != pid) { + continue; + } + if (wpid == pid) + break; + } + return wpid; +} + +/* 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() +{ + struct stat statBuf; + + if (check_free_memory() > 1000) + return; + + if (stat("/etc/fstab", &statBuf) == 0) { + /* swapon -a requires /proc typically */ + waitfor("mount proc /proc -t proc", console, FALSE); + /* Try to turn on swap */ + waitfor("swapon -a", console, FALSE); + if (check_free_memory() < 1000) + goto goodnight; + } else + goto goodnight; + return; + + goodnight: + message(CONSOLE, + "Sorry, your computer does not have enough memory.\r\n"); + loop_forever(); +} + +/* Run all commands to be run right before halt/reboot */ +static void run_actions(initActionEnum action) +{ + initAction *a, *tmp; + for (a = initActionList; a; a = tmp) { + tmp = a->nextPtr; + if (a->action == action) { + waitfor(a->process, a->console, FALSE); + delete_initAction(a); + } + } +} + + +#ifndef DEBUG_INIT +static void shutdown_system(void) +{ + + /* first disable our SIGHUP signal */ + signal(SIGHUP, SIG_DFL); + + /* Allow Ctrl-Alt-Del to reboot system. */ + init_reboot(RB_ENABLE_CAD); + + message(CONSOLE|LOG, "\r\nThe system is going down NOW !!\r\n"); + sync(); + + /* Send signals to every process _except_ pid 1 */ + message(CONSOLE|LOG, "Sending SIGTERM to all processes.\r\n"); + kill(-1, SIGTERM); + sleep(1); + sync(); + + message(CONSOLE|LOG, "Sending SIGKILL to all processes.\r\n"); + kill(-1, SIGKILL); + sleep(1); + + /* run everything to be run at "shutdown" */ + run_actions(SHUTDOWN); + + sync(); + if (kernelVersion > 0 && kernelVersion <= KERNEL_VERSION(2,2,11)) { + /* bdflush, kupdate not needed for kernels >2.2.11 */ + bdflush(1, 0); + sync(); + } +} + +static void halt_signal(int sig) +{ + shutdown_system(); + message(CONSOLE|LOG, + "The system is halted. Press %s or turn off power\r\n", + (secondConsole == NULL) /* serial console */ + ? "Reset" : "CTRL-ALT-DEL"); + sync(); + + /* allow time for last message to reach serial console */ + sleep(2); + + if (sig == SIGUSR2 && kernelVersion >= KERNEL_VERSION(2,2,0)) + 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.\r\n"); + 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); +} + +#endif /* ! DEBUG_INIT */ + +static void new_initAction(initActionEnum action, char *process, char *cons) +{ + initAction *newAction; + + if (*cons == '\0') + cons = console; + + /* If BusyBox detects that a serial console is in use, + * then entries not refering to the console or null devices will _not_ be run. + * The exception to this rule is the null device. + */ + if (secondConsole == NULL && strcmp(cons, console) + && strcmp(cons, "/dev/null")) + return; + + newAction = calloc((size_t) (1), sizeof(initAction)); + if (!newAction) { + message(LOG | CONSOLE, "Memory allocation failure\n"); + loop_forever(); + } + newAction->nextPtr = initActionList; + initActionList = newAction; + strncpy(newAction->process, process, 255); + newAction->action = action; + strncpy(newAction->console, cons, 255); + newAction->pid = 0; +// message(LOG|CONSOLE, "process='%s' action='%d' console='%s'\n", +// newAction->process, newAction->action, newAction->console); +} + +static void delete_initAction(initAction * action) +{ + initAction *a, *b = NULL; + + for (a = initActionList; a; b = a, a = a->nextPtr) { + if (a == action) { + if (b == NULL) { + initActionList = a->nextPtr; + } else { + b->nextPtr = a->nextPtr; + } + free(a); + break; + } + } +} + +/* NOTE that if BB_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 BB_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 BB_FEATURE_USE_INITTAB + FILE *file; + char buf[256], lineAsRead[256], tmpConsole[256]; + char *id, *runlev, *action, *process, *eol; + const struct initActionType *a = actions; + int foundIt; + + + file = fopen(INITTAB, "r"); + if (file == NULL) { + /* No inittab file -- set up some default behavior */ +#endif + /* Reboot on Ctrl-Alt-Del */ + new_initAction(CTRLALTDEL, "/sbin/reboot", console); + /* Swapoff on halt/reboot */ + new_initAction(SHUTDOWN, "/sbin/swapoff -a", console); + /* Umount all filesystems on halt/reboot */ + new_initAction(SHUTDOWN, "/bin/umount -a -r", console); + /* Askfirst shell on tty1 */ + new_initAction(ASKFIRST, LOGIN_SHELL, console); + /* Askfirst shell on tty2 */ + if (secondConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, secondConsole); + /* Askfirst shell on tty3 */ + if (thirdConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, thirdConsole); + /* Askfirst shell on tty4 */ + if (fourthConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, fourthConsole); + /* sysinit */ + new_initAction(SYSINIT, INIT_SCRIPT, console); + + return; +#ifdef BB_FEATURE_USE_INITTAB + } + + while (fgets(buf, 255, file) != NULL) { + foundIt = FALSE; + /* 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\n", 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\n", lineAsRead); + continue; + } else { + *action = '\0'; + ++action; + } + + /* Separate the action from the process */ + process = strchr(action, ':'); + if (process == NULL || *(process + 1) == '\0') { + message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); + continue; + } else { + *process = '\0'; + ++process; + } + + /* Ok, now process it */ + a = actions; + while (a->name != 0) { + if (strcmp(a->name, action) == 0) { + if (*id != '\0') { + strcpy(tmpConsole, "/dev/"); + strncat(tmpConsole, id, 200); + id = tmpConsole; + } + new_initAction(a->action, process, id); + foundIt = TRUE; + } + a++; + } + if (foundIt == TRUE) + continue; + else { + /* Choke on an unknown action */ + message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); + } + } + return; +#endif /* BB_FEATURE_USE_INITTAB */ +} + + + +extern int init_main(int argc, char **argv) +{ + initAction *a, *tmp; + pid_t wpid; + int status; + +#ifndef DEBUG_INIT + /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ + if (getpid() != 1 +#ifdef BB_FEATURE_LINUXRC + && strstr(applet_name, "linuxrc") == NULL +#endif + ) + { + show_usage(); + } + /* Set up sig handlers -- be sure to + * clear all of these in run() */ + signal(SIGUSR1, halt_signal); + signal(SIGUSR2, halt_signal); + signal(SIGINT, ctrlaltdel_signal); + signal(SIGTERM, reboot_signal); + + /* 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 what kernel this is running */ + kernelVersion = get_kernel_revision(); + + /* 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); + set_term(0); + chdir("/"); + setsid(); + + /* Make sure PATH is set to something sane */ + putenv("PATH="_PATH_STDPATH); + + /* Hello world */ +#ifndef DEBUG_INIT + message( +#if ! defined BB_FEATURE_EXTRA_QUIET + CONSOLE| +#endif + LOG, + "init started: %s\r\n", full_version); +#else + message( +#if ! defined BB_FEATURE_EXTRA_QUIET + CONSOLE| +#endif + LOG, + "init(%d) started: %s\r\n", getpid(), full_version); +#endif + + + /* 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"))) { + /* Ask first then start a shell on tty2-4 */ + if (secondConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, secondConsole); + if (thirdConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, thirdConsole); + if (fourthConsole != NULL) + new_initAction(ASKFIRST, LOGIN_SHELL, fourthConsole); + /* Start a shell on tty1 */ + new_initAction(RESPAWN, LOGIN_SHELL, console); + } else { + /* Not in single user mode -- see what inittab says */ + + /* NOTE that if BB_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 */ + for (a = initActionList; a; a = tmp) { + tmp = a->nextPtr; + if (a->action == SYSINIT) { + waitfor(a->process, a->console, FALSE); + /* Now remove the "sysinit" entry from the list */ + delete_initAction(a); + } + } + /* Next run anything that wants to block */ + for (a = initActionList; a; a = tmp) { + tmp = a->nextPtr; + if (a->action == WAIT) { + waitfor(a->process, a->console, FALSE); + /* Now remove the "wait" entry from the list */ + delete_initAction(a); + } + } + /* Next run anything to be run only once */ + for (a = initActionList; a; a = tmp) { + tmp = a->nextPtr; + if (a->action == ONCE) { + run(a->process, a->console, FALSE); + /* Now remove the "once" entry from the list */ + delete_initAction(a); + } + } + /* If there is nothing else to do, stop */ + if (initActionList == NULL) { + message(LOG | CONSOLE, + "No more tasks for init -- sleeping forever.\n"); + loop_forever(); + } + + /* Now run the looping stuff for the rest of forever */ + while (1) { + for (a = initActionList; a; a = a->nextPtr) { + /* Only run stuff with pid==0. If they have + * a pid, that means they are still running */ + if (a->pid == 0) { + switch (a->action) { + case RESPAWN: + /* run the respawn stuff */ + a->pid = run(a->process, a->console, FALSE); + break; + case ASKFIRST: + /* run the askfirst stuff */ + a->pid = run(a->process, a->console, TRUE); + break; + /* silence the compiler's incessant whining */ + default: + break; + } + } + } + /* Wait for a child process to exit */ + wpid = wait(&status); + if (wpid > 0) { + /* Find out who died and clean up their corpse */ + for (a = initActionList; a; a = a->nextPtr) { + if (a->pid == wpid) { + a->pid = 0; + message(LOG, + "Process '%s' (pid %d) exited. Scheduling it for restart.\n", + a->process, wpid); + } + } + } + sleep(1); + } +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/init/poweroff.c b/busybox/init/poweroff.c new file mode 100644 index 000000000..8bb20e9bb --- /dev/null +++ b/busybox/init/poweroff.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini poweroff implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +extern int poweroff_main(int argc, char **argv) +{ +#ifdef BB_FEATURE_LINUXRC + /* don't assume init's pid == 1 */ + pid_t *pid = find_pid_by_name("init"); + if (!pid || *pid<=0) + error_msg_and_die("no process killed"); + return(kill(*pid, SIGUSR2)); +#else + return(kill(1, SIGUSR2)); +#endif +} diff --git a/busybox/init/reboot.c b/busybox/init/reboot.c new file mode 100644 index 000000000..35c147b34 --- /dev/null +++ b/busybox/init/reboot.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini reboot implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +extern int reboot_main(int argc, char **argv) +{ +#ifdef BB_FEATURE_LINUXRC + /* don't assume init's pid == 1 */ + pid_t *pid = find_pid_by_name("init"); + if (!pid || *pid<=0) + error_msg_and_die("no process killed"); + return(kill(*pid, SIGTERM)); +#else + return(kill(1, SIGTERM)); +#endif +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/insmod.c b/busybox/insmod.c new file mode 100644 index 000000000..1a63ecb2a --- /dev/null +++ b/busybox/insmod.c @@ -0,0 +1,3503 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini insmod implementation for busybox + * + * This version of insmod supports x86, ARM, SH3/4, powerpc, m68k, + * and MIPS. + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen + * and Ron Alder + * + * Modified by Bryan Rittmeyer to support SH4 + * and (theoretically) SH3. I have only tested SH4 in little endian mode. + * + * Modified by Alcove, Julien Gaulmin and + * Nicolas Ferre to support ARM7TDMI. Only + * very minor changes required to also work with StrongArm and presumably + * all ARM based systems. + * + * Magnus Damm 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 BB_USE_xxx_ENTRIES... + * + * Quinn Jensen 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 + * + * Based almost entirely on the Linux modutils-2.3.11 implementation. + * Copyright 1996, 1997 Linux International. + * New implementation contributed by Richard Henderson + * Based on original work by Bjorn Ekwall + * Restructured (and partly rewritten) by: + * Björn Ekwall 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#ifdef BB_FEATURE_NEW_MODULE_INTERFACE +# define new_sys_init_module init_module +#else +# define old_sys_init_module init_module +#endif + +#ifdef BB_FEATURE_INSMOD_LOADINKMEM +#define LOADBITS 0 +#else +#define LOADBITS 1 +#endif + +#if defined(__powerpc__) +#define BB_USE_PLT_ENTRIES +#define BB_PLT_ENTRY_SIZE 16 +#endif + +#if defined(__arm__) +#define BB_USE_PLT_ENTRIES +#define BB_PLT_ENTRY_SIZE 8 +#define BB_USE_GOT_ENTRIES +#define BB_GOT_ENTRY_SIZE 8 +#endif + +#if defined(__sh__) +#define BB_USE_GOT_ENTRIES +#define BB_GOT_ENTRY_SIZE 4 +#endif + +#if defined(__i386__) +#define BB_USE_GOT_ENTRIES +#define BB_GOT_ENTRY_SIZE 4 +#endif + +#if defined(__mips__) +// neither used +#endif + +//---------------------------------------------------------------------------- +//--------modutils module.h, lines 45-242 +//---------------------------------------------------------------------------- + +/* Definitions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson + + 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.70 2001/07/31 22:51:49 andersen Exp $" + +/* This file contains the structures used by the 2.0 and 2.1 kernels. + We do not use the kernel headers directly because we do not wish + to be dependant on a particular kernel version to compile insmod. */ + + +/*======================================================================*/ +/* The structures used by Linux 2.0. */ + +/* The symbol format used by get_kernel_syms(2). */ +struct old_kernel_sym +{ + unsigned long value; + char name[60]; +}; + +struct old_module_ref +{ + unsigned long module; /* kernel addresses */ + unsigned long next; +}; + +struct old_module_symbol +{ + unsigned long addr; + unsigned long name; +}; + +struct old_symbol_table +{ + int size; /* total, including string table!!! */ + int n_symbols; + int n_refs; + struct old_module_symbol symbol[0]; /* actual size defined by n_symbols */ + struct old_module_ref ref[0]; /* actual size defined by n_refs */ +}; + +struct old_mod_routines +{ + unsigned long init; + unsigned long cleanup; +}; + +struct old_module +{ + unsigned long next; + unsigned long ref; /* the list of modules that refer to me */ + unsigned long symtab; + unsigned long name; + int size; /* size of module in pages */ + unsigned long addr; /* address of module */ + int state; + unsigned long cleanup; /* cleanup routine */ +}; + +/* Sent to init_module(2) or'ed into the code size parameter. */ +static const int OLD_MOD_AUTOCLEAN = 0x40000000; /* big enough, but no sign problems... */ + +int get_kernel_syms(struct old_kernel_sym *); +int old_sys_init_module(const char *name, char *code, unsigned codesize, + struct old_mod_routines *, struct old_symbol_table *); + +/*======================================================================*/ +/* 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; +#ifdef BB_FEATURE_NEW_MODULE_INTERFACE + 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 */ +#endif +}; + +#define ARCHDATA_SEC_NAME "__archdata" +#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 new_sys_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 + + 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.70 2001/07/31 22:51:49 andersen Exp $" + +/* The relocatable object is manipulated using elfin types. */ + +#include +#include + + +/* Machine-specific elf macros for i386 et al. */ + +/* the SH changes have only been tested on the SH4 in =little endian= mode */ +/* I'm not sure about big endian, so let's warn: */ + +#if (defined(__SH4__) || defined(__SH3__)) && defined(__BIG_ENDIAN__) +#error insmod.c may require changes for use on big endian SH4/SH3 +#endif + +/* it may or may not work on the SH1/SH2... So let's error on those + also */ +#if (defined(__sh__) && (!(defined(__SH3__) || defined(__SH4__)))) +#error insmod.c may require changes for non-SH3/SH4 use +#endif + +#define ELFCLASSM ELFCLASS32 + +#if (defined(__mc68000__)) +#define ELFDATAM ELFDATA2MSB +#endif + + + +#if defined(__sh__) + +#define MATCH_MACHINE(x) (x == EM_SH) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFDATAM ELFDATA2LSB + +#elif defined(__arm__) + +#define MATCH_MACHINE(x) (x == EM_ARM) +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#define ELFDATAM ELFDATA2LSB + +#elif defined(__powerpc__) + +#define MATCH_MACHINE(x) (x == EM_PPC) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFDATAM ELFDATA2MSB + +#elif defined(__mips__) + +/* 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 MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE) +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#ifdef __MIPSEB__ +#define ELFDATAM ELFDATA2MSB +#endif +#ifdef __MIPSEL__ +#define ELFDATAM ELFDATA2LSB +#endif + +#elif defined(__i386__) + +/* presumably we can use these for anything but the SH and ARM*/ +/* this is the previous behavior, but it does result in + insmod.c being broken on anything except 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 ELFDATAM ELFDATA2LSB + +#elif defined(__mc68000__) + +#define MATCH_MACHINE(x) (x == EM_68K) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela + +#else +#error Sorry, but insmod.c does not yet support this architecture... +#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 libc5. */ +#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 BB_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 int arch_create_got (struct obj_file *f); + +static int arch_init_module (struct obj_file *f, struct new_module *); + +#endif /* obj.h */ +//---------------------------------------------------------------------------- +//--------end of modutils obj.h +//---------------------------------------------------------------------------- + + + + + +#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_export = 1; + + +/*======================================================================*/ + +/* previously, these were named i386_* but since we could be + compiling for the sh, I've renamed them to the more general + arch_* These structures are the same between the x86 and SH, + and we can't support anything else right now anyway. In the + future maybe they should be #if defined'd */ + +/* Done ;-) */ + + + +#if defined(BB_USE_PLT_ENTRIES) +struct arch_plt_entry +{ + int offset; + int allocated:1; + int inited:1; /* has been set up */ +}; +#endif + +#if defined(BB_USE_GOT_ENTRIES) +struct arch_got_entry { + int offset; + unsigned offset_done:1; + unsigned reloc_done: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(BB_USE_PLT_ENTRIES) + struct obj_section *plt; +#endif +#if defined(BB_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(BB_USE_PLT_ENTRIES) + struct arch_plt_entry pltent; +#endif +#if defined(BB_USE_GOT_ENTRIES) + struct arch_got_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[FILENAME_MAX + 1]; +static char m_fullName[FILENAME_MAX + 1]; + + + +/*======================================================================*/ + + +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 = strdup(filename); + tmp = get_last_path_component(tmp1); + if (strcmp(tmp, fullname) == 0) { + free(tmp1); + /* Stop searching if we find a match */ + safe_strncpy(m_filename, filename, sizeof(m_filename)); + return (TRUE); + } + free(tmp1); + } + return (FALSE); +} + + +/*======================================================================*/ + +static struct obj_file *arch_new_file(void) +{ + struct arch_file *f; + f = xmalloc(sizeof(*f)); + +#if defined(BB_USE_PLT_ENTRIES) + f->plt = NULL; +#endif +#if defined(BB_USE_GOT_ENTRIES) + f->got = NULL; +#endif +#if defined(__mips__) + f->mips_hi16_list = NULL; +#endif + + 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)); + +#if defined(BB_USE_PLT_ENTRIES) + memset(&sym->pltent, 0, sizeof(sym->pltent)); +#endif +#if defined(BB_USE_GOT_ENTRIES) + memset(&sym->gotent, 0, sizeof(sym->gotent)); +#endif + + 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; +#if !(defined(__mips__)) + struct arch_symbol *isym = (struct arch_symbol *) sym; +#endif + + ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset); + ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset; +#if defined(BB_USE_GOT_ENTRIES) + ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0; +#endif +#if defined(BB_USE_PLT_ENTRIES) + ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0; + struct arch_plt_entry *pe; + unsigned long *ip; +#endif + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) { + +/* even though these constants seem to be the same for + the i386 and the sh, we "#if define" them for clarity + and in case that ever changes */ +#if defined(__sh__) + case R_SH_NONE: +#elif defined(__arm__) + case R_ARM_NONE: +#elif defined(__i386__) + case R_386_NONE: +#elif defined(__mc68000__) + case R_68K_NONE: +#elif defined(__powerpc__) + case R_PPC_NONE: +#elif defined(__mips__) + case R_MIPS_NONE: +#endif + break; + +#if defined(__sh__) + case R_SH_DIR32: +#elif defined(__arm__) + case R_ARM_ABS32: +#elif defined(__i386__) + case R_386_32: +#elif defined(__mc68000__) + case R_68K_32: +#elif defined(__powerpc__) + case R_PPC_ADDR32: +#elif defined(__mips__) + case R_MIPS_32: +#endif + *loc += v; + break; +#if defined(__mc68000__) + 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; +#endif /* __mc68000__ */ + +#if 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; +#endif + +#if defined(__mips__) + 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; + } +#endif + +#if defined(__arm__) +#elif defined(__sh__) + case R_SH_REL32: + *loc += v - dot; + break; +#elif defined(__i386__) + case R_386_PLT32: + case R_386_PC32: + *loc += v - dot; + break; +#elif defined(__mc68000__) + 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; +#elif defined(__powerpc__) + case R_PPC_REL32: + *loc = v - dot; + break; +#endif + +#if defined(__sh__) + case R_SH_PLT32: + *loc = v - dot; + break; +#elif defined(__i386__) +#endif + +#if defined(BB_USE_PLT_ENTRIES) + +#if defined(__arm__) + case R_ARM_PC24: + case R_ARM_PLT32: +#endif +#if defined(__powerpc__) + case R_PPC_REL24: +#endif + /* find the plt entry and initialize it if necessary */ + assert(isym != NULL); + + pe = (struct arch_plt_entry*) &isym->pltent; + + 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 + pe->inited = 1; + } + + /* relative distance to target */ + v -= dot; + /* if the target is too far away.... */ + if ((int)v < -0x02000000 || (int)v >= 0x02000000) { + /* go via the plt */ + v = plt + pe->offset - dot; + } + if (v & 3) + 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 + break; +#endif /* BB_USE_PLT_ENTRIES */ + +#if defined(__arm__) +#elif defined(__sh__) + case R_SH_GLOB_DAT: + case R_SH_JMP_SLOT: + *loc = v; + break; +#elif defined(__i386__) + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + *loc = v; + break; +#elif defined(__mc68000__) + case R_68K_GLOB_DAT: + case R_68K_JMP_SLOT: + *loc = v; + break; +#endif + +#if defined(__arm__) +#elif defined(__sh__) + case R_SH_RELATIVE: + *loc += f->baseaddr + rel->r_addend; + break; +#elif defined(__i386__) + case R_386_RELATIVE: + *loc += f->baseaddr; + break; +#elif defined(__mc68000__) + case R_68K_RELATIVE: + *(int *)loc += f->baseaddr; + break; +#endif + +#if defined(BB_USE_GOT_ENTRIES) + +#if !defined(__68k__) +#if defined(__sh__) + case R_SH_GOTPC: +#elif defined(__arm__) + case R_ARM_GOTPC: +#elif defined(__i386__) + case R_386_GOTPC: +#endif + assert(got != 0); +#if defined(__sh__) + *loc += got - dot + rel->r_addend;; +#elif defined(__i386__) || defined(__arm__) || defined(__m68k_) + *loc += got - dot; +#endif + break; +#endif // __68k__ + +#if defined(__sh__) + case R_SH_GOT32: +#elif defined(__arm__) + case R_ARM_GOT32: +#elif defined(__i386__) + case R_386_GOT32: +#elif defined(__mc68000__) + case R_68K_GOT32: +#endif + assert(isym != NULL); + /* needs an entry in the .got: set it, once */ + if (!isym->gotent.reloc_done) { + isym->gotent.reloc_done = 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; + + /* address relative to the got */ +#if !defined(__mc68000__) +#if defined(__sh__) + case R_SH_GOTOFF: +#elif defined(__arm__) + case R_ARM_GOTOFF: +#elif defined(__i386__) + case R_386_GOTOFF: +#elif defined(__mc68000__) + case R_68K_GOTOFF: +#endif + assert(got != 0); + *loc += v - got; + break; +#endif // __mc68000__ + +#endif /* BB_USE_GOT_ENTRIES */ + + default: + printf("Warning: unhandled reloc %d\n",(int)ELF32_R_TYPE(rel->r_info)); + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +static int arch_create_got(struct obj_file *f) +{ +#if defined(BB_USE_GOT_ENTRIES) || defined(BB_USE_PLT_ENTRIES) + struct arch_file *ifile = (struct arch_file *) f; + int i; +#if defined(BB_USE_GOT_ENTRIES) + int got_offset = 0, gotneeded = 0; +#endif +#if defined(BB_USE_PLT_ENTRIES) + int plt_offset = 0, pltneeded = 0; +#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)]; + + switch (ELF32_R_TYPE(rel->r_info)) { +#if defined(__arm__) + case R_ARM_GOT32: + break; +#elif defined(__sh__) + case R_SH_GOT32: + break; +#elif defined(__i386__) + case R_386_GOT32: + break; +#elif defined(__mc68000__) + case R_68K_GOT32: + break; +#endif + +#if defined(__powerpc__) + case R_PPC_REL24: + pltneeded = 1; + break; +#endif + +#if defined(__arm__) + case R_ARM_PC24: + case R_ARM_PLT32: + pltneeded = 1; + break; + + case R_ARM_GOTPC: + case R_ARM_GOTOFF: + gotneeded = 1; + if (got_offset == 0) + got_offset = 4; +#elif defined(__sh__) + case R_SH_GOTPC: + case R_SH_GOTOFF: + gotneeded = 1; +#elif defined(__i386__) + case R_386_GOTPC: + case R_386_GOTOFF: + gotneeded = 1; +#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(BB_USE_GOT_ENTRIES) + if (!intsym->gotent.offset_done) { + intsym->gotent.offset_done = 1; + intsym->gotent.offset = got_offset; + got_offset += BB_GOT_ENTRY_SIZE; + } +#endif +#if defined(BB_USE_PLT_ENTRIES) + if (pltneeded && intsym->pltent.allocated == 0) { + intsym->pltent.allocated = 1; + intsym->pltent.offset = plt_offset; + plt_offset += BB_PLT_ENTRY_SIZE; + intsym->pltent.inited = 0; + pltneeded = 0; + } +#endif + } + } + +#if defined(BB_USE_GOT_ENTRIES) + if (got_offset) { + struct obj_section* myrelsec = obj_find_section(f, ".got"); + + if (myrelsec) { + obj_extend_section(myrelsec, got_offset); + } else { + myrelsec = obj_create_alloced_section(f, ".got", + BB_GOT_ENTRY_SIZE, + got_offset); + assert(myrelsec); + } + + ifile->got = myrelsec; + } +#endif + +#if defined(BB_USE_PLT_ENTRIES) + if (plt_offset) + ifile->plt = obj_create_alloced_section(f, ".plt", + BB_PLT_ENTRY_SIZE, + plt_offset); +#endif +#endif + return 1; +} + +static int arch_init_module(struct obj_file *f, struct new_module *mod) +{ + return 1; +} + + +/*======================================================================*/ + +/* 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 BB_FEATURE_INSMOD_VERSION_CHECKING +/* Get the kernel version in the canonical integer form. */ + +static int get_kernel_version(char str[STRVERSIONLEN]) +{ + struct utsname uts_info; + int kv; + + if (uname(&uts_info) < 0) + return -1; + strncpy(str, uts_info.release, STRVERSIONLEN); + + kv = get_kernel_revision(); + if(kv==0) + return -1; +} + +/* 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 /* BB_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) + 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) + 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; + + 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; + + sym = obj_find_symbol(f, (char *) s->name); + if (sym && !ELFW(ST_BIND) (sym->info) == STB_LOCAL) { + sym = obj_add_symbol(f, (char *) s->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 in pre 2.1 kernels. */ + +static int +old_process_module_arguments(struct obj_file *f, int argc, char **argv) +{ + while (argc > 0) { + char *p, *q; + struct obj_symbol *sym; + int *loc; + + p = *argv; + if ((q = strchr(p, '=')) == NULL) { + argc--; + continue; + } + *q++ = '\0'; + + sym = obj_find_symbol(f, p); + + /* Also check that the parameter was not resolved from the kernel. */ + if (sym == NULL || sym->secidx > SHN_HIRESERVE) { + error_msg("symbol for parameter %s not found", p); + return 0; + } + + loc = (int *) (f->sections[sym->secidx]->contents + sym->value); + + /* Do C quoting if we begin with a ". */ + if (*q == '"') { + char *r, *str; + + str = alloca(strlen(q)); + for (r = str, q++; *q != '"'; ++q, ++r) { + if (*q == '\0') { + error_msg("improperly terminated string argument for %s", p); + 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'; + obj_string_patch(f, sym->secidx, sym->value, str); + } else if (*q >= '0' && *q <= '9') { + do + *loc++ = strtoul(q, &q, 0); + while (*q++ == ','); + } else { + char *contents = f->sections[sym->secidx]->contents; + char *myloc = contents + sym->value; + char *r; /* To search for commas */ + + /* Break the string with comas */ + while ((r = strchr(q, ',')) != (char *) NULL) { + *r++ = '\0'; + obj_string_patch(f, sym->secidx, myloc - contents, q); + myloc += sizeof(char *); + q = r; + } + + /* last part */ + obj_string_patch(f, sym->secidx, myloc - contents, q); + } + + argc--, argv++; + } + + return 1; +} + +#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING +static int old_is_module_checksummed(struct obj_file *f) +{ + return obj_find_symbol(f, "Using_Versions") != NULL; +} +/* Get the module's kernel version in the canonical integer form. */ + +static int +old_get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) +{ + struct obj_symbol *sym; + char *p, *q; + int a, b, c; + + sym = obj_find_symbol(f, "kernel_version"); + if (sym == NULL) + return -1; + + p = f->sections[sym->secidx]->contents + sym->value; + 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 /* BB_FEATURE_INSMOD_VERSION_CHECKING */ + +#ifdef BB_FEATURE_OLD_MODULE_INTERFACE + +/* Fetch all the symbols and divvy them up as appropriate for the modules. */ + +static int old_get_kernel_symbols(const char *m_name) +{ + struct old_kernel_sym *ks, *k; + struct new_module_symbol *s; + struct external_module *mod; + int nks, nms, nmod, i; + + nks = get_kernel_syms(NULL); + if (nks <= 0) { + if (nks) + perror_msg("get_kernel_syms: %s", m_name); + else + error_msg("No kernel symbols"); + return 0; + } + + ks = k = xmalloc(nks * sizeof(*ks)); + + if (get_kernel_syms(ks) != nks) { + perror("inconsistency with get_kernel_syms -- is someone else " + "playing with modules?"); + free(ks); + return 0; + } + + /* Collect the module information. */ + + mod = NULL; + nmod = -1; + + while (k->name[0] == '#' && k->name[1]) { + struct old_kernel_sym *k2; + + /* Find out how many symbols this module has. */ + for (k2 = k + 1; k2->name[0] != '#'; ++k2) + continue; + nms = k2 - k - 1; + + mod = xrealloc(mod, (++nmod + 1) * sizeof(*mod)); + mod[nmod].name = k->name + 1; + mod[nmod].addr = k->value; + mod[nmod].used = 0; + mod[nmod].nsyms = nms; + mod[nmod].syms = s = (nms ? xmalloc(nms * sizeof(*s)) : NULL); + + for (i = 0, ++k; i < nms; ++i, ++s, ++k) { + s->name = (unsigned long) k->name; + s->value = k->value; + } + + k = k2; + } + + ext_modules = mod; + n_ext_modules = nmod + 1; + + /* Now collect the symbols for the kernel proper. */ + + if (k->name[0] == '#') + ++k; + + nksyms = nms = nks - (k - ks); + ksyms = s = (nms ? xmalloc(nms * sizeof(*s)) : NULL); + + for (i = 0; i < nms; ++i, ++s, ++k) { + s->name = (unsigned long) k->name; + s->value = k->value; + } + + return 1; +} + +/* Return the kernel symbol checksum version, or zero if not used. */ + +static int old_is_kernel_checksummed(void) +{ + /* Using_Versions is the first symbol. */ + if (nksyms > 0 + && strcmp((char *) ksyms[0].name, + "Using_Versions") == 0) return ksyms[0].value; + else + return 0; +} + + +static int old_create_mod_use_count(struct obj_file *f) +{ + struct obj_section *sec; + + sec = obj_create_alloced_section_first(f, ".moduse", sizeof(long), + sizeof(long)); + + obj_add_symbol(f, "mod_use_count_", -1, + ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), sec->idx, 0, + sizeof(long)); + + return 1; +} + +static int +old_init_module(const char *m_name, struct obj_file *f, + unsigned long m_size) +{ + char *image; + struct old_mod_routines routines; + struct old_symbol_table *symtab; + int ret; + + /* Create the symbol table */ + { + int nsyms = 0, strsize = 0, total; + + /* Size things first... */ + if (flag_export) { + int i; + for (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->ksymidx = nsyms++; + strsize += strlen(sym->name) + 1; + } + } + } + + total = (sizeof(struct old_symbol_table) + + nsyms * sizeof(struct old_module_symbol) + + n_ext_modules_used * sizeof(struct old_module_ref) + + strsize); + symtab = xmalloc(total); + symtab->size = total; + symtab->n_symbols = nsyms; + symtab->n_refs = n_ext_modules_used; + + if (flag_export && nsyms) { + struct old_module_symbol *ksym; + char *str; + int i; + + ksym = symtab->symbol; + str = ((char *) ksym + nsyms * sizeof(struct old_module_symbol) + + n_ext_modules_used * sizeof(struct old_module_ref)); + + for (i = 0; i < HASH_BUCKETS; ++i) { + struct obj_symbol *sym; + for (sym = f->symtab[i]; sym; sym = sym->next) + if (sym->ksymidx >= 0) { + ksym->addr = obj_symbol_final_value(f, sym); + ksym->name = + (unsigned long) str - (unsigned long) symtab; + + strcpy(str, sym->name); + str += strlen(sym->name) + 1; + ksym++; + } + } + } + + if (n_ext_modules_used) { + struct old_module_ref *ref; + int i; + + ref = (struct old_module_ref *) + ((char *) symtab->symbol + nsyms * sizeof(struct old_module_symbol)); + + for (i = 0; i < n_ext_modules; ++i) + if (ext_modules[i].used) + ref++->module = ext_modules[i].addr; + } + } + + /* Fill in routines. */ + + routines.init = + obj_symbol_final_value(f, obj_find_symbol(f, "init_module")); + routines.cleanup = + obj_symbol_final_value(f, obj_find_symbol(f, "cleanup_module")); + + /* 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); + + /* image holds the complete relocated module, accounting correctly for + mod_use_count. However the old module kernel support assume that + it is receiving something which does not contain mod_use_count. */ + ret = old_sys_init_module(m_name, image + sizeof(long), + m_size | (flag_autoclean ? OLD_MOD_AUTOCLEAN + : 0), &routines, symtab); + if (ret) + perror_msg("init_module: %s", m_name); + + free(image); + free(symtab); + + return ret == 0; +} + +#else + +#define old_create_mod_use_count(x) TRUE +#define old_init_module(x, y, z) TRUE + +#endif /* BB_FEATURE_OLD_MODULE_INTERFACE */ + + + +/*======================================================================*/ +/* 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; + 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) { + error_msg("invalid parameter %s", key); + return 0; + } + + sym = obj_find_symbol(f, key); + + /* Also check that the parameter was not resolved from the kernel. */ + if (sym == NULL || sym->secidx > SHN_HIRESERVE) { + 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') { + 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 usefull, as the previous case + doesn't null 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))) { + 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) { + 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: + 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) { + error_msg("too many values for %s (max %d)", key, max); + return 0; + } + ++q; + break; + + default: + error_msg("invalid argument syntax for %s", key); + return 0; + } + } + + end_of_arg: + if (n < min) { + error_msg("too few values for %s (min %d)", key, min); + return 0; + } + + argc--, argv++; + } + + return 1; +} + +#ifdef BB_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; + 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 /* BB_FEATURE_INSMOD_VERSION_CHECKING */ + + +#ifdef BB_FEATURE_NEW_MODULE_INTERFACE + +/* 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; + } + 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; + } + 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: + 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; + } + 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, "__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; +} + + +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, "__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) { + 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, "init_module")); + module->cleanup = + obj_symbol_final_value(f, obj_find_symbol(f, "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; + } + + if (!arch_init_module(f, module)) + return 0; + + /* 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 = new_sys_init_module(m_name, (struct new_module *) image); + if (ret) + perror_msg("init_module: %s", m_name); + + free(image); + + return ret == 0; +} + +#else + +#define new_init_module(x, y, z) TRUE +#define new_create_this_module(x, y) 0 +#define new_create_module_ksymtab(x) +#define query_module(v, w, x, y, z) -1 + +#endif /* BB_FEATURE_NEW_MODULE_INTERFACE */ + + +/*======================================================================*/ + +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 { + 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) { + error_msg("%s of type %ld for %s", errmsg, + (long) ELFW(R_TYPE) (rel->r_info), + strtab + extsym->st_name); + } else { + 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) { + 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) { + 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)) { + error_msg("ELF file not for this architecture"); + return NULL; + } + if (f->header.e_type != ET_REL) { + error_msg("ELF file not a relocatable object"); + return NULL; + } + + /* Read the section headers. */ + + if (f->header.e_shentsize != sizeof(ElfW(Shdr))) { + 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) { + 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) { + perror_msg("error reading ELF section data"); + return NULL; + } + } else { + sec->contents = NULL; + } + break; + +#if SHT_RELM == SHT_REL + case SHT_RELA: + error_msg("RELA relocations not supported on this architecture"); + return NULL; +#else + case SHT_REL: + 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; + } + + 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))) { + 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) { + const char *name; + if (sym->st_name) + name = strtab + sym->st_name; + else + name = f->sections[sym->st_shndx]->name; + + obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, + sym->st_value, sym->st_size); + } + } + break; + + case SHT_RELM: + if (sec->header.sh_entsize != sizeof(ElfW(RelM))) { + 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 BB_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 = (char*) f->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) { + errorMsg("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[] = { + "cleanup_module", + "init_module", + "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)); +} + + + +extern int insmod_main( int argc, char **argv) +{ + int opt; + int k_crcs; + int k_new_syscalls; + int len; + char *tmp; + unsigned long m_size; + ElfW(Addr) m_addr; + FILE *fp; + struct obj_file *f; + struct stat st; + char m_name[FILENAME_MAX + 1] = "\0"; + int exit_status = EXIT_FAILURE; + int m_has_modinfo; +#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING + int k_version; + char k_strversion[STRVERSIONLEN]; + char m_strversion[STRVERSIONLEN]; + int m_version; + int m_crcs; +#endif + + /* Parse any options */ + while ((opt = getopt(argc, argv, "fkvxLo:")) > 0) { + switch (opt) { + case 'f': /* force loading */ + flag_force_load = 1; + break; + case 'k': /* module loaded by kerneld, auto-cleanable */ + flag_autoclean = 1; + break; + case 'v': /* verbose output */ + flag_verbose = 1; + break; + case 'x': /* do not export externs */ + flag_export = 0; + break; + case 'o': /* name the output module */ + strncpy(m_name, optarg, FILENAME_MAX); + 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; + default: + show_usage(); + } + } + + if (argv[optind] == NULL) { + show_usage(); + } + + /* Grab the module name */ + if ((tmp = strrchr(argv[optind], '/')) != NULL) { + tmp++; + } else { + tmp = argv[optind]; + } + len = strlen(tmp); + + if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') + len -= 2; + memcpy(m_fullName, tmp, len); + m_fullName[len]='\0'; + if (*m_name == '\0') { + strcpy(m_name, m_fullName); + } + strcat(m_fullName, ".o"); + + /* 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) { + struct utsname myuname; + + /* 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 (uname(&myuname) == 0) { + char module_dir[FILENAME_MAX]; + char real_module_dir[FILENAME_MAX]; + snprintf (module_dir, sizeof(module_dir), "%s/%s", + _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 (module_dir, real_module_dir) == NULL) + strcpy(real_module_dir, module_dir); + recursive_action(real_module_dir, TRUE, FALSE, FALSE, + check_module_name_match, 0, m_fullName); + } + + /* Check if we have found anything yet */ + if (m_filename[0] == '\0' || ((fp = fopen(m_filename, "r")) == NULL)) + { + char module_dir[FILENAME_MAX]; + 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) == FALSE) + { + if (m_filename[0] == '\0' + || ((fp = fopen(m_filename, "r")) == NULL)) + { + error_msg("%s: no module by that name found", m_fullName); + return EXIT_FAILURE; + } + } else + error_msg_and_die("%s: no module by that name found", m_fullName); + } + } else + safe_strncpy(m_filename, argv[optind], sizeof(m_filename)); + + printf("Using %s\n", m_filename); + + if ((f = obj_load(fp, LOADBITS)) == NULL) + 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 BB_FEATURE_INSMOD_VERSION_CHECKING + /* Version correspondence? */ + + k_version = get_kernel_version(k_strversion); + if (m_has_modinfo) { + m_version = new_get_module_version(f, m_strversion); + } else { + m_version = old_get_module_version(f, m_strversion); + if (m_version == -1) { + error_msg("couldn't find the kernel version the module was " + "compiled for"); + goto out; + } + } + + if (strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0) { + if (flag_force_load) { + 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, k_strversion); + } else { + 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, k_strversion); + goto out; + } + } + k_crcs = 0; +#endif /* BB_FEATURE_INSMOD_VERSION_CHECKING */ + + k_new_syscalls = !query_module(NULL, 0, NULL, 0, NULL); + + if (k_new_syscalls) { +#ifdef BB_FEATURE_NEW_MODULE_INTERFACE + if (!new_get_kernel_symbols()) + goto out; + k_crcs = new_is_kernel_checksummed(); +#else + error_msg("Not configured to support new kernels"); + goto out; +#endif + } else { +#ifdef BB_FEATURE_OLD_MODULE_INTERFACE + if (!old_get_kernel_symbols(m_name)) + goto out; + k_crcs = old_is_kernel_checksummed(); +#else + error_msg("Not configured to support old kernels"); + goto out; +#endif + } + +#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING + if (m_has_modinfo) + m_crcs = new_is_module_checksummed(f); + else + m_crcs = old_is_module_checksummed(f); + + if (m_crcs != k_crcs) + obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash); +#endif /* BB_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 (k_new_syscalls + ? !new_create_this_module(f, m_name) + : !old_create_mod_use_count(f)) + { + goto out; + } + + if (!obj_check_undefineds(f)) { + goto out; + } + obj_allocate_commons(f); + + /* done with the module name, on to the optional var=value arguments */ + ++optind; + + if (optind < argc) { + if (m_has_modinfo + ? !new_process_module_arguments(f, argc - optind, argv + optind) + : !old_process_module_arguments(f, argc - optind, argv + optind)) + { + goto out; + } + } + + arch_create_got(f); + hide_special_symbols(f); + + if (k_new_syscalls) + 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: + error_msg("A module named %s already exists", m_name); + goto out; + case ENOMEM: + error_msg("Can't allocate kernel memory for module; needed %lu bytes", + m_size); + goto out; + default: + 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 + */ + // f->imagebase = (char*) m_addr; + f->imagebase = (ElfW(Addr)) m_addr; + if (!obj_load_progbits(fp, f)) { + delete_module(m_name); + goto out; + } +#endif + + if (!obj_relocate(f, m_addr)) { + delete_module(m_name); + goto out; + } + + if (k_new_syscalls + ? !new_init_module(m_name, f, m_size) + : !old_init_module(m_name, f, m_size)) + { + delete_module(m_name); + goto out; + } + + exit_status = EXIT_SUCCESS; + +out: + fclose(fp); + return(exit_status); +} diff --git a/busybox/install.sh b/busybox/install.sh new file mode 100755 index 000000000..d163a2ef8 --- /dev/null +++ b/busybox/install.sh @@ -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/kill.c b/busybox/kill.c new file mode 100644 index 000000000..3884ebdf4 --- /dev/null +++ b/busybox/kill.c @@ -0,0 +1,142 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini kill/killall implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +static const int KILL = 0; +static const int KILLALL = 1; + + +extern int kill_main(int argc, char **argv) +{ + int whichApp, sig = SIGTERM; + const char *name; + +#ifdef BB_KILLALL + /* Figure out what we are trying to do here */ + whichApp = (strcmp(applet_name, "killall") == 0)? KILLALL : KILL; +#else + whichApp = KILL; +#endif + + argc--; + argv++; + /* Parse any options */ + if (argc < 1) + show_usage(); + + while (argc > 0 && **argv == '-') { + while (*++(*argv)) { + switch (**argv) { + case 'l': + if(argc>1) { + for(argv++; *argv; argv++) { + name = u_signal_names(*argv, &sig, -1); + if(name!=NULL) + printf("%s\n", name); + } + } else { + int col = 0; + for(sig=1; sig < NSIG; sig++) { + name = u_signal_names(0, &sig, 1); + if(name==NULL) /* unnamed */ + continue; + col += printf("%2d) %-16s", sig, name); + if (col > 60) { + printf("\n"); + col = 0; + } + } + printf("\n"); + } + return EXIT_SUCCESS; + case '-': + show_usage(); + default: + name = u_signal_names(*argv, &sig, 0); + if(name==NULL) + error_msg_and_die( "bad signal name: %s", *argv); + argc--; + argv++; + goto do_it_now; + } + argc--; + argv++; + } + } + + do_it_now: + + if (whichApp == KILL) { + /* Looks like they want to do a kill. Do that */ + while (--argc >= 0) { + int pid; + + if (!isdigit(**argv)) + perror_msg_and_die( "Bad PID"); + pid = strtol(*argv, NULL, 0); + if (kill(pid, sig) != 0) + perror_msg_and_die( "Could not kill pid '%d'", pid); + argv++; + } + } +#ifdef BB_KILLALL + else { + int all_found = TRUE; + pid_t myPid=getpid(); + /* Looks like they want to do a killall. Do that */ + while (--argc >= 0) { + pid_t* pidList; + + pidList = find_pid_by_name( *argv); + if (!pidList || *pidList<=0) { + all_found = FALSE; + error_msg_and_die( "%s: no process killed", *argv); + } + + for(; pidList && *pidList!=0; pidList++) { + if (*pidList==myPid) + continue; + if (kill(*pidList, sig) != 0) + perror_msg_and_die( "Could not kill pid '%d'", *pidList); + } + /* Note that we don't bother to free the memory + * allocated in find_pid_by_name(). It will be freed + * upon exit, so we can save a byte or two */ + argv++; + } + if (all_found == FALSE) + return EXIT_FAILURE; + } +#endif + + return EXIT_SUCCESS; +} diff --git a/busybox/klogd.c b/busybox/klogd.c new file mode 100644 index 000000000..d7b54e9c8 --- /dev/null +++ b/busybox/klogd.c @@ -0,0 +1,153 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini klogd implementation for busybox + * + * Copyright (C) 2001 by Gennady Feldman . + * Changes: Made this a standalone busybox module which uses standalone + * syslog() client interface. + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * Copyright (C) 2000 by Karl M. Hegbloom + * + * "circular buffer" Copyright (C) 2000 by Gennady Feldman + * + * Maintainer: Gennady Feldman 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 +#include +#include /* for our signal() handlers */ +#include /* strncpy() */ +#include /* errno and friends */ +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +# ifdef __alpha__ +# define klogctl syslog +# endif +#else +# include +#endif + +#include "busybox.h" + +static void klogd_signal(int sig) +{ + klogctl(7, NULL, 0); + klogctl(0, 0, 0); + //logMessage(0, "Kernel log daemon exiting."); + syslog_msg(LOG_DAEMON, 0, "Kernel log daemon exiting."); + exit(TRUE); +} + +static void doKlogd (void) __attribute__ ((noreturn)); +static void doKlogd (void) +{ + int priority = LOG_INFO; + char log_buffer[4096]; + int i, n, lastc; + char *start; + + /* 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); + + syslog_msg(LOG_DAEMON, 0, "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) { + char message[80]; + + if (errno == EINTR) + continue; + snprintf(message, 79, "klogd: Error return from sys_sycall: %d - %s.\n", + errno, strerror(errno)); + syslog_msg(LOG_DAEMON, LOG_SYSLOG | LOG_ERR, message); + exit(1); + } + + /* klogctl buffer parsing modelled after code in dmesg.c */ + start=&log_buffer[0]; + lastc='\0'; + for (i=0; i') i++; + start = &log_buffer[i]; + } + if (log_buffer[i] == '\n') { + log_buffer[i] = '\0'; /* zero terminate this message */ + syslog_msg(LOG_DAEMON, LOG_KERN | priority, 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; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "n")) > 0) { + switch (opt) { + case 'n': + doFork = FALSE; + break; + default: + show_usage(); + } + } + + if (doFork == TRUE) { + if (daemon(0, 1) < 0) + perror_msg_and_die("daemon"); + } + doKlogd(); + + return EXIT_SUCCESS; +} + +/* +Local Variables +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/lash.c b/busybox/lash.c new file mode 100644 index 000000000..0af669116 --- /dev/null +++ b/busybox/lash.c @@ -0,0 +1,1632 @@ +/* vi: set sw=4 ts=4: */ +/* + * lash -- the BusyBox Lame-Ass SHell + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * 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.c + */ + +//For debugging/development on the shell only... +//#define DEBUG_SHELL + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "cmdedit.h" + +#ifdef BB_LOCALE_SUPPORT +#include +#endif + +#include +#define expand_t glob_t + + +static const int MAX_READ = 128; /* size of input buffer for `read' builtin */ +#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" + + +enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE, + REDIRECT_APPEND +}; + +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; + + +struct jobset { + struct job *head; /* head of list of running jobs */ + struct job *fg; /* current foreground job */ +}; + +struct redir_struct { + enum redir_type type; /* type of redirection */ + int fd; /* file descriptor being redirected */ + char *filename; /* file to redirect fd to */ +}; + +struct child_prog { + pid_t pid; /* 0 if exited */ + char **argv; /* program name and arguments */ + int num_redirects; /* elements in redirection array */ + struct redir_struct *redirects; /* I/O redirects */ + int is_stopped; /* is the program currently running? */ + struct job *family; /* pointer back to the child's parent 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 pid_t shell_pgrp; +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 ' 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 = 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) { + error_msg("%s: no current job", child->argv[0]); + return EXIT_FAILURE; + } + } else { + if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { + 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) { + 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 { + 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 = 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 BB_FEATURE_SH_FANCY_PROMPT + if (strncmp(v, "PS1=", 4)==0) + PS1 = getenv("PS1"); +#endif + +#ifdef BB_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 */ + strcpy(string, child->argv[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=" + ** 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) + 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; +} + + +/* 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); + if (cmd->progs[i].redirects) + free(cmd->progs[i].redirects); + } + if (cmd->progs) + free(cmd->progs); + if (cmd->text) + free(cmd->text); + if (cmd->cmdbuf) + 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) + perror_msg("waitpid"); +} + +/* 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!) */ + perror_msg("error opening %s", redir->filename); + return 1; + } + + if (openfd != redir->fd) { + if (squirrel && redir->fd < 3) { + squirrel[redir->fd] = dup(redir->fd); + } + 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); + } + } +} + +static inline void cmdedit_set_initial_prompt(void) +{ +#ifndef BB_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 BB_FEATURE_SH_FANCY_PROMPT + /* Set up the prompt */ + if (shell_context == 0) { + if (PS1) + 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 BB_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] = 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 = strdup(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); + 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. */ + 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) { + 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; + 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) { + 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, *chptr; + int argc_l = 0; + int done = 0; + int argv_alloced; + int i, saw_quote = 0; + char quote = '\0'; + int count; + struct child_prog *prog; + + /* 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->redirects = NULL; + prog->is_stopped = 0; + prog->family = job; + + 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) { + 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; + + 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) { + 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) { + 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) { + 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; + + case '&': /* background */ + *inbg = 1; + case ';': /* multiple commands */ + done = 1; + return_command = *command_ptr + (src - *command_ptr) + 1; + break; + + case '\\': + src++; + if (!*src) { + 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 BB_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) { + applet_name=x->cmd; + exit (x->function(child)); + } + } +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + /* Check if the command matches any busybox internal + * commands ("applets") here. Following discussions from + * November 2000 on busybox@opensource.lineo.com, don't use + * 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]; + +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + /* If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then + * if you run /bin/cat, it will use BusyBox cat even if + * /bin/cat exists on the filesystem and is _not_ busybox. + * Some systems want this, others do not. Choose wisely. :-) + */ + name = get_last_path_component(name); +#endif + + { + 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); + perror_msg_and_die("%s", child->argv[0]); +} + +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; + + 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) + perror_msg("tcsetpgrp"); + } +} + +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) 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 squirrel[] = {-1, -1, -1}; + int rcode; + setup_redirects(child, squirrel); + rcode = x->function(child); + restore_redirects(squirrel); + return rcode; + } + } + } + + if (!(child->pid = fork())) { + /* 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; + pid_t parent_pgrp; + int i; + int inbg; + int status; + newjob.job_list = &job_list; + newjob.job_context = DEFAULT_CONTEXT; + + /* save current owner of TTY so we can restore it on exit */ + parent_pgrp = tcgetpgrp(shell_terminal); + + 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) == FALSE) { + 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) + 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; + } + } 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) + perror_msg("tcsetpgrp"); + } + } + } + free(command); + + /* return controlling TTY back to parent process group before exiting */ + if (tcsetpgrp(shell_terminal, parent_pgrp)) + perror_msg("tcsetpgrp"); + + /* return exit status if called with "-c" */ + if (input == NULL && WIFEXITED(status)) + return WEXITSTATUS(status); + + return 0; +} + + +#ifdef BB_FEATURE_CLEAN_UP +void free_memory(void) +{ + if (cwd) { + free(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 + +/* 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() +{ + /* 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(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 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) + error_msg_and_die("multiple -c arguments"); + local_pending_command = xstrdup(argv[optind]); + optind++; + argv = argv+optind; + break; + case 'i': + interactive = TRUE; + break; + default: + 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(fileno(stdin)) && isatty(fileno(stdout))) { + 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 */ + printf( "\n\n" BB_BANNER " Built-in shell (lash)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); + } else if (local_pending_command==NULL) { + //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]); + input = 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 = unknown; + +#ifdef BB_FEATURE_CLEAN_UP + atexit(free_memory); +#endif + +#ifdef BB_FEATURE_COMMAND_EDITING + cmdedit_set_initial_prompt(); +#else + PS1 = NULL; +#endif + + return (busy_loop(input)); +} diff --git a/busybox/length.c b/busybox/length.c new file mode 100644 index 000000000..73becd28a --- /dev/null +++ b/busybox/length.c @@ -0,0 +1,13 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include "busybox.h" + +extern int length_main(int argc, char **argv) +{ + if (argc != 2 || **(argv + 1) == '-') + show_usage(); + printf("%lu\n", (long)strlen(argv[1])); + return EXIT_SUCCESS; +} diff --git a/busybox/libbb/.cvsignore b/busybox/libbb/.cvsignore new file mode 100644 index 000000000..2bbe016f5 --- /dev/null +++ b/busybox/libbb/.cvsignore @@ -0,0 +1 @@ +loop.h diff --git a/busybox/libbb/Makefile b/busybox/libbb/Makefile new file mode 100644 index 000000000..a9ea76947 --- /dev/null +++ b/busybox/libbb/Makefile @@ -0,0 +1,11 @@ +# Silly wrapper makefile. This Makefile is _not_ used by the build system for +# busybox, it is just to make working on libbb more conveinient. +# -Erik Andersen + +all: + make -C .. libbb.a + +clean: + - rm -rf libbb.a + - find -name \*.o -exec rm -f {} \; + diff --git a/busybox/libbb/README b/busybox/libbb/README new file mode 100644 index 000000000..0e36f84b6 --- /dev/null +++ b/busybox/libbb/README @@ -0,0 +1,15 @@ +Please see the LICENSE file for copyright information. + +libbb is BusyBox's utility library. This all used to be in a single file +(utility.c to be specific). When I split utility.c up to create libbb, I did +not carefully fix up the copyright and licensing information. I'll do that for +the next release. + +For now, justtrust me that a bunch of people have worked on this stuff, +and it is all GPL'ed. + + Erik Andersen + + + + diff --git a/busybox/libbb/arith.c b/busybox/libbb/arith.c new file mode 100644 index 000000000..04c45ec3d --- /dev/null +++ b/busybox/libbb/arith.c @@ -0,0 +1,263 @@ +/* Copyright (c) 2001 Aaron Lehmann + + 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 writen in yacc. The supported operators are + * listed in #defines below. Parens, order of operations, and error handling + * are supported. This code is threadsafe. */ + +/* To use the routine, call it with an expression string. It returns an + * integer result. You will also need to define an "error" function + * that takes printf arguments and _does not return_, or modify the code + * to use another error mechanism. */ + +#include +#include +#include "libbb.h" + +typedef char operator; + +#define tok_decl(prec,id) (((id)<<5)|(prec)) +#define PREC(op) ((op)&0x1F) + +#define TOK_LPAREN tok_decl(0,0) + +#define TOK_OR tok_decl(1,0) + +#define TOK_AND tok_decl(2,0) + +#define TOK_BOR tok_decl(3,0) + +#define TOK_BXOR tok_decl(4,0) + +#define TOK_BAND tok_decl(5,0) + +#define TOK_EQ tok_decl(6,0) +#define TOK_NE tok_decl(6,1) + +#define TOK_LT tok_decl(7,0) +#define TOK_GT tok_decl(7,1) +#define TOK_GE tok_decl(7,2) +#define TOK_LE tok_decl(7,3) + +#define TOK_LSHIFT tok_decl(8,0) +#define TOK_RSHIFT tok_decl(8,1) + +#define TOK_ADD tok_decl(9,0) +#define TOK_SUB tok_decl(9,1) + +#define TOK_MUL tok_decl(10,0) +#define TOK_DIV tok_decl(10,1) +#define TOK_REM tok_decl(10,2) + +#define UNARYPREC 14 +#define TOK_BNOT tok_decl(UNARYPREC,0) +#define TOK_NOT tok_decl(UNARYPREC,1) +#define TOK_UMINUS tok_decl(UNARYPREC,2) + +#define TOK_NUM tok_decl(15,0) + +#define ARITH_APPLY(op) arith_apply(op, numstack, &numstackptr) +#define NUMPTR (*numstackptr) +static short arith_apply(operator op, long *numstack, long **numstackptr) +{ + if (NUMPTR == numstack) goto err; + if (op == TOK_UMINUS) + NUMPTR[-1] *= -1; + else if (op == TOK_NOT) + NUMPTR[-1] = !(NUMPTR[-1]); + else if (op == TOK_BNOT) + NUMPTR[-1] = ~(NUMPTR[-1]); + + /* Binary operators */ + else { + if (NUMPTR-1 == numstack) goto err; + --NUMPTR; + if (op == TOK_BOR) + NUMPTR[-1] |= *NUMPTR; + else if (op == TOK_OR) + NUMPTR[-1] = *NUMPTR || NUMPTR[-1]; + else if (op == TOK_BAND) + NUMPTR[-1] &= *NUMPTR; + else if (op == TOK_AND) + NUMPTR[-1] = NUMPTR[-1] && *NUMPTR; + else if (op == TOK_EQ) + NUMPTR[-1] = (NUMPTR[-1] == *NUMPTR); + else if (op == TOK_NE) + NUMPTR[-1] = (NUMPTR[-1] != *NUMPTR); + else if (op == TOK_GE) + NUMPTR[-1] = (NUMPTR[-1] >= *NUMPTR); + else if (op == TOK_RSHIFT) + NUMPTR[-1] >>= *NUMPTR; + else if (op == TOK_LSHIFT) + NUMPTR[-1] <<= *NUMPTR; + else if (op == TOK_GT) + NUMPTR[-1] = (NUMPTR[-1] > *NUMPTR); + else if (op == TOK_LT) + NUMPTR[-1] = (NUMPTR[-1] < *NUMPTR); + else if (op == TOK_LE) + NUMPTR[-1] = (NUMPTR[-1] <= *NUMPTR); + else if (op == TOK_MUL) + NUMPTR[-1] *= *NUMPTR; + else if (op == TOK_DIV) { + if(*NUMPTR==0) + return -2; + NUMPTR[-1] /= *NUMPTR; + } + else if (op == TOK_REM) { + if(*NUMPTR==0) + return -2; + NUMPTR[-1] %= *NUMPTR; + } + else if (op == TOK_ADD) + NUMPTR[-1] += *NUMPTR; + else if (op == TOK_SUB) + NUMPTR[-1] -= *NUMPTR; + } + return 0; +err: return(-1); +} + +extern long arith (const char *startbuf, int *errcode) +{ + register char arithval; + const char *expr = startbuf; + + operator lasttok = TOK_MUL, op; + size_t datasizes = strlen(startbuf); + unsigned char prec; + + long *numstack, *numstackptr; + operator *stack = alloca(datasizes * sizeof(operator)), *stackptr = stack; + + *errcode = 0; + numstack = alloca((datasizes/2+1)*sizeof(long)), numstackptr = numstack; + + while ((arithval = *expr)) { + if (arithval == ' ' || arithval == '\n' || arithval == '\t') + goto prologue; + if ((unsigned)arithval-'0' <= 9) /* isdigit */ { + *numstackptr++ = strtol(expr, (char **) &expr, 10); + lasttok = TOK_NUM; + continue; + } if (arithval == '(') { + *stackptr++ = TOK_LPAREN; + lasttok = TOK_LPAREN; + goto prologue; + } if (arithval == ')') { + lasttok = TOK_NUM; + while (stackptr != stack) { + op = *--stackptr; + if (op == TOK_LPAREN) + goto prologue; + *errcode = ARITH_APPLY(op); + if(*errcode) return *errcode; + } + goto err; /* Mismatched parens */ + } if (arithval == '|') { + if (*++expr == '|') + op = TOK_OR; + else { + --expr; + op = TOK_BOR; + } + } else if (arithval == '&') { + if (*++expr == '&') + op = TOK_AND; + else { + --expr; + op = TOK_BAND; + } + } else if (arithval == '=') { + if (*++expr != '=') goto err; /* Unknown token */ + op = TOK_EQ; + } else if (arithval == '!') { + if (*++expr == '=') + op = TOK_NE; + else { + --expr; + op = TOK_NOT; + } + } else if (arithval == '>') { + switch (*++expr) { + case '=': + op = TOK_GE; + break; + case '>': + op = TOK_RSHIFT; + break; + default: + --expr; + op = TOK_GT; + } + } else if (arithval == '<') { + switch (*++expr) { + case '=': + op = TOK_LE; + break; + case '<': + op = TOK_LSHIFT; + break; + default: + --expr; + op = TOK_LT; + } + } else if (arithval == '*') + op = TOK_MUL; + else if (arithval == '/') + op = TOK_DIV; + else if (arithval == '%') + op = TOK_REM; + else if (arithval == '+') { + if (lasttok != TOK_NUM) goto prologue; /* Unary plus */ + op = TOK_ADD; + } else if (arithval == '-') + op = (lasttok == TOK_NUM) ? TOK_SUB : TOK_UMINUS; + else if (arithval == '~') + op = TOK_BNOT; + else goto err; /* Unknown token */ + + prec = PREC(op); + if (prec != UNARYPREC) + while (stackptr != stack && PREC(stackptr[-1]) >= prec) { + *errcode = ARITH_APPLY(*--stackptr); + if(*errcode) return *errcode; + } + *stackptr++ = op; + lasttok = op; +prologue: ++expr; + } /* yay */ + + while (stackptr != stack) { + *errcode = ARITH_APPLY(*--stackptr); + if(*errcode) return *errcode; + } + if (numstackptr != numstack+1) { +err: + *errcode = -1; + return -1; + /* NOTREACHED */ + } + + return *numstack; +} diff --git a/busybox/libbb/ask_confirmation.c b/busybox/libbb/ask_confirmation.c new file mode 100644 index 000000000..f2922379c --- /dev/null +++ b/busybox/libbb/ask_confirmation.c @@ -0,0 +1,53 @@ +/* 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 +#include "libbb.h" + + +int ask_confirmation() +{ + int c = '\0'; + int ret = 0; + + while (c != '\n') { + c = getchar(); + if ( c != '\n' ) { + ret = ((c=='y')||(c=='Y')) ? 1 : 0; + } + } + return ret; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/chomp.c b/busybox/libbb/chomp.c new file mode 100644 index 000000000..111d4cf77 --- /dev/null +++ b/busybox/libbb/chomp.c @@ -0,0 +1,49 @@ +/* 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 +#include +#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/concat_path_file.c b/busybox/libbb/concat_path_file.c new file mode 100644 index 000000000..86dd2fbbf --- /dev/null +++ b/busybox/libbb/concat_path_file.c @@ -0,0 +1,26 @@ +/* + * busybox library eXtendet funcion + * + * concatenate path and file name to new allocation buffer, + * not addition '/' if path name already have '/' + * +*/ + +#include +#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++; + outbuf = xmalloc(strlen(path)+strlen(filename)+1+(lc==NULL)); + sprintf(outbuf, "%s%s%s", path, (lc==NULL)? "/" : "", filename); + + return outbuf; +} diff --git a/busybox/libbb/copy_file.c b/busybox/libbb/copy_file.c new file mode 100644 index 000000000..c79fbeb14 --- /dev/null +++ b/busybox/libbb/copy_file.c @@ -0,0 +1,237 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini copy_file implementation for busybox + * + * + * Copyright (C) 2001 by 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libbb.h" + +int copy_file(const char *source, const char *dest, int flags) +{ + struct stat source_stat; + struct stat dest_stat; + int dest_exists = 1; + int status = 0; + + if (((flags & FILEUTILS_PRESERVE_SYMLINKS) && + lstat(source, &source_stat) < 0) || + (!(flags & FILEUTILS_PRESERVE_SYMLINKS) && + stat(source, &source_stat) < 0)) { + perror_msg("%s", source); + return -1; + } + + if (stat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + perror_msg("unable to stat `%s'", dest); + return -1; + } + dest_exists = 0; + } + + if (dest_exists && source_stat.st_rdev == dest_stat.st_rdev && + source_stat.st_ino == dest_stat.st_ino) { + error_msg("`%s' and `%s' are the same file", source, dest); + return -1; + } + + if (S_ISDIR(source_stat.st_mode)) { + DIR *dp; + struct dirent *d; + mode_t saved_umask = 0; + + if (!(flags & FILEUTILS_RECUR)) { + error_msg("%s: omitting directory", source); + return -1; + } + + /* Create DEST. */ + if (dest_exists) { + if (!S_ISDIR(dest_stat.st_mode)) { + 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); + perror_msg("cannot create directory `%s'", dest); + return -1; + } + + umask(saved_umask); + } + + /* Recursively copy files in SOURCE. */ + if ((dp = opendir(source)) == NULL) { + perror_msg("unable to open directory `%s'", source); + status = -1; + goto end; + } + + while ((d = readdir(dp)) != NULL) { + char *new_source, *new_dest; + + if (strcmp(d->d_name, ".") == 0 || + strcmp(d->d_name, "..") == 0) + continue; + + new_source = concat_path_file(source, d->d_name); + 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); + } + + /* ??? What if an error occurs in readdir? */ + + if (closedir(dp) < 0) { + perror_msg("unable to close directory `%s'", source); + status = -1; + } + + if (!dest_exists && + chmod(dest, source_stat.st_mode & ~saved_umask) < 0) { + perror_msg("unable to change permissions of `%s'", dest); + status = -1; + } + } else if (S_ISREG(source_stat.st_mode)) { + FILE *sfp, *dfp; + + if (dest_exists) { + if (flags & FILEUTILS_INTERACTIVE) { + fprintf(stderr, "%s: overwrite `%s'? ", applet_name, dest); + if (!ask_confirmation()) + return 0; + } + + if ((dfp = fopen(dest, "w")) == NULL) { + if (!(flags & FILEUTILS_FORCE)) { + perror_msg("unable to open `%s'", dest); + return -1; + } + + if (unlink(dest) < 0) { + perror_msg("unable to remove `%s'", dest); + return -1; + } + + dest_exists = 0; + } + } + + if (!dest_exists) { + int fd; + + if ((fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode)) < 0 || + (dfp = fdopen(fd, "w")) == NULL) { + if (fd >= 0) + close(fd); + perror_msg("unable to open `%s'", dest); + return -1; + } + } + + if ((sfp = fopen(source, "r")) == NULL) { + fclose(dfp); + perror_msg("unable to open `%s'", source); + status = -1; + goto end; + } + + if (copy_file_chunk(sfp, dfp, -1) < 0) + status = -1; + + if (fclose(dfp) < 0) { + perror_msg("unable to close `%s'", dest); + status = -1; + } + + if (fclose(sfp) < 0) { + 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)) { + if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { + 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) { + perror_msg("cannot create fifo `%s'", dest); + return -1; + } + } else if (S_ISLNK(source_stat.st_mode)) { + char *lpath = xreadlink(source); + if (symlink(lpath, dest) < 0) { + 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) + perror_msg("unable to preserve ownership of `%s'", dest); +#endif + return 0; + } else { + error_msg("internal error: unrecognized file type"); + return -1; + } + +end: + + if (flags & FILEUTILS_PRESERVE_STATUS) { + struct utimbuf times; + + times.actime = source_stat.st_atime; + times.modtime = source_stat.st_mtime; + if (utime(dest, ×) < 0) + 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); + perror_msg("unable to preserve ownership of `%s'", dest); + } + if (chmod(dest, source_stat.st_mode) < 0) + perror_msg("unable to preserve permissions of `%s'", dest); + } + + return status; +} diff --git a/busybox/libbb/copy_file_chunk.c b/busybox/libbb/copy_file_chunk.c new file mode 100644 index 000000000..c440a6102 --- /dev/null +++ b/busybox/libbb/copy_file_chunk.c @@ -0,0 +1,74 @@ +/* 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 +#include +#include "libbb.h" + +/* Copy CHUNKSIZE bytes (or until EOF if CHUNKSIZE equals -1) from SRC_FILE + * to DST_FILE. */ +extern int copy_file_chunk(FILE *src_file, FILE *dst_file, unsigned long long chunksize) +{ + size_t nread, nwritten, size; + char buffer[BUFSIZ]; + + while (chunksize != 0) { + if (chunksize > BUFSIZ) + size = BUFSIZ; + else + size = chunksize; + + nread = fread (buffer, 1, size, src_file); + + if (nread != size && ferror (src_file)) { + perror_msg ("read"); + return -1; + } else if (nread == 0) { + if (chunksize != -1) { + error_msg ("Unable to read all data"); + return -1; + } + + return 0; + } + + nwritten = fwrite (buffer, 1, nread, dst_file); + + if (nwritten != nread) { + if (ferror (dst_file)) + perror_msg ("write"); + else + error_msg ("Unable to write all data"); + return -1; + } + + if (chunksize != -1) + chunksize -= nwritten; + } + + return 0; +} diff --git a/busybox/libbb/copyfd.c b/busybox/libbb/copyfd.c new file mode 100644 index 000000000..aa938d105 --- /dev/null +++ b/busybox/libbb/copyfd.c @@ -0,0 +1,59 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2001 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 +#include +#include +#include "libbb.h" + + +extern int copyfd(int fd1, int fd2) +{ + char buf[8192]; + ssize_t nread, nwrote; + + while (1) { + nread = safe_read(fd1, buf, sizeof(buf)); + if (nread == 0) + break; + if (nread == -1) { + perror_msg("read"); + return -1; + } + + nwrote = full_write(fd2, buf, nread); + if (nwrote == -1) { + perror_msg("write"); + return -1; + } + } + + return 0; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/create_icmp_socket.c b/busybox/libbb/create_icmp_socket.c new file mode 100644 index 000000000..d804b3987 --- /dev/null +++ b/busybox/libbb/create_icmp_socket.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * create raw socket for icmp protocol test permision + * and drop root privilegies if running setuid + * + */ + +#include +#include +#include +#include +#include +#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) + error_msg_and_die("permission denied. (are you root?)"); + else + perror_msg_and_die(can_not_create_raw_socket); + } + + /* drop root privs if running setuid */ + setuid(getuid()); + + return sock; +} diff --git a/busybox/libbb/device_open.c b/busybox/libbb/device_open.c new file mode 100644 index 000000000..8e97ce6c5 --- /dev/null +++ b/busybox/libbb/device_open.c @@ -0,0 +1,59 @@ +/* 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 +#include +#include "libbb.h" + + +/* try to open up the specified device */ +extern int device_open(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/dirname.c b/busybox/libbb/dirname.c new file mode 100644 index 000000000..5f839945d --- /dev/null +++ b/busybox/libbb/dirname.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dirname function. + * + * Copyright (C) 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 +#include "libbb.h" + +/* Return a string on the heap containing the directory component of PATH. */ + +char *dirname(const char *path) +{ + const char *s; + + /* Go to the end of the string. */ + s = path + strlen(path) - 1; + + /* Strip off trailing /s (unless it is also the leading /). */ + while (path < s && s[0] == '/') + s--; + + /* Strip the last component. */ + while (path <= s && s[0] != '/') + s--; + + while (path < s && s[0] == '/') + s--; + + if (s < path) + return xstrdup ("."); + else + return xstrndup (path, s - path + 1); +} diff --git a/busybox/libbb/error_msg.c b/busybox/libbb/error_msg.c new file mode 100644 index 000000000..c7d5fdb98 --- /dev/null +++ b/busybox/libbb/error_msg.c @@ -0,0 +1,52 @@ +/* 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 +#include +#include +#include +#include "libbb.h" + +extern void error_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + 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 index 000000000..b950ee00c --- /dev/null +++ b/busybox/libbb/error_msg_and_die.c @@ -0,0 +1,53 @@ +/* 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 +#include +#include +#include +#include "libbb.h" + +extern void error_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + verror_msg(s, p); + va_end(p); + putc('\n', stderr); + exit(EXIT_FAILURE); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/fgets_str.c b/busybox/libbb/fgets_str.c new file mode 100644 index 000000000..33d8d00cc --- /dev/null +++ b/busybox/libbb/fgets_str.c @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 +#include +#include + +/* + * Continue reading from file until the terminating string is encountered. + * Return data as string. + * e.g. fgets_str(file, "\n"); will read till end of file + */ + +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) { + break; + } + + /* grow the line buffer as necessary */ + while (idx > linebufsz - 2) { + linebuf = realloc(linebuf, linebufsz += 1000); /* GROWBY */ + } + + 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; + } + } + if (idx == 0) { + return NULL; + } + 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 index 000000000..2d9481a69 --- /dev/null +++ b/busybox/libbb/find_mount_point.c @@ -0,0 +1,81 @@ +/* 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 +#include +#include "libbb.h" + + +#include +/* + * 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 index 000000000..7f39dd41c --- /dev/null +++ b/busybox/libbb/find_pid_by_name.c @@ -0,0 +1,199 @@ +/* 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 +#include +#include +#include +#include +#include "libbb.h" + +#define READ_BUF_SIZE 50 + + +/* For Erik's nifty devps device driver */ +#ifdef BB_FEATURE_USE_DEVPS_PATCH +#include + +/* find_pid_by_name() + * + * This finds the pid of the specified process, + * by using the /dev/ps device driver. + * + * Returns a list of all matching PIDs + */ +extern pid_t* find_pid_by_name( char* pidName) +{ + int fd, i, j; + char device[] = "/dev/ps"; + pid_t num_pids; + pid_t* pid_array = NULL; + pid_t* pidList=NULL; + + /* open device */ + fd = open(device, O_RDONLY); + if (fd < 0) + perror_msg_and_die("open failed for `%s'", device); + + /* Find out how many processes there are */ + if (ioctl (fd, DEVPS_GET_NUM_PIDS, &num_pids)<0) + perror_msg_and_die("\nDEVPS_GET_PID_LIST"); + + /* Allocate some memory -- grab a few extras just in case + * some new processes start up while we wait. The kernel will + * just ignore any extras if we give it too many, and will trunc. + * the list if we give it too few. */ + pid_array = (pid_t*) xcalloc( num_pids+10, sizeof(pid_t)); + pid_array[0] = num_pids+10; + + /* Now grab the pid list */ + if (ioctl (fd, DEVPS_GET_PID_LIST, pid_array)<0) + perror_msg_and_die("\nDEVPS_GET_PID_LIST"); + + /* Now search for a match */ + for (i=1, j=0; id_name, "..") == 0) + continue; + + /* If it isn't a number, we don't want it */ + if (!isdigit(*next->d_name)) + continue; + + sprintf(filename, "/proc/%s/status", next->d_name); + if (! (status = fopen(filename, "r")) ) { + continue; + } + if (fgets(buffer, READ_BUF_SIZE-1, status) == NULL) { + fclose(status); + continue; + } + fclose(status); + + /* Buffer should contain a string like "Name: binary_name" */ + sscanf(buffer, "%*s %s", name); + if (strcmp(name, pidName) == 0) { + pidList=xrealloc( pidList, sizeof(pid_t) * (i+2)); + pidList[i++]=strtol(next->d_name, NULL, 0); + } + } + + if (pidList) + pidList[i]=0; + else if ( strcmp(pidName, "init")==0) { + /* If we found nothing and they were trying to kill "init", + * guess PID 1 and call it good... Perhaps we should simply + * exit if /proc isn't mounted, but this will do for now. */ + pidList=xrealloc( pidList, sizeof(pid_t)); + pidList[0]=1; + } else { + pidList=xrealloc( pidList, sizeof(pid_t)); + pidList[0]=-1; + } + return pidList; +} +#endif /* BB_FEATURE_USE_DEVPS_PATCH */ + +/* 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 index 000000000..f8f68464d --- /dev/null +++ b/busybox/libbb/find_root_device.c @@ -0,0 +1,84 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * Patched by a bunch of people. 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 + * + */ + +#include +#include +#include +#include +#include "libbb.h" + + + +extern char *find_real_root_device_name(const char* name) +{ + DIR *dir; + struct dirent *entry; + struct stat statBuf, rootStat; + char *fileName = NULL; + dev_t dev; + + if (stat("/", &rootStat) != 0) + perror_msg("could not stat '/'"); + else { + if ((dev = rootStat.st_rdev)==0) + dev=rootStat.st_dev; + + dir = opendir("/dev"); + if (!dir) + perror_msg("could not open '/dev'"); + else { + while((entry = readdir(dir)) != NULL) { + + /* Must skip ".." since that is "/", and so we + * would get a false positive on ".." */ + if (strcmp(entry->d_name, "..") == 0) + continue; + + fileName = concat_path_file("/dev", entry->d_name); + + /* 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=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 index 000000000..e9c4bbfc6 --- /dev/null +++ b/busybox/libbb/full_read.c @@ -0,0 +1,70 @@ +/* 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 +#include +#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. + */ +int full_read(int fd, char *buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) { + cc = read(fd, buf, len); + + if (cc < 0) + return -1; + + if (cc == 0) + break; + + 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 index 000000000..dc9937fa3 --- /dev/null +++ b/busybox/libbb/full_write.c @@ -0,0 +1,66 @@ +/* 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 +#include +#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. + */ +int full_write(int fd, const char *buf, int len) +{ + int cc; + int total; + + total = 0; + + while (len > 0) { + cc = write(fd, buf, len); + + if (cc < 0) + return -1; + + 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/get_console.c b/busybox/libbb/get_console.c new file mode 100644 index 000000000..3b36a59e7 --- /dev/null +++ b/busybox/libbb/get_console.c @@ -0,0 +1,129 @@ +/* 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 +#include +#include +#include +#include +#include "libbb.h" + + + + + +/* From */ +static const int KDGKBTYPE = 0x4B33; /* get keyboard type */ +static const int KB_84 = 0x01; +static const int KB_101 = 0x02; /* this is what we always answer */ + +int is_a_console(int fd) +{ + char arg; + + arg = 0; + return (ioctl(fd, KDGKBTYPE, &arg) == 0 + && ((arg == KB_101) || (arg == KB_84))); +} + +static int open_a_console(char *fnam) +{ + int fd; + + /* try read-only */ + 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); + + /* if failed, fail */ + if (fd < 0) + return -1; + + /* if not a console, fail */ + if (!is_a_console(fd)) { + close(fd); + return -1; + } + + /* success */ + 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). + * + * if tty_name is non-NULL, try this one instead. + */ + +int get_console_fd(char *tty_name) +{ + int fd; + + if (tty_name) { + if (-1 == (fd = open_a_console(tty_name))) + return -1; + else + return fd; + } + + fd = open_a_console(CURRENT_TTY); + if (fd >= 0) + return fd; + + fd = open_a_console(CURRENT_VC); + if (fd >= 0) + return fd; + + fd = open_a_console(CONSOLE_DEV); + if (fd >= 0) + return fd; + + for (fd = 0; fd < 3; fd++) + if (is_a_console(fd)) + return fd; + + error_msg("Couldn't get a file descriptor referring to the console"); + return -1; /* 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 index 000000000..f1ddfbde0 --- /dev/null +++ b/busybox/libbb/get_last_path_component.c @@ -0,0 +1,71 @@ +/* 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 +#include +#include "libbb.h" + + +char *get_last_path_component(char *path) +{ + char *s; + register char *ptr = path; + register char *prev = 0; + + while (*ptr) + ptr++; + s = ptr - 1; + + /* strip trailing slashes */ + while (s != path && *s == '/') { + *s-- = '\0'; + } + + /* find last component */ + ptr = path; + while (*ptr != '\0') { + if (*ptr == '/') + prev = ptr; + ptr++; + } + s = prev; + + if (s == NULL || s[1] == '\0') + return path; + else + return s+1; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/get_line_from_file.c b/busybox/libbb/get_line_from_file.c new file mode 100644 index 000000000..759481731 --- /dev/null +++ b/busybox/libbb/get_line_from_file.c @@ -0,0 +1,72 @@ +/* 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 +#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. */ +extern char *get_line_from_file(FILE *file) +{ + static const int GROWBY = 80; /* how large we will grow strings by */ + + int ch; + int idx = 0; + char *linebuf = NULL; + int linebufsz = 0; + + while (1) { + ch = fgetc(file); + if (ch == EOF) + break; + /* grow the line buffer as necessary */ + while (idx > linebufsz-2) + linebuf = xrealloc(linebuf, linebufsz += GROWBY); + linebuf[idx++] = (char)ch; + if ((char)ch == '\n') + break; + } + + if (idx == 0) + return NULL; + + linebuf[idx] = 0; + return linebuf; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/gz_open.c b/busybox/libbb/gz_open.c new file mode 100644 index 000000000..ef30ff894 --- /dev/null +++ b/busybox/libbb/gz_open.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include +#include "libbb.h" + +extern FILE *gz_open(FILE *compressed_file, int *pid) +{ + int unzip_pipe[2]; + + if (pipe(unzip_pipe)!=0) { + error_msg("pipe error"); + return(NULL); + } + if ((*pid = fork()) == -1) { + error_msg("fork failed"); + return(NULL); + } + if (*pid==0) { + /* child process */ + close(unzip_pipe[0]); + unzip(compressed_file, fdopen(unzip_pipe[1], "w")); + fflush(NULL); + fclose(compressed_file); + close(unzip_pipe[1]); + exit(EXIT_SUCCESS); + } + close(unzip_pipe[1]); + if (unzip_pipe[0] == -1) { + error_msg("gzip stream init failed"); + } + return(fdopen(unzip_pipe[0], "r")); +} diff --git a/busybox/libbb/herror_msg.c b/busybox/libbb/herror_msg.c new file mode 100644 index 000000000..f4210edad --- /dev/null +++ b/busybox/libbb/herror_msg.c @@ -0,0 +1,50 @@ +/* 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 +#include + +#include "libbb.h" + +extern void herror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + 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 index 000000000..0df5ed016 --- /dev/null +++ b/busybox/libbb/herror_msg_and_die.c @@ -0,0 +1,51 @@ +/* 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 +#include + +#include "libbb.h" + +extern void herror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + vherror_msg(s, p); + va_end(p); + exit(EXIT_FAILURE); +} + + +/* 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 index 000000000..7bdad36a9 --- /dev/null +++ b/busybox/libbb/human_readable.c @@ -0,0 +1,88 @@ +/* + * 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 +#include "libbb.h" + +const char *make_human_readable_str(unsigned 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 = ((unsigned long long) 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/inode_hash.c b/busybox/libbb/inode_hash.c new file mode 100644 index 000000000..790af8f31 --- /dev/null +++ b/busybox/libbb/inode_hash.c @@ -0,0 +1,106 @@ +/* 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 +#include +#include +#include "libbb.h" + +#define HASH_SIZE 311 /* Should be prime */ +#define hash_inode(i) ((i) % HASH_SIZE) + +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; +} + +/* 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; + } + } +} + + +/* 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 index 000000000..484597c5f --- /dev/null +++ b/busybox/libbb/interface.c @@ -0,0 +1,2168 @@ +/* + * 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.4 2001/07/19 22:28:02 andersen Exp $ + * + * Author: Fred N. van Kempen, + * 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 + * + * {1.34} - 19980630 - Arnaldo Carvalho de Melo + * - 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) + * stolen from net-tools-1.59 and stripped down for busybox by + * Erik Andersen + */ + +/* + * 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. + */ + +/* #define KEEP_UNUSED */ + +/* + * + * Protocol Families. + * + */ +#define HAVE_AFINET 1 +#undef HAVE_AFINET6 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 +#include +#endif +#include "libbb.h" + +#define _(x) x +#define _PATH_PROCNET_DEV "/proc/net/dev" +#define new(p) ((p) = xcalloc(1,sizeof(*(p)))) +#define KRELEASE(maj,min,patch) ((maj) * 65536 + (min)*256 + (patch)) + +static int procnetdev_vsn = 1; + + +/* Ugh. But libc5 doesn't provide POSIX types. */ +#include + + +#ifdef HAVE_HWSLIP +#include +#endif + +#if HAVE_AFINET6 + +#ifndef _LINUX_IN6_H +/* + * This is in linux/include/net/ipv6.h. + */ + +struct in6_ifreq { + struct in6_addr ifr6_addr; + __u32 ifr6_prefixlen; + unsigned int ifr6_ifindex; +}; + +#endif + +#endif /* HAVE_AFINET6 */ + +#if HAVE_AFIPX +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) +#include +#else +#include "ipx.h" +#endif +#endif +#if 0 +#include "net-support.h" +#include "pathnames.h" +#include "version.h" +#include "../intl.h" +#include "interface.h" +#include "sockets.h" +#include "util.h" +#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; +}; + +static struct aftype *aftypes[]; + +#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 + +#if 0 +extern int h_errno; /* some netdb.h versions don't export this */ +#endif + +/* cache */ +struct addr { + struct sockaddr_in addr; + char *name; + int host; + struct addr *next; +}; + +static struct addr *INET_nn = NULL; /* addr-to-name cache */ + +#ifdef KEEP_UNUSED +static int INET_resolve(char *name, struct sockaddr_in *sin, int hostfirst) +{ + struct hostent *hp; + struct netent *np; + + /* Grmpf. -FvK */ + sin->sin_family = AF_INET; + sin->sin_port = 0; + + /* Default is special, meaning 0.0.0.0. */ + if (!strcmp(name, "default")) { + sin->sin_addr.s_addr = INADDR_ANY; + return (1); + } + /* Look to see if it's a dotted quad. */ + if (inet_aton(name, &sin->sin_addr)) { + return 0; + } + /* If we expect this to be a hostname, try hostname database first */ +#ifdef DEBUG + if (hostfirst) fprintf (stderr, "gethostbyname (%s)\n", name); +#endif + if (hostfirst && + (hp = gethostbyname(name)) != (struct hostent *) NULL) { + memcpy((char *) &sin->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 + fprintf (stderr, "getnetbyname (%s)\n", name); +#endif + if ((np = getnetbyname(name)) != (struct netent *) NULL) { + sin->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 + fprintf (stderr, "gethostbyname (%s)\n", name); +#endif + if ((hp = gethostbyname(name)) == (struct hostent *) NULL) { + errno = h_errno; + return -1; + } + memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], + sizeof(struct in_addr)); + + return 0; +} +#endif /* KEEP_UNUSED */ + +/* numeric: & 0x8000: default instead of *, + * & 0x4000: host instead of net, + * & 0x0fff: don't resolve + */ +static 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 + fprintf(stderr, _("rresolve: unsupport address family %d !\n"), s_in->sin_family); +#endif + errno = EAFNOSUPPORT; + return (-1); + } + ad = (unsigned long) s_in->sin_addr.s_addr; +#ifdef DEBUG + fprintf (stderr, "rresolve: %08lx, mask %08x, num %08x \n", ad, netmask, numeric); +#endif + if (ad == INADDR_ANY) { + if ((numeric & 0x0FFF) == 0) { + if (numeric & 0x8000) + safe_strncpy(name, "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 + fprintf (stderr, "rresolve: found %s %08lx in cache\n", (host? "host": "net"), ad); +#endif + return (0); + } + pn = pn->next; + } + + host_ad = ntohl(ad); + np = NULL; + ent = NULL; + if (host) { +#ifdef DEBUG + fprintf (stderr, "gethostbyaddr (%08lx)\n", ad); +#endif + ent = gethostbyaddr((char *) &ad, 4, AF_INET); + if (ent != NULL) + safe_strncpy(name, ent->h_name, len); + } else { +#ifdef DEBUG + fprintf (stderr, "getnetbyaddr (%08lx)\n", host_ad); +#endif +#if 0 + np = getnetbyaddr(host_ad, AF_INET); + if (np != NULL) + safe_strncpy(name, np->n_name, len); +#endif + } + 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 = (char *) xmalloc(strlen(name) + 1); + strcpy(pn->name, name); + INET_nn = pn; + + return (0); +} + +#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 */ + +/* 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)) { + fprintf(stderr, _("Too much address family arguments.\n")); + return (0); + } + if (paft->flag) + (*paft->flag)++; + if (afname[0]) + strcat(afname, ","); + strcat(afname, paft->name); + break; + } + if (!paft->alias) { + fprintf(stderr, _("Unknown address family `%s'.\n"), 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, ',')) + fprintf(stderr, _("Please don't supply more than one address family.\n")); + 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) + fprintf(stderr, _("No usable address families found.\n")); + return sfd; +} + +/* like strcmp(), but knows about numbers */ +static int nstrcmp(const char *astr, const char *b) +{ + const char *a = astr; + + while (*a == *b) { + if (*a == '\0') + return 0; + a++; + b++; + } + if (isdigit(*a)) { + if (!isdigit(*b)) + return -1; + while (a > astr) { + a--; + if (!isdigit(*a)) { + a++; + break; + } + if (!isdigit(*b)) + return -1; + b--; + } + return atoi(a) > atoi(b) ? 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) { + fprintf(stderr, _("warning: no inet socket available: %s\n"), + strerror(errno)); + /* 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; +} + +static char *get_name(char *name, char *p) +{ + while (isspace(*p)) + p++; + while (*p) { + if (isspace(*p)) + break; + if (*p == ':') { /* could be an alias */ + char *dot = p, *dotname = name; + *name++ = *p++; + while (isdigit(*p)) + *name++ = *p++; + if (*p != ':') { /* it wasn't, backup */ + p = dot; + name = dotname; + } + if (*p == '\0') + return NULL; + p++; + break; + } + *name++ = *p++; + } + *name++ = '\0'; + return p; +} + +static int get_dev_fields(char *bp, struct interface *ife) +{ + switch (procnetdev_vsn) { + case 3: + sscanf(bp, + "%Lu %Lu %lu %lu %lu %lu %lu %lu %Lu %Lu %lu %lu %lu %lu %lu %lu", + &ife->stats.rx_bytes, + &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, + &ife->stats.rx_multicast, + + &ife->stats.tx_bytes, + &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); + break; + case 2: + sscanf(bp, "%Lu %Lu %lu %lu %lu %lu %Lu %Lu %lu %lu %lu %lu %lu", + &ife->stats.rx_bytes, + &ife->stats.rx_packets, + &ife->stats.rx_errors, + &ife->stats.rx_dropped, + &ife->stats.rx_fifo_errors, + &ife->stats.rx_frame_errors, + + &ife->stats.tx_bytes, + &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.rx_multicast = 0; + break; + case 1: + sscanf(bp, "%Lu %lu %lu %lu %lu %Lu %lu %lu %lu %lu %lu", + &ife->stats.rx_packets, + &ife->stats.rx_errors, + &ife->stats.rx_dropped, + &ife->stats.rx_fifo_errors, + &ife->stats.rx_frame_errors, + + &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.rx_bytes = 0; + ife->stats.tx_bytes = 0; + ife->stats.rx_multicast = 0; + break; + } + return 0; +} + +static inline int procnetdev_version(char *buf) +{ + if (strstr(buf, "compressed")) + return 3; + if (strstr(buf, "bytes")) + return 2; + return 1; +} + +static int if_readlist_proc(char *target) +{ + static int proc_read; + FILE *fh; + char buf[512]; + struct interface *ife; + int err; + + if (proc_read) + return 0; + if (!target) + proc_read = 1; + + fh = fopen(_PATH_PROCNET_DEV, "r"); + if (!fh) { + fprintf(stderr, _("Warning: cannot open %s (%s). Limited output.\n"), + _PATH_PROCNET_DEV, strerror(errno)); + return if_readconf(); + } + fgets(buf, sizeof buf, fh); /* eat line */ + fgets(buf, sizeof buf, fh); + +#if 0 /* pretty, but can't cope with missing fields */ + fmt = proc_gen_fmt(_PATH_PROCNET_DEV, 1, fh, + "face", "", /* parsed separately */ + "bytes", "%lu", + "packets", "%lu", + "errs", "%lu", + "drop", "%lu", + "fifo", "%lu", + "frame", "%lu", + "compressed", "%lu", + "multicast", "%lu", + "bytes", "%lu", + "packets", "%lu", + "errs", "%lu", + "drop", "%lu", + "fifo", "%lu", + "colls", "%lu", + "carrier", "%lu", + "compressed", "%lu", + NULL); + if (!fmt) + return -1; +#else + procnetdev_vsn = procnetdev_version(buf); +#endif + + err = 0; + while (fgets(buf, sizeof buf, fh)) { + char *s, name[IFNAMSIZ]; + s = get_name(name, buf); + ife = add_interface(name); + get_dev_fields(s, ife); + ife->statistics_valid = 1; + if (target && !strcmp(target,name)) + break; + } + if (ferror(fh)) { + perror(_PATH_PROCNET_DEV); + err = -1; + proc_read = 0; + } + +#if 0 + free(fmt); +#endif + 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); + } + fprintf(stderr, _("%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 +#include + +static struct hwtype ether_hwtype; + +/* 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 + fprintf(stderr, _("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 + fprintf(stderr, _("in_ether(%s): invalid ether address!\n"), 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 == ':') { + if (i == ETH_ALEN) { +#ifdef DEBUG + fprintf(stderr, _("in_ether(%s): trailing : ignored!\n"), + orig) +#endif + ; /* nothing */ + } + bufp++; + } + } + + /* That's it. Any trailing junk? */ + if ((i == ETH_ALEN) && (*bufp != '\0')) { +#ifdef DEBUG + fprintf(stderr, _("in_ether(%s): trailing junk!\n"), orig); + errno = EINVAL; + return (-1); +#endif + } +#ifdef DEBUG + fprintf(stderr, "in_ether(%s): %s\n", 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 + +#ifdef KEEP_UNUSED +/* Start the PPP encapsulation on the file descriptor. */ +static int do_ppp(int fd) +{ + fprintf(stderr, _("You cannot start PPP with this program.\n")); + 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 + ðer_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 +static const char *if_port_text[][4] = +{ + /* Keep in step with */ + {"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}, +}; +#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\0k\0M"; + +static void print_bytes_scaled(unsigned long long ull, const char *end) +{ + unsigned long long int_part; + unsigned long frac_part; + const char *ext; + int i; + + frac_part = 0; + ext = TRext; + int_part = ull; + for (i=0 ; i<2 ; i++) { + if (int_part >= 1024) { + frac_part = ((int_part % 1024) * 10) / 1024; + int_part /= 1024; + ext += 2; /* Kb, Mb */ + } + } + + printf("X bytes:%Lu (%Lu.%lu %sb)%s", ull, int_part, frac_part, ext, end); +} + +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; + extern struct aftype inet6_aftype; + 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 + /* FIXME: should be integrated into interface.c. */ + + 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]); + inet6_aftype.input(1, addr6, (struct sockaddr *) &sap); + printf(_(" inet6 addr: %s/%d"), + inet6_aftype.sprint((struct sockaddr *) &sap, 1), plen); + printf(_(" Scope:")); + switch (scope) { + 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] ")); + if (ptr->flags & IFF_UP) + printf(_("UP ")); + if (ptr->flags & IFF_BROADCAST) + printf(_("BROADCAST ")); + if (ptr->flags & IFF_DEBUG) + printf(_("DEBUG ")); + if (ptr->flags & IFF_LOOPBACK) + printf(_("LOOPBACK ")); + if (ptr->flags & IFF_POINTOPOINT) + printf(_("POINTOPOINT ")); + if (ptr->flags & IFF_NOTRAILERS) + printf(_("NOTRAILERS ")); + if (ptr->flags & IFF_RUNNING) + printf(_("RUNNING ")); + if (ptr->flags & IFF_NOARP) + printf(_("NOARP ")); + if (ptr->flags & IFF_PROMISC) + printf(_("PROMISC ")); + if (ptr->flags & IFF_ALLMULTI) + printf(_("ALLMULTI ")); + if (ptr->flags & IFF_SLAVE) + printf(_("SLAVE ")); + if (ptr->flags & IFF_MASTER) + printf(_("MASTER ")); + if (ptr->flags & IFF_MULTICAST) + printf(_("MULTICAST ")); +#ifdef HAVE_DYNAMIC + if (ptr->flags & IFF_DYNAMIC) + printf(_("DYNAMIC ")); +#endif + /* 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%x "), 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) { + 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 index 000000000..65f4fee00 --- /dev/null +++ b/busybox/libbb/isdirectory.c @@ -0,0 +1,71 @@ +/* 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 +#include +#include +#include "libbb.h" + +/* + * Return TRUE if a fileName is a directory. + * Nonexistant files return FALSE. + */ +int is_directory(const char *fileName, const int followLinks, struct stat *statBuf) +{ + int status; + int didMalloc = 0; + + if (statBuf == NULL) { + statBuf = (struct stat *)xmalloc(sizeof(struct stat)); + ++didMalloc; + } + + if (followLinks == TRUE) + status = stat(fileName, statBuf); + else + status = lstat(fileName, statBuf); + + if (status < 0 || !(S_ISDIR(statBuf->st_mode))) { + status = FALSE; + } + else status = TRUE; + + if (didMalloc) { + free(statBuf); + statBuf = NULL; + } + 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 index 000000000..09cd582c4 --- /dev/null +++ b/busybox/libbb/kernel_version.c @@ -0,0 +1,66 @@ +/* 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 +#include +#include +#include /* 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) { } + */ +extern int get_kernel_revision(void) +{ + struct utsname name; + char *s; + int i, r; + + if (uname(&name) == -1) { + 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 index 000000000..4e2ee92ed --- /dev/null +++ b/busybox/libbb/last_char_is.c @@ -0,0 +1,40 @@ +/* + * busybox library eXtended function + * + * Copyright (C) 2001 Larry Doolittle, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#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; + if (!s) + return NULL; + sret = (char *)s+strlen(s)-1; + if (sret>=s && *sret == c) { + return sret; + } else { + return NULL; + } +} diff --git a/busybox/libbb/libbb.h b/busybox/libbb/libbb.h new file mode 100644 index 000000000..04ed2ae82 --- /dev/null +++ b/busybox/libbb/libbb.h @@ -0,0 +1,325 @@ +/* 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 __LIBBB_H__ +#define __LIBBB_H__ 1 + +#include +#include +#include +#include + +#include + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#include + +#ifndef _BB_INTERNAL_H_ +#include "../busybox.h" +#endif + +#if (__GNU_LIBRARY__ < 5) && (!defined __dietlibc__) +/* libc5 doesn't define socklen_t */ +typedef unsigned int socklen_t; +/* libc5 doesn't implement BSD 4.4 daemon() */ +extern int daemon (int nochdir, int noclose); +/* libc5 doesn't implement strtok_r */ +char *strtok_r(char *s, const char *delim, char **ptrptr); +#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 + +static inline int is_decimal(int ch) { return ((ch >= '0') && (ch <= '9')); } +static inline int is_octal(int ch) { return ((ch >= '0') && (ch <= '7')); } + +/* 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 show_usage(void) __attribute__ ((noreturn)); +extern void error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); +extern void error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))); +extern void perror_msg(const char *s, ...); +extern void perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn)); +extern void vherror_msg(const char *s, va_list p); +extern void herror_msg(const char *s, ...); +extern void herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn)); + +/* These two are used internally -- you shouldn't need to use them */ +extern void verror_msg(const char *s, va_list p); +extern void vperror_msg(const char *s, va_list p); + +const char *mode_string(int mode); +const char *time_string(time_t timeVal); +int is_directory(const char *name, int followLinks, struct stat *statBuf); +int isDevice(const char *name); + +int remove_file(const char *path, int flags); +int copy_file(const char *source, const char *dest, int flags); +int copy_file_chunk(FILE *src_file, FILE *dst_file, unsigned long long chunksize); +char *buildName(const char *dirName, const char *fileName); +int makeString(int argc, const char **argv, char *buf, int bufLen); +char *getChunk(int size); +char *chunkstrdup(const char *str); +void freeChunks(void); +ssize_t safe_read(int fd, void *buf, size_t count); +int full_write(int fd, const char *buf, int len); +int full_read(int fd, char *buf, int len); +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 parse_mode( const char* s, mode_t* theMode); + +extern int get_kernel_revision(void); + +extern int get_console_fd(char* tty_name); +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 atoi_w_units (const char *cp); +extern pid_t* find_pid_by_name( char* pidName); +extern char *find_real_root_device_name(const char* name); +extern char *get_line_from_file(FILE *file); +extern void print_file(FILE *file); +extern int copyfd(int fd1, int fd2); +extern int print_file_by_name(char *filename); +extern char process_escape_sequence(const char **ptr); +extern char *get_last_path_component(char *path); +extern FILE *wfopen(const char *path, const char *mode); +extern FILE *xfopen(const char *path, const char *mode); +extern void chomp(char *s); +extern void trim(char *s); +extern struct BB_applet *find_applet_by_name(const char *name); +void run_applet_by_name(const char *name, int argc, char **argv); + +#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); +extern char *xstrdup (const char *s); +#endif +extern char *xstrndup (const char *s, int n); +extern char * safe_strncpy(char *dst, const char *src, size_t size); + +struct suffix_mult { + const char *suffix; + int mult; +}; + +extern unsigned long parse_number(const char *numstr, + const struct suffix_mult *suffixes); + + +/* 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 void my_getpwuid(char *name, long uid); +extern void my_getgrgid(char *group, long gid); +extern long my_getpwnamegid(const char *name); + +extern int device_open(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); + +void syslog_msg_with_name(const char *name, int facility, int pri, const char *msg); +void syslog_msg(int facility, int pri, const char *msg); + +/* 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 size, unsigned long block_size, unsigned long display_unit); + +int 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 *last_char_is(const char *s, int c); + +extern long arith (const char *startbuf, int *errcode); + +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; +file_header_t *get_header_ar(FILE *in_file); +file_header_t *get_header_cpio(FILE *src_stream); +file_header_t *get_header_tar(FILE *tar_stream); + +enum extract_functions_e { + extract_verbose_list = 1, + extract_list = 2, + extract_one_to_buffer = 4, + extract_to_stdout = 8, + extract_all_to_fs = 16, + extract_preserve_date = 32, + extract_data_tar_gz = 64, + extract_control_tar_gz = 128, + extract_unzip_only = 256, + extract_unconditional = 512, + extract_create_leading_dirs = 1024, + extract_quiet = 2048, + extract_exclude_list = 4096 +}; +char *unarchive(FILE *src_stream, FILE *out_stream, file_header_t *(*get_header)(FILE *), + const int extract_function, const char *prefix, char **extract_names); +char *deb_extract(const char *package_filename, FILE *out_stream, const int extract_function, + const char *prefix, const char *filename); +int read_package_field(const char *package_buffer, char **field_name, char **field_value); +char *fgets_str(FILE *file, const char *terminating_string); + +extern int unzip(FILE *l_in_file, FILE *l_out_file); +extern void gz_close(int gunzip_pid); +extern FILE *gz_open(FILE *compressed_file, int *pid); + +extern struct hostent *xgethostbyname(const char *name); +extern int create_icmp_socket(void); + +char *dirname (const char *path); + +int make_directory (char *path, long mode, int flags); + +const char *u_signal_names(const char *str_sig, int *signo, int startnum); + +#define CT_AUTO 0 +#define CT_UNIX2DOS 1 +#define CT_DOS2UNIX 2 +/* extern int convert(char *fn, int ConvType); */ + +enum { + FILEUTILS_PRESERVE_STATUS = 1, + FILEUTILS_PRESERVE_SYMLINKS = 2, + FILEUTILS_RECUR = 4, + FILEUTILS_FORCE = 8, + FILEUTILS_INTERACTIVE = 16 +}; + +extern const char *applet_name; +extern const char * const full_version; +extern const char * const name_too_long; +extern const char * const omitting_directory; +extern const char * const not_a_directory; +extern const char * const memory_exhausted; +extern const char * const invalid_date; +extern const char * const invalid_option; +extern const char * const io_error; +extern const char * const dash_dash_help; +extern const char * const write_error; +extern const char * const too_few_args; +extern const char * const name_longer_than_foo; +extern const char * const unknown; +extern const char * const can_not_create_raw_socket; + +#ifdef BB_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" +# define SC_0 "/dev/tts/0" +# define SC_1 "/dev/tts/1" +# define VC_FORMAT "/dev/vc/%d" +# define SC_FORMAT "/dev/tts/%d" +#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" +# define SC_0 "/dev/ttyS0" +# define SC_1 "/dev/ttyS1" +# define VC_FORMAT "/dev/tty%d" +# define SC_FORMAT "/dev/ttyS%d" +#endif + +/* The following devices are the same on devfs and non-devfs systems. */ +#define CURRENT_TTY "/dev/tty" +#define CONSOLE_DEV "/dev/console" + +#endif /* __LIBBB_H__ */ diff --git a/busybox/libbb/libc5.c b/busybox/libbb/libc5.c new file mode 100644 index 000000000..20295fd4b --- /dev/null +++ b/busybox/libbb/libc5.c @@ -0,0 +1,167 @@ +/* vi: set sw=4 ts=4: */ + + +#include +#include +#include +#include +#include +#include + + +#if __GNU_LIBRARY__ < 5 + + +/* Copyright (C) 1991 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 Library General Public License as +published by the Free Software Foundation; either version 2 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* + * Modified by Manuel Novoa III Mar 1, 2001 + * + * Converted original strtok.c code of strtok to __strtok_r. + * Cleaned up logic and reduced code size. + */ + + +char *strtok_r(char *s, const char *delim, char **save_ptr) +{ + char *token; + + token = 0; /* Initialize to no token. */ + + if (s == 0) { /* If not first time called... */ + s = *save_ptr; /* restart from where we left off. */ + } + + if (s != 0) { /* If not finished... */ + *save_ptr = 0; + + s += strspn(s, delim); /* Skip past any leading delimiters. */ + if (*s != '\0') { /* We have a token. */ + token = s; + *save_ptr = strpbrk(token, delim); /* Find token's end. */ + if (*save_ptr != 0) { + /* Terminate the token and make SAVE_PTR point past it. */ + *(*save_ptr)++ = '\0'; + } + } + } + + return token; +} + +/* Basically getdelim() with the delimiter hard wired to '\n' */ +ssize_t getline(char **linebuf, size_t *n, FILE *file) +{ + return (getdelim (linebuf, n, '\n', file)); +} + + +/* + * daemon implementation for uClibc + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Modified for uClibc by Erik Andersen + * , + * + * The uClibc 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. + * + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the GNU C Library; see the file COPYING.LIB. 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. + */ + +int daemon( int nochdir, int noclose ) +{ + int fd; + + switch (fork()) { + case -1: + return(-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return(-1); + + 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); + } + return(0); +} + + +/*- + * Copyright (c) 1990, 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. + * + * 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. + */ + + +#endif + diff --git a/busybox/libbb/loop.c b/busybox/libbb/loop.c new file mode 100644 index 000000000..4754b8da1 --- /dev/null +++ b/busybox/libbb/loop.c @@ -0,0 +1,128 @@ +/* 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 +#include +#include +#include +#include +#include +#include "libbb.h" +#include "loop.h" /* Pull in loop device support */ + +extern int del_loop(const char *device) +{ + int fd; + + if ((fd = open(device, O_RDONLY)) < 0) { + perror_msg("%s", device); + return (FALSE); + } + if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { + 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)) { + perror_msg("%s", file); + return 1; + } + if ((fd = open(device, mode)) < 0) { + close(ffd); + 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) { + 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); + 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, "/dev/loop%d", 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 index 000000000..0a9d7b160 --- /dev/null +++ b/busybox/libbb/make_directory.c @@ -0,0 +1,67 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini make_directory implementation for busybox + * + * Copyright (C) 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 +#include +#include +#include +#include +#include + +#include "libbb.h" + +/* Create the directory PATH with mode MODE, or the default if MODE is -1. + * Also create parent directories as necessary if flags contains + * FILEUTILS_RECUR. */ + +int make_directory (char *path, long mode, int flags) +{ + if (!(flags & FILEUTILS_RECUR)) { + if (mkdir (path, 0777) < 0) { + perror_msg ("Cannot create directory `%s'", path); + return -1; + } + + if (mode != -1 && chmod (path, mode) < 0) { + perror_msg ("Cannot set permissions of directory `%s'", path); + return -1; + } + } else { + struct stat st; + + if (stat (path, &st) < 0 && errno == ENOENT) { + char *parent = dirname (path); + mode_t mask = umask (0); + umask (mask); + + if (make_directory (parent, (0777 & ~mask) | 0300, + FILEUTILS_RECUR) < 0) + return -1; + free (parent); + + if (make_directory (path, mode, 0) < 0) + return -1; + } + } + + return 0; +} diff --git a/busybox/libbb/messages.c b/busybox/libbb/messages.c new file mode 100644 index 000000000..552c3ab5b --- /dev/null +++ b/busybox/libbb/messages.c @@ -0,0 +1,67 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2001 by Lineo, inc. + * Written by 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 "libbb.h" + +#ifdef L_full_version + const char * const full_version = BB_BANNER " multi-call binary"; +#endif +#ifdef L_name_too_long + const char * const name_too_long = "file name too long"; +#endif + +#ifdef L_omitting_directory + const char * const omitting_directory = "%s: omitting directory"; +#endif +#ifdef L_not_a_directory + const char * const not_a_directory = "%s: not a directory"; +#endif +#ifdef L_memory_exhausted + const char * const memory_exhausted = "memory exhausted"; +#endif +#ifdef L_invalid_date + const char * const invalid_date = "invalid date `%s'"; +#endif +#ifdef L_invalid_option + const char * const invalid_option = "invalid option -- %c"; +#endif +#ifdef L_io_error + const char * const io_error = "%s: input/output error -- %s"; +#endif +#ifdef L_dash_dash_help + const char * const dash_dash_help = "--help"; +#endif +#ifdef L_write_error + const char * const write_error = "Write Error"; +#endif +#ifdef L_too_few_args + const char * const too_few_args = "too few arguments"; +#endif +#ifdef L_name_longer_than_foo + const char * const name_longer_than_foo = "Names longer than %d chars not supported."; +#endif +#ifdef L_unknown + const char * const unknown = "(unknown)"; +#endif + +#ifdef L_can_not_create_raw_socket + const char * const can_not_create_raw_socket = "can`t create raw socket"; +#endif diff --git a/busybox/libbb/mk_loop_h.sh b/busybox/libbb/mk_loop_h.sh new file mode 100755 index 000000000..71c987376 --- /dev/null +++ b/busybox/libbb/mk_loop_h.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Figure out (i) the type of dev_t (ii) the defines for loop stuff +# +# Output of this script is normally redirected to "loop.h". + +# Since 1.3.79 there is an include file +# that defines __kernel_dev_t. +# (The file itself appeared in 1.3.78, but there it defined __dev_t.) +# If it exists, we use it, or, rather, which +# avoids namespace pollution. Otherwise we guess that __kernel_dev_t +# is an unsigned short (which is true on i386, but false on alpha). + +# BUG: This test is actually broken if your gcc is not configured to +# search /usr/include, as may well happen with cross-compilers. +# It would be better to ask $(CC) if these files can be found. + +if [ -f /usr/include/linux/posix_types.h ]; then + echo '#include ' + echo '#undef dev_t' + echo '#define dev_t __kernel_dev_t' +else + echo '#undef dev_t' + echo '#define dev_t unsigned short' +fi + +# Next we have to find the loop stuff itself. +# First try kernel source, then a private version. + +if [ -f /usr/include/linux/loop.h ]; then + echo '#include ' +else + echo '#include "real_loop.h"' +fi + +echo '#undef dev_t' + diff --git a/busybox/libbb/mode_string.c b/busybox/libbb/mode_string.c new file mode 100644 index 000000000..0a3d6e6f0 --- /dev/null +++ b/busybox/libbb/mode_string.c @@ -0,0 +1,82 @@ +/* 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 +#include "libbb.h" + + + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) + +/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */ +static const mode_t SBIT[] = { + 0, 0, S_ISUID, + 0, 0, S_ISGID, + 0, 0, S_ISVTX +}; + +/* The 9 mode bits to test */ +static const mode_t MBIT[] = { + S_IRUSR, S_IWUSR, S_IXUSR, + S_IRGRP, S_IWGRP, S_IXGRP, + S_IROTH, S_IWOTH, S_IXOTH +}; + +static const char MODE1[] = "rwxrwxrwx"; +static const char MODE0[] = "---------"; +static const char SMODE1[] = "..s..s..t"; +static const char SMODE0[] = "..S..S..T"; + +/* + * Return the standard ls-like mode string from a file mode. + * This is static and so is overwritten on each call. + */ +const char *mode_string(int mode) +{ + static char buf[12]; + + int i; + + buf[0] = TYPECHAR(mode); + for (i = 0; i < 9; i++) { + if (mode & SBIT[i]) + buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i]; + else + buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i]; + } + return buf; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/module_syscalls.c b/busybox/libbb/module_syscalls.c new file mode 100644 index 000000000..36b75fb93 --- /dev/null +++ b/busybox/libbb/module_syscalls.c @@ -0,0 +1,88 @@ +/* vi: set sw=4 ts=4: */ +/* + * some system calls possibly missing from libc + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ +#include +#ifndef __UCLIBC__ +#include +#endif +#include "libbb.h" + + +#if __GNU_LIBRARY__ < 5 +/* These syscalls are not included as part of libc5 */ +_syscall1(int, delete_module, const char *, name); +_syscall1(int, get_kernel_syms, __ptr_t, ks); + +/* 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. */ +_syscall5(int, init_module, void *, first, void *, second, void *, third, + void *, fourth, void *, fifth); + +#ifndef __NR_query_module +#warning This kernel does not support the query_module syscall +#warning -> The query_module system call is being stubbed out... +int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret) +{ + fprintf(stderr, "\n\nTo make this application work, you will need to recompile\n"); + fprintf(stderr, "with a kernel supporting the query_module system call. -Erik\n\n"); + errno=ENOSYS; + return -1; +} +#else +_syscall5(int, query_module, const char *, name, int, which, + void *, buf, size_t, bufsize, size_t*, ret); +#endif + +/* Jump through hoops to fixup error return codes */ +#define __NR___create_module __NR_create_module +static inline _syscall2(long, __create_module, const char *, name, size_t, size) +unsigned long create_module(const char *name, size_t size) +{ + long ret = __create_module(name, size); + + if (ret == -1 && errno > 125) { + ret = -errno; + errno = 0; + } + return ret; +} + +#endif /* __GNU_LIBRARY__ < 5 */ + + +/* 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 index 000000000..28c9978ef --- /dev/null +++ b/busybox/libbb/mtab.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include +#include "libbb.h" + +extern const char mtab_file[]; /* Defined in utility.c */ +static const int MS_RDONLY = 1; /* Mount read-only. */ + +void erase_mtab(const char *name) +{ + struct mntent entries[20]; + int count = 0; + FILE *mountTable = setmntent(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) { + perror_msg("%s", mtab_file); + return; + } + + while ((m = getmntent(mountTable)) != 0) { + 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(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) + perror_msg("%s", mtab_file); +} + +void write_mtab(char *blockDevice, char *directory, + char *filesystemType, long flags, char *string_flags) +{ + FILE *mountTable = setmntent(mtab_file, "a+"); + struct mntent m; + + if (mountTable == 0) { + perror_msg("%s", 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 index 000000000..c872b9a71 --- /dev/null +++ b/busybox/libbb/mtab_file.c @@ -0,0 +1,52 @@ +/* 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 +#include "libbb.h" + + +/* Busybox mount uses either /proc/mounts or /dev/mtab to + * get the list of currently mounted filesystems */ +#if defined BB_FEATURE_MOUNT_MTAB_SUPPORT +const char mtab_file[] = "/etc/mtab"; +#else +# if defined BB_FEATURE_USE_DEVPS_PATCH + const char mtab_file[] = "/dev/mtab"; +# else + const char mtab_file[] = "/proc/mounts"; +# endif +#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 index 000000000..fabd4776c --- /dev/null +++ b/busybox/libbb/my_getgrgid.c @@ -0,0 +1,55 @@ +/* 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 +#include +#include "../pwd_grp/pwd.h" +#include "../pwd_grp/grp.h" +#include "libbb.h" + + +/* gets a groupname given a gid */ +void my_getgrgid(char *group, long gid) +{ + struct group *mygroup; + + mygroup = getgrgid(gid); + if (mygroup==NULL) + sprintf(group, "%-8ld ", (long)gid); + else + strcpy(group, mygroup->gr_name); +} + + +/* 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 index 000000000..e3226a275 --- /dev/null +++ b/busybox/libbb/my_getgrnam.c @@ -0,0 +1,56 @@ +/* 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 +#include +#include "../pwd_grp/pwd.h" +#include "../pwd_grp/grp.h" +#include "libbb.h" + + + +/* returns a gid given a group name */ +long my_getgrnam(const char *name) +{ + struct group *mygroup; + + mygroup = getgrnam(name); + if (mygroup==NULL) + 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 index 000000000..ae73ae7f1 --- /dev/null +++ b/busybox/libbb/my_getpwnam.c @@ -0,0 +1,56 @@ +/* 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 +#include +#include "../pwd_grp/pwd.h" +#include "../pwd_grp/grp.h" +#include "libbb.h" + + + +/* returns a uid given a username */ +long my_getpwnam(const char *name) +{ + struct passwd *myuser; + + myuser = getpwnam(name); + if (myuser==NULL) + 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_getpwnamegid.c b/busybox/libbb/my_getpwnamegid.c new file mode 100644 index 000000000..fb3d148ce --- /dev/null +++ b/busybox/libbb/my_getpwnamegid.c @@ -0,0 +1,61 @@ +/* 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 +#include +#include "../pwd_grp/pwd.h" +#include "../pwd_grp/grp.h" +#include "libbb.h" + + + +/* gets a gid given a user name */ +long my_getpwnamegid(const char *name) +{ + struct group *mygroup; + struct passwd *myuser; + + myuser=getpwnam(name); + if (myuser==NULL) + error_msg_and_die("unknown user name: %s", name); + + mygroup = getgrgid(myuser->pw_gid); + if (mygroup==NULL) + error_msg_and_die("unknown gid %ld", (long)myuser->pw_gid); + + 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_getpwuid.c b/busybox/libbb/my_getpwuid.c new file mode 100644 index 000000000..46c7a884a --- /dev/null +++ b/busybox/libbb/my_getpwuid.c @@ -0,0 +1,55 @@ +/* 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 +#include +#include "../pwd_grp/pwd.h" +#include "../pwd_grp/grp.h" +#include "libbb.h" + + + +/* gets a username given a uid */ +void my_getpwuid(char *name, long uid) +{ + struct passwd *myuser; + + myuser = getpwuid(uid); + if (myuser==NULL) + sprintf(name, "%-8ld ", (long)uid); + else + strcpy(name, myuser->pw_name); +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/parse_mode.c b/busybox/libbb/parse_mode.c new file mode 100644 index 000000000..30d2f21cf --- /dev/null +++ b/busybox/libbb/parse_mode.c @@ -0,0 +1,138 @@ +/* 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 +#include +#include "libbb.h" + + +/* This function parses the sort of string you might pass + * to chmod (i.e., [ugoa]{+|-|=}[rwxst] ) and returns the + * correct mode described by the string. */ +extern int parse_mode(const char *s, mode_t * theMode) +{ + static const mode_t group_set[] = { + S_ISUID | S_IRWXU, /* u */ + S_ISGID | S_IRWXG, /* g */ + S_IRWXO, /* o */ + S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO /* a */ + }; + + static const mode_t mode_set[] = { + S_IRUSR | S_IRGRP | S_IROTH, /* r */ + S_IWUSR | S_IWGRP | S_IWOTH, /* w */ + S_IXUSR | S_IXGRP | S_IXOTH, /* x */ + S_ISUID | S_ISGID, /* s */ + S_ISVTX /* t */ + }; + + static const char group_chars[] = "ugoa"; + static const char mode_chars[] = "rwxst"; + + const char *p; + + mode_t andMode = + S_ISVTX | S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; + mode_t orMode = 0; + mode_t mode; + mode_t groups; + char type; + char c; + + if (s==NULL) { + return (FALSE); + } + + do { + mode = 0; + groups = 0; + NEXT_GROUP: + if ((c = *s++) == '\0') { + return -1; + } + for (p=group_chars ; *p ; p++) { + if (*p == c) { + groups |= group_set[(int)(p-group_chars)]; + goto NEXT_GROUP; + } + } + switch (c) { + case '=': + case '+': + case '-': + type = c; + if (groups == 0) { /* The default is "all" */ + groups |= S_ISUID | S_ISGID | S_ISVTX + | S_IRWXU | S_IRWXG | S_IRWXO; + } + break; + default: + if ((c < '0') || (c > '7') || (mode | groups)) { + return (FALSE); + } else { + *theMode = strtol(--s, NULL, 8); + return (TRUE); + } + } + + NEXT_MODE: + if (((c = *s++) != '\0') && (c != ',')) { + for (p=mode_chars ; *p ; p++) { + if (*p == c) { + mode |= mode_set[(int)(p-mode_chars)]; + goto NEXT_MODE; + } + } + break; /* We're done so break out of loop.*/ + } + switch (type) { + case '=': + andMode &= ~(groups); /* Now fall through. */ + case '+': + orMode |= mode & groups; + break; + case '-': + andMode &= ~(mode & groups); + orMode &= ~(mode & groups); + break; + } + } while (c == ','); + + *theMode &= andMode; + *theMode |= orMode; + + return TRUE; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/parse_number.c b/busybox/libbb/parse_number.c new file mode 100644 index 000000000..c90511dca --- /dev/null +++ b/busybox/libbb/parse_number.c @@ -0,0 +1,74 @@ +/* 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 +#include +#include +#include "libbb.h" + + +unsigned long parse_number(const char *numstr, + const struct suffix_mult *suffixes) +{ + const struct suffix_mult *sm; + unsigned long int ret; + int len; + char *end; + + ret = strtoul(numstr, &end, 10); + if (numstr == end) + error_msg_and_die("invalid number `%s'", numstr); + while (end[0] != '\0') { + sm = suffixes; + while ( sm != 0 ) { + if(sm->suffix) { + len = strlen(sm->suffix); + if (strncmp(sm->suffix, end, len) == 0) { + ret *= sm->mult; + end += len; + break; + } + sm++; + + } else + sm = 0; + } + if (sm == 0) + error_msg_and_die("invalid number `%s'", numstr); + } + return ret; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/perror_msg.c b/busybox/libbb/perror_msg.c new file mode 100644 index 000000000..18c71ab1c --- /dev/null +++ b/busybox/libbb/perror_msg.c @@ -0,0 +1,51 @@ +/* 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 +#include +#include +#include +#include "libbb.h" + +extern void perror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + 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 index 000000000..9d304a26b --- /dev/null +++ b/busybox/libbb/perror_msg_and_die.c @@ -0,0 +1,52 @@ +/* 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 +#include +#include +#include +#include "libbb.h" + +extern void perror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + vperror_msg(s, p); + va_end(p); + exit(EXIT_FAILURE); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/print_file.c b/busybox/libbb/print_file.c new file mode 100644 index 000000000..bfedc5eff --- /dev/null +++ b/busybox/libbb/print_file.c @@ -0,0 +1,61 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2001 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 +#include +#include "libbb.h" + + +extern void print_file(FILE *file) +{ + fflush(stdout); + copyfd(fileno(file), fileno(stdout)); + fclose(file); +} + +extern int print_file_by_name(char *filename) +{ + struct stat statBuf; + int status = TRUE; + + if(is_directory(filename, TRUE, &statBuf)==TRUE) { + error_msg("%s: Is directory", filename); + status = FALSE; + } else { + FILE *f = wfopen(filename, "r"); + if(f!=NULL) + print_file(f); + else + status = FALSE; + } + + return status; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/process_escape_sequence.c b/busybox/libbb/process_escape_sequence.c new file mode 100644 index 000000000..67b0490ce --- /dev/null +++ b/busybox/libbb/process_escape_sequence.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) Manuel Nova III + * and Vladimir Oleynik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "libbb.h" + + + +char 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; + int num_digits; + unsigned int n; + + n = 0; + q = *ptr; + + for ( num_digits = 0 ; num_digits < 3 ; ++num_digits) { + if ((*q < '0') || (*q > '7')) { /* not a digit? */ + break; + } + n = n * 8 + (*q++ - '0'); + } + + if (num_digits == 0) { /* mnemonic escape sequence? */ + for (p=charmap ; *p ; p++) { + if (*p == *q) { + q++; + break; + } + } + n = *(p+(sizeof(charmap)/2)); + } + + /* doesn't hurt to fall through to here from mnemonic case */ + if (n > UCHAR_MAX) { /* is octal code too big for a char? */ + n /= 8; /* adjust value and */ + --q; /* back up one char */ + } + + *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/read_package_field.c b/busybox/libbb/read_package_field.c new file mode 100644 index 000000000..f561df831 --- /dev/null +++ b/busybox/libbb/read_package_field.c @@ -0,0 +1,91 @@ +#include +#include +#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 == TRUE) { + /* 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 = xstrndup(&package_buffer[offset_name_start], name_length); + } + if (value_length > 0) { + *field_value = xstrndup(&package_buffer[offset_value_start], value_length); + } else { + *field_value = NULL; + } + return(next_offset); +} + diff --git a/busybox/libbb/real_loop.h b/busybox/libbb/real_loop.h new file mode 100644 index 000000000..1bd7fa87a --- /dev/null +++ b/busybox/libbb/real_loop.h @@ -0,0 +1,37 @@ +/* + * include/linux/loop.h + * + * Written by Theodore Ts'o, 3/29/93. + * + * Copyright 1993 by Theodore Ts'o. Redistribution of this file is + * permitted under the GNU Public License. + */ + +#define LO_NAME_SIZE 64 +#define LO_KEY_SIZE 32 + +struct loop_info { + int lo_number; /* ioctl r/o */ + dev_t lo_device; /* ioctl r/o */ + unsigned long lo_inode; /* ioctl r/o */ + dev_t lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned long lo_init[2]; + char reserved[4]; +}; + +#define LO_CRYPT_NONE 0 +#define LO_CRYPT_XOR 1 +#define LO_CRYPT_DES 2 +#define LO_CRYPT_IDEA 3 +#define MAX_LO_CRYPT 4 + +#define LOOP_SET_FD 0x4C00 +#define LOOP_CLR_FD 0x4C01 +#define LOOP_SET_STATUS 0x4C02 +#define LOOP_GET_STATUS 0x4C03 diff --git a/busybox/libbb/recursive_action.c b/busybox/libbb/recursive_action.c new file mode 100644 index 000000000..6672db17f --- /dev/null +++ b/busybox/libbb/recursive_action.c @@ -0,0 +1,150 @@ +/* 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 +#include +#include +#include +#include /* 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 == TRUE) + status = stat(fileName, &statbuf); + else + status = lstat(fileName, &statbuf); + + if (status < 0) { +#ifdef DEBUG_RECURS_ACTION + fprintf(stderr, + "status=%d followLinks=%d TRUE=%d\n", + status, followLinks, TRUE); +#endif + perror_msg("%s", fileName); + return FALSE; + } + + if ((followLinks == FALSE) && (S_ISLNK(statbuf.st_mode))) { + if (fileAction == NULL) + return TRUE; + else + return fileAction(fileName, &statbuf, userData); + } + + if (recurse == FALSE) { + 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 == FALSE) { + status = dirAction(fileName, &statbuf, userData); + if (status == FALSE) { + perror_msg("%s", fileName); + return FALSE; + } else if (status == SKIP) + return TRUE; + } + dir = opendir(fileName); + if (!dir) { + perror_msg("%s", fileName); + return FALSE; + } + status = TRUE; + while ((next = readdir(dir)) != NULL) { + char *nextFile; + + if ((strcmp(next->d_name, "..") == 0) + || (strcmp(next->d_name, ".") == 0)) { + continue; + } + nextFile = concat_path_file(fileName, next->d_name); + if (recursive_action(nextFile, TRUE, followLinks, depthFirst, + fileAction, dirAction, userData) == FALSE) { + status = FALSE; + } + free(nextFile); + } + closedir(dir); + if (dirAction != NULL && depthFirst == TRUE) { + if (dirAction(fileName, &statbuf, userData) == FALSE) { + perror_msg("%s", fileName); + return FALSE; + } + } + if (status == FALSE) + 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 index 000000000..3b84680c4 --- /dev/null +++ b/busybox/libbb/remove_file.c @@ -0,0 +1,129 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini remove_file implementation for busybox + * + * + * Copyright (C) 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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) { + perror_msg("unable to stat `%s'", path); + return -1; + } + + path_exists = 0; + } + + if (!path_exists) { + if (!(flags & FILEUTILS_FORCE)) { + 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)) { + error_msg("%s: is a directory", path); + return -1; + } + + if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && + isatty(0)) || + (flags & FILEUTILS_INTERACTIVE)) { + fprintf(stderr, "%s: descend into directory `%s'? ", applet_name, + path); + if (!ask_confirmation()) + return 0; + } + + if ((dp = opendir(path)) == NULL) { + perror_msg("unable to open `%s'", path); + return -1; + } + + while ((d = readdir(dp)) != NULL) { + char *new_path; + + if (strcmp(d->d_name, ".") == 0 || + strcmp(d->d_name, "..") == 0) + continue; + + new_path = concat_path_file(path, d->d_name); + if (remove_file(new_path, flags) < 0) + status = -1; + free(new_path); + } + + if (closedir(dp) < 0) { + perror_msg("unable to close `%s'", path); + return -1; + } + + if (flags & FILEUTILS_INTERACTIVE) { + fprintf(stderr, "%s: remove directory `%s'? ", applet_name, path); + if (!ask_confirmation()) + return status; + } + + if (rmdir(path) < 0) { + 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'? ", applet_name, path); + if (!ask_confirmation()) + return 0; + } + + if (unlink(path) < 0) { + perror_msg("unable to remove `%s'", path); + return -1; + } + + return 0; + } +} diff --git a/busybox/libbb/safe_read.c b/busybox/libbb/safe_read.c new file mode 100644 index 000000000..dbf4aa7e4 --- /dev/null +++ b/busybox/libbb/safe_read.c @@ -0,0 +1,54 @@ +/* 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 +#include +#include +#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 index 000000000..55ec79802 --- /dev/null +++ b/busybox/libbb/safe_strncpy.c @@ -0,0 +1,48 @@ +/* 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 +#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/simplify_path.c b/busybox/libbb/simplify_path.c new file mode 100644 index 000000000..4641bae88 --- /dev/null +++ b/busybox/libbb/simplify_path.c @@ -0,0 +1,78 @@ +/* vi: set sw=4 ts=4: */ +/* + * simplify_path implementation for busybox + * + * + * Copyright (C) 2001 Vladimir N. Oleynik + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +#include "libbb.h" + +static inline char *strcpy_overlap(char *dst, const char *src) +{ + char *ptr = dst; + + do *dst++ = *src; while (*src++); + return ptr; +} + +char *simplify_path(const char *path) +{ + char *s, *start, *next; + + if (path[0] == '/') + start = xstrdup(path); + else { + s = xgetcwd(NULL); + start = concat_path_file(s, path); + free(s); + } + s = start; + /* remove . and .. */ + while(*s) { + if(*s++ == '/' && (*s == '/' || *s == 0)) { + /* remove duplicate and trailing slashes */ + s = strcpy_overlap(s-1, s); + } + else if(*(s-1) == '.' && *(s-2)=='/') { + if(*s == '/' || *s == 0) { + /* remove . */ + s = strcpy_overlap(s-1, s); /* maybe set // */ + s--; + } else if(*s == '.') { + next = s+1; /* set after ".." */ + if(*next == '/' || *next == 0) { /* "../" */ + if((s-=2) > start) + /* skip previous dir */ + do s--; while(*s != '/'); + /* remove previous dir */ + strcpy_overlap(s, next); + } + + } + } + } + if(start[0]==0) { + start[0]='/'; + start[1]=0; + } + return start; +} diff --git a/busybox/libbb/syscalls.c b/busybox/libbb/syscalls.c new file mode 100644 index 000000000..426a14aa1 --- /dev/null +++ b/busybox/libbb/syscalls.c @@ -0,0 +1,115 @@ +/* vi: set sw=4 ts=4: */ +/* + * some system calls possibly missing from libc + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ + + +#include +#ifndef __UCLIBC__ +#include +#endif +#include "libbb.h" + +#if defined(__ia64__) +int sysfs( int option, unsigned int fs_index, char * buf) +{ + return(syscall(__NR_sysfs, option, fs_index, buf)); +} +#else +_syscall3(int, sysfs, int, option, unsigned int, fs_index, char *, buf); +#endif + +#ifndef __NR_pivot_root +#warning This kernel does not support the pivot_root syscall +#warning -> The pivot_root system call is being stubbed out... +int pivot_root(const char * new_root,const char * put_old) +{ + /* 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. + */ + fprintf(stderr, "\n\nTo make this application work, you will need to recompile\n"); + fprintf(stderr, "with a kernel supporting the pivot_root system call. -Erik\n\n"); + errno=ENOSYS; + return -1; +} +#else +# if defined(__ia64__) + int pivot_root(const char * new_root,const char * put_old) + { + return(syscall(__NR_pivot_root, new_root, put_old)); + } +# else + _syscall2(int,pivot_root,const char *,new_root,const char *,put_old); +# endif +#endif + + + + +#if __GNU_LIBRARY__ < 5 || ((__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)) +/* These syscalls are not included as part of libc5 */ +_syscall2(int, bdflush, int, func, int, data); + +#ifndef __alpha__ +# define __NR_klogctl __NR_syslog + _syscall3(int, klogctl, int, type, char *, b, int, len); +#endif + +#ifndef __NR_umount2 +# warning This kernel does not support the umount2 syscall +# warning -> The umount2 system call is being stubbed out... +int umount2(const char * special_file, int flags) +{ + /* 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. + */ + fprintf(stderr, "\n\nTo make this application work, you will need to recompile\n"); + fprintf(stderr, "with a kernel supporting the umount2 system call. -Erik\n\n"); + errno=ENOSYS; + return -1; +} +# else +_syscall2(int, umount2, const char *, special_file, int, flags); +#endif + + +#endif /* __GNU_LIBRARY__ < 5 */ + + +/* 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 index 000000000..5dadcc433 --- /dev/null +++ b/busybox/libbb/syslog_msg_with_name.c @@ -0,0 +1,51 @@ +/* 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 +#include +#include "libbb.h" + +void syslog_msg_with_name(const char *name, int facility, int pri, const char *msg) +{ + openlog(name, 0, facility); + syslog(pri, "%s", msg); + closelog(); +} + +void syslog_msg(int facility, int pri, const char *msg) +{ + syslog_msg_with_name(applet_name, facility, pri, msg); +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/time_string.c b/busybox/libbb/time_string.c new file mode 100644 index 000000000..076529006 --- /dev/null +++ b/busybox/libbb/time_string.c @@ -0,0 +1,68 @@ +/* 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 +#include +#include +#include +#include "libbb.h" + + +/* + * Return the standard ls-like time string from a time_t + * This is static and so is overwritten on each call. + */ +const char *time_string(time_t timeVal) +{ + time_t now; + char *str; + static char buf[26]; + + time(&now); + + str = ctime(&timeVal); + + strcpy(buf, &str[4]); + buf[12] = '\0'; + + if ((timeVal > now) || (timeVal < now - 365 * 24 * 60 * 60L)) { + strcpy(&buf[7], &str[20]); + buf[11] = '\0'; + } + + return buf; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/trim.c b/busybox/libbb/trim.c new file mode 100644 index 000000000..76b87ca1c --- /dev/null +++ b/busybox/libbb/trim.c @@ -0,0 +1,53 @@ +/* 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 +#include +#include +#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 index 000000000..623b10312 --- /dev/null +++ b/busybox/libbb/u_signal_names.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include + +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/unarchive.c b/busybox/libbb/unarchive.c new file mode 100644 index 000000000..91db2e3b1 --- /dev/null +++ b/busybox/libbb/unarchive.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2000 by Glenn McGrath + * Copyright (C) 2001 by Laurence Anderson + * + * Based on previous work by busybox developers and others. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 +#include +#include +#include +#include +#include +#include "libbb.h" + +extern void seek_sub_file(FILE *src_stream, const int count); +extern char *extract_archive(FILE *src_stream, FILE *out_stream, const file_header_t *file_entry, + const int function, const char *prefix); + + +#ifdef L_archive_offset +off_t archive_offset; +#else +extern off_t archive_offset; +#endif + +#ifdef L_seek_sub_file +void seek_sub_file(FILE *src_stream, const int count) +{ + int i; + /* Try to fseek as faster */ + archive_offset += count; + if (fseek(src_stream, count, SEEK_CUR) != 0 && errno == ESPIPE) { + for (i = 0; i < count; i++) { + fgetc(src_stream); + } + } + return; +} +#endif + + + +#ifdef L_extract_archive +/* Extract the data postioned at src_stream to either filesystem, stdout or + * buffer depending on the value of 'function' which is defined in libbb.h + * + * prefix doesnt have to be just a directory, it may prefix the filename as well. + * + * e.g. '/var/lib/dpkg/info/dpkg.' will extract all files to the base bath + * '/var/lib/dpkg/info/' and all files/dirs created in that dir will have + * 'dpkg.' as their prefix + * + * For this reason if prefix does point to a dir then it must end with a + * trailing '/' or else the last dir will be assumed to be the file prefix + */ +char *extract_archive(FILE *src_stream, FILE *out_stream, const file_header_t *file_entry, + const int function, const char *prefix) +{ + FILE *dst_stream = NULL; + char *full_name = NULL; + char *buffer = NULL; + struct utimbuf t; + + /* prefix doesnt have to be a proper path it may prepend + * the filename as well */ + if (prefix != NULL) { + /* strip leading '/' in filename to extract as prefix may not be dir */ + /* Cant use concat_path_file here as prefix might not be a directory */ + char *path = file_entry->name; + if (strncmp("./", path, 2) == 0) { + path += 2; + if (strlen(path) == 0) { + return(NULL); + } + } + full_name = xmalloc(strlen(prefix) + strlen(path) + 1); + strcpy(full_name, prefix); + strcat(full_name, path); + } else { + full_name = file_entry->name; + } + if (function & extract_to_stdout) { + if (S_ISREG(file_entry->mode)) { + copy_file_chunk(src_stream, out_stream, file_entry->size); + archive_offset += file_entry->size; + } + } + else if (function & extract_one_to_buffer) { + if (S_ISREG(file_entry->mode)) { + buffer = (char *) xmalloc(file_entry->size + 1); + fread(buffer, 1, file_entry->size, src_stream); + buffer[file_entry->size] = '\0'; + archive_offset += file_entry->size; + return(buffer); + } + } + else if (function & extract_all_to_fs) { + struct stat oldfile; + int stat_res; + stat_res = lstat (full_name, &oldfile); + if (stat_res == 0) { /* The file already exists */ + if ((function & extract_unconditional) || (oldfile.st_mtime < file_entry->mtime)) { + if (!S_ISDIR(oldfile.st_mode)) { + unlink(full_name); /* Directories might not be empty etc */ + } + } else { + if ((function & extract_quiet) != extract_quiet) { + error_msg("%s not created: newer or same age file exists", file_entry->name); + } + seek_sub_file(src_stream, file_entry->size); + return (NULL); + } + } + if (function & extract_create_leading_dirs) { /* Create leading directories with default umask */ + char *parent = dirname(full_name); + if (make_directory (parent, -1, FILEUTILS_RECUR) != 0) { + if ((function & extract_quiet) != extract_quiet) { + error_msg("couldn't create leading directories"); + } + } + free (parent); + } + switch(file_entry->mode & S_IFMT) { + case S_IFREG: + if (file_entry->link_name) { /* Found a cpio hard link */ + if (link(file_entry->link_name, full_name) != 0) { + if ((function & extract_quiet) != extract_quiet) { + perror_msg("Cannot link from %s to '%s'", + file_entry->name, file_entry->link_name); + } + } + } else { + if ((dst_stream = wfopen(full_name, "w")) == NULL) { + seek_sub_file(src_stream, file_entry->size); + return NULL; + } + archive_offset += file_entry->size; + copy_file_chunk(src_stream, dst_stream, file_entry->size); + fclose(dst_stream); + } + break; + case S_IFDIR: + if (stat_res != 0) { + if (mkdir(full_name, file_entry->mode) < 0) { + if ((function & extract_quiet) != extract_quiet) { + perror_msg("extract_archive: "); + } + } + } + break; + case S_IFLNK: + if (symlink(file_entry->link_name, full_name) < 0) { + if ((function & extract_quiet) != extract_quiet) { + perror_msg("Cannot create symlink from %s to '%s'", file_entry->name, file_entry->link_name); + } + return NULL; + } + break; + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + if (mknod(full_name, file_entry->mode, file_entry->device) == -1) { + if ((function & extract_quiet) != extract_quiet) { + perror_msg("Cannot create node %s", file_entry->name); + } + return NULL; + } + break; + } + + /* Changing a symlink's properties normally changes the properties of the + * file pointed to, so dont try and change the date or mode, lchown does + * does the right thing, but isnt available in older versions of libc */ + if (S_ISLNK(file_entry->mode)) { +#if (__GLIBC__ > 2) && (__GLIBC_MINOR__ > 1) + lchown(full_name, file_entry->uid, file_entry->gid); +#endif + } else { + if (function & extract_preserve_date) { + t.actime = file_entry->mtime; + t.modtime = file_entry->mtime; + utime(full_name, &t); + } + chmod(full_name, file_entry->mode); + chown(full_name, file_entry->uid, file_entry->gid); + } + } else { + /* If we arent extracting data we have to skip it, + * if data size is 0 then then just do it anyway + * (saves testing for it) */ + seek_sub_file(src_stream, file_entry->size); + } + + /* extract_list and extract_verbose_list can be used in conjunction + * with one of the above four extraction functions, so do this seperately */ + if (function & extract_verbose_list) { + fprintf(out_stream, "%s %d/%d %8d %s ", mode_string(file_entry->mode), + file_entry->uid, file_entry->gid, + (int) file_entry->size, time_string(file_entry->mtime)); + } + if ((function & extract_list) || (function & extract_verbose_list)){ + /* fputs doesnt add a trailing \n, so use fprintf */ + fprintf(out_stream, "%s\n", file_entry->name); + } + + free(full_name); + + return(NULL); /* Maybe we should say if failed */ +} +#endif + +#ifdef L_unarchive +char *unarchive(FILE *src_stream, FILE *out_stream, file_header_t *(*get_headers)(FILE *), + const int extract_function, const char *prefix, char **extract_names) +{ + file_header_t *file_entry; + int found; + int i; + char *buffer = NULL; + + archive_offset = 0; + while ((file_entry = get_headers(src_stream)) != NULL) { + found = FALSE; + if (extract_names == NULL) { + found = TRUE; + } else { + for(i = 0; extract_names[i] != 0; i++) { + if (strcmp(extract_names[i], file_entry->name) == 0) { + found = TRUE; + } + } + } + + if (found) { + buffer = extract_archive(src_stream, out_stream, file_entry, extract_function, prefix); + } else { + /* seek past the data entry */ + seek_sub_file(src_stream, file_entry->size); + } + } + return(buffer); +} +#endif + +#ifdef L_get_header_ar +file_header_t *get_header_ar(FILE *src_stream) +{ + file_header_t *typed; + 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; + static char *ar_long_names; + + if (fread(ar.raw, 1, 60, src_stream) != 60) { + return(NULL); + } + archive_offset += 60; + /* align the headers based on the header magic */ + if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) { + /* some version of ar, have an extra '\n' after each data entry, + * this puts the next header out by 1 */ + if (ar.formated.magic[1] != '`') { + error_msg("Invalid magic"); + return(NULL); + } + /* read the next char out of what would be the data section, + * if its a '\n' then it is a valid header offset by 1*/ + archive_offset++; + if (fgetc(src_stream) != '\n') { + error_msg("Invalid magic"); + return(NULL); + } + /* fix up the header, we started reading 1 byte too early */ + /* raw_header[60] wont be '\n' as it should, but it doesnt matter */ + memmove(ar.raw, &ar.raw[1], 59); + } + + typed = (file_header_t *) xcalloc(1, sizeof(file_header_t)); + + typed->size = (size_t) atoi(ar.formated.size); + /* long filenames have '/' as the first character */ + if (ar.formated.name[0] == '/') { + 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_names = (char *) xrealloc(ar_long_names, typed->size); + fread(ar_long_names, 1, typed->size, src_stream); + archive_offset += typed->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(src_stream)); /* Return next header */ + } else if (ar.formated.name[1] == ' ') { + /* This is the index of symbols in the file for compilers */ + seek_sub_file(src_stream, typed->size); + return (get_header_ar(src_stream)); /* 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 */ + if (!ar_long_names) { + error_msg("Cannot resolve long file name"); + return (NULL); + } + typed->name = xstrdup(ar_long_names + atoi(&ar.formated.name[1])); + } + } else { + /* short filenames */ + typed->name = xcalloc(1, 16); + strncpy(typed->name, ar.formated.name, 16); + } + typed->name[strcspn(typed->name, " /")]='\0'; + + /* convert the rest of the now valid char header to its typed struct */ + parse_mode(ar.formated.mode, &typed->mode); + typed->mtime = atoi(ar.formated.date); + typed->uid = atoi(ar.formated.uid); + typed->gid = atoi(ar.formated.gid); + + return(typed); +} +#endif + +#ifdef L_get_header_cpio +struct hardlinks { + file_header_t *entry; + int inode; + struct hardlinks *next; +}; + +file_header_t *get_header_cpio(FILE *src_stream) +{ + file_header_t *cpio_entry = NULL; + char cpio_header[110]; + int namesize; + char dummy[16]; + int major, minor, nlink, inode; + static struct hardlinks *saved_hardlinks = NULL; + static int pending_hardlinks = 0; + + if (pending_hardlinks) { /* Deal with any pending hardlinks */ + struct hardlinks *tmp = saved_hardlinks, *oldtmp = NULL; + while (tmp) { + if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */ + cpio_entry = tmp->entry; + if (oldtmp) oldtmp->next = tmp->next; /* Remove item from linked list */ + else saved_hardlinks = tmp->next; + free(tmp); + return (cpio_entry); + } + oldtmp = tmp; + tmp = tmp->next; + } + pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */ + } + + /* There can be padding before archive header */ + seek_sub_file(src_stream, (4 - (archive_offset % 4)) % 4); + if (fread(cpio_header, 1, 110, src_stream) == 110) { + archive_offset += 110; + if (strncmp(cpio_header, "07070", 5) != 0) { + error_msg("Unsupported format or invalid magic"); + return(NULL); + } + switch (cpio_header[5]) { + case '2': /* "crc" header format */ + /* Doesnt do the crc check yet */ + case '1': /* "newc" header format */ + cpio_entry = (file_header_t *) xcalloc(1, sizeof(file_header_t)); + sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c", + dummy, &inode, (unsigned int*)&cpio_entry->mode, + (unsigned int*)&cpio_entry->uid, (unsigned int*)&cpio_entry->gid, + &nlink, &cpio_entry->mtime, &cpio_entry->size, + dummy, &major, &minor, &namesize, dummy); + + cpio_entry->name = (char *) xcalloc(1, namesize); + fread(cpio_entry->name, 1, namesize, src_stream); /* Read in filename */ + archive_offset += namesize; + /* Skip padding before file contents */ + seek_sub_file(src_stream, (4 - (archive_offset % 4)) % 4); + if (strcmp(cpio_entry->name, "TRAILER!!!") == 0) { + printf("%d blocks\n", (int) (archive_offset % 512 ? (archive_offset / 512) + 1 : archive_offset / 512)); /* Always round up */ + if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */ + struct hardlinks *tmp = saved_hardlinks, *oldtmp = NULL; + while (tmp) { + 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(NULL); + } + + if (S_ISLNK(cpio_entry->mode)) { + cpio_entry->link_name = (char *) xcalloc(1, cpio_entry->size + 1); + fread(cpio_entry->link_name, 1, cpio_entry->size, src_stream); + archive_offset += cpio_entry->size; + cpio_entry->size = 0; /* Stop possiable seeks in future */ + } + if (nlink > 1 && !S_ISDIR(cpio_entry->mode)) { + if (cpio_entry->size == 0) { /* Put file on a linked list for later */ + struct hardlinks *new = xmalloc(sizeof(struct hardlinks)); + new->next = saved_hardlinks; + new->inode = inode; + new->entry = cpio_entry; + saved_hardlinks = new; + return(get_header_cpio(src_stream)); /* Recurse to next file */ + } else { /* Found the file with data in */ + struct hardlinks *tmp = saved_hardlinks; + pending_hardlinks = 1; + while (tmp) { + if (tmp->inode == inode) { + tmp->entry->link_name = xstrdup(cpio_entry->name); + nlink--; + } + tmp = tmp->next; + } + if (nlink > 1) error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?"); + } + } + cpio_entry->device = (major << 8) | minor; + break; + default: + error_msg("Unsupported format"); + return(NULL); + } + if (ferror(src_stream) || feof(src_stream)) { + perror_msg("Stream error"); + return(NULL); + } + } + return(cpio_entry); +} +#endif + +#ifdef L_get_header_tar +file_header_t *get_header_tar(FILE *tar_stream) +{ + union { + 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; + file_header_t *tar_entry = NULL; + long i; + long sum = 0; + + if (archive_offset % 512 != 0) { + seek_sub_file(tar_stream, 512 - (archive_offset % 512)); + } + + if (fread(tar.raw, 1, 512, tar_stream) != 512) { + /* Unfortunatly its common for tar files to have all sorts of + * trailing garbage, fail silently */ +// error_msg("Couldnt read header"); + return(NULL); + } + archive_offset += 512; + + /* Check header has valid magic, unfortunately some tar files + * have empty (0'ed) tar entries at the end, which will + * cause this to fail, so fail silently for now + */ + if (strncmp(tar.formated.magic, "ustar", 5) != 0) { + return(NULL); + } + + /* 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)) { + error_msg("Invalid tar header checksum"); + return(NULL); + } + + /* convert to type'ed variables */ + tar_entry = xcalloc(1, sizeof(file_header_t)); + tar_entry->name = xstrdup(tar.formated.name); + + parse_mode(tar.formated.mode, &tar_entry->mode); + tar_entry->uid = strtol(tar.formated.uid, NULL, 8); + tar_entry->gid = strtol(tar.formated.gid, NULL, 8); + tar_entry->size = strtol(tar.formated.size, NULL, 8); + tar_entry->mtime = strtol(tar.formated.mtime, NULL, 8); + tar_entry->link_name = strlen(tar.formated.linkname) ? + xstrdup(tar.formated.linkname) : NULL; + tar_entry->device = (strtol(tar.formated.devmajor, NULL, 8) << 8) + + strtol(tar.formated.devminor, NULL, 8); + + return(tar_entry); +} +#endif + +#ifdef L_deb_extract +char *deb_extract(const char *package_filename, FILE *out_stream, + const int extract_function, const char *prefix, const char *filename) +{ + FILE *deb_stream; + FILE *uncompressed_stream = NULL; + file_header_t *ar_header = NULL; + char **file_list = NULL; + char *output_buffer = NULL; + char *ared_file = NULL; + char ar_magic[8]; + int gunzip_pid; + + if (filename != NULL) { + file_list = xmalloc(sizeof(char *) * 2); + file_list[0] = xstrdup(filename); + file_list[1] = NULL; + } + + if (extract_function & extract_control_tar_gz) { + ared_file = xstrdup("control.tar.gz"); + } + else if (extract_function & extract_data_tar_gz) { + ared_file = xstrdup("data.tar.gz"); + } + + /* open the debian package to be worked on */ + deb_stream = wfopen(package_filename, "r"); + if (deb_stream == NULL) { + return(NULL); + } + /* set the buffer size */ + setvbuf(deb_stream, NULL, _IOFBF, 0x8000); + + /* check ar magic */ + fread(ar_magic, 1, 8, deb_stream); + if (strncmp(ar_magic,"!",7) != 0) { + error_msg_and_die("invalid magic"); + } + archive_offset = 8; + + while ((ar_header = get_header_ar(deb_stream)) != NULL) { + if (strcmp(ared_file, ar_header->name) == 0) { + /* open a stream of decompressed data */ + uncompressed_stream = gz_open(deb_stream, &gunzip_pid); + archive_offset = 0; + output_buffer = unarchive(uncompressed_stream, out_stream, get_header_tar, extract_function, prefix, file_list); + } + seek_sub_file(deb_stream, ar_header->size); + } + gz_close(gunzip_pid); + fclose(deb_stream); + fclose(uncompressed_stream); + free(ared_file); + return(output_buffer); +} +#endif diff --git a/busybox/libbb/unzip.c b/busybox/libbb/unzip.c new file mode 100644 index 000000000..ee746216d --- /dev/null +++ b/busybox/libbb/unzip.c @@ -0,0 +1,1026 @@ +/* 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 + * based on gzip sources + * + * Adjusted further by Erik Andersen , + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "libbb.h" + +static FILE *in_file, *out_file; + +/* these are freed by gz_close */ +static unsigned char *window; +static unsigned long *crc_table; + +static unsigned long crc = 0xffffffffL; /* shift register contents */ + +/* Return codes from gzip */ +static const int ERROR = 1; + +/* + * window size--must be a power of two, and + * at least 32K for zip's deflate method + */ +static const int WSIZE = 0x8000; + +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ +static const int BMAX = 16; /* maximum bit length of any code (16 for explode) */ +static const int N_MAX = 288; /* maximum number of codes in any set */ + +static long bytes_out; /* number of output bytes */ +static unsigned long outcnt; /* bytes in output buffer */ + +static unsigned hufts; /* track memory usage */ +static unsigned long bb; /* bit buffer */ +static unsigned bk; /* bits in bit buffer */ + +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 const unsigned short mask_bits[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +//static int error_number = 0; +/* ======================================================================== + * Signal and error handler. + */ + +static void abort_gzip() +{ + error_msg("gzip aborted\n"); + exit(ERROR); +} + +static void make_crc_table() +{ + unsigned long table_entry; /* crc shift register */ + unsigned long poly = 0; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + crc_table = (unsigned long *) malloc(256 * sizeof(unsigned long)); + + /* Make exclusive-or pattern from polynomial (0xedb88320) */ + for (i = 0; i < sizeof(p)/sizeof(int); i++) + poly |= 1L << (31 - p[i]); + + /* Compute and print table of CRC's, five per line */ + for (i = 0; i < 256; i++) { + 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--) { + table_entry = table_entry & 1 ? (table_entry >> 1) ^ poly : table_entry >> 1; + } + crc_table[i]=table_entry; + } +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void flush_window(void) +{ + int n; + + if (outcnt == 0) + return; + + for (n = 0; n < outcnt; n++) { + crc = crc_table[((int) crc ^ (window[n])) & 0xff] ^ (crc >> 8); + } + + if (fwrite(window, 1, outcnt, out_file) != outcnt) { + error_msg_and_die("Couldnt write"); + } + bytes_out += (unsigned long) outcnt; + outcnt = 0; +} + +/* + * 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, *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 short *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 */ + if ((q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t))) == NULL) { + if (h) { + huft_free(u[0]); + } + return 3; /* not enough memory */ + } + hufts += z + 1; /* track memory usage */ + *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 *tl, huft_t *td, int bl, int bd) +{ + register unsigned long e; /* table entry flag/number of extra bits */ + unsigned long n, d; /* length and index for copy */ + unsigned long w; /* current window position */ + huft_t *t; /* pointer to table entry */ + unsigned ml, md; /* masks for bl and bd bits */ + register unsigned long b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = outcnt; /* initialize window position */ + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + for (;;) { /* do until end of block */ + while (k < (unsigned) bl) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + if ((e = (t = tl + ((unsigned) b & ml))->e) > 16) + do { + if (e == 99) { + return 1; + } + b >>= t->b; + k -= t->b; + e -= 16; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + } 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 */ + window[w++] = (unsigned char) t->v.n; + if (w == WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + } else { /* it's an EOB or a length */ + + /* exit if end of block */ + if (e == 15) { + break; + } + + /* get length of block to copy */ + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + n = t->v.n + ((unsigned) b & mask_bits[e]); + b >>= e; + k -= e; + + /* decode distance of block to copy */ + while (k < (unsigned) bd) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + + if ((e = (t = td + ((unsigned) b & md))->e) > 16) + do { + if (e == 99) + return 1; + b >>= t->b; + k -= t->b; + e -= 16; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); + b >>= t->b; + k -= t->b; + while (k < e) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + d = w - t->v.n - ((unsigned) b & mask_bits[e]); + b >>= e; + k -= e; + + /* do the copy */ + do { + n -= (e = (e = WSIZE - ((d &= WSIZE - 1) > w ? d : w)) > n ? n : e); +#if !defined(NOMEMCPY) && !defined(DEBUG) + if (w - d >= e) { /* (this test assumes unsigned comparison) */ + memcpy(window + w, window + d, e); + w += e; + d += e; + } else /* do it slow to avoid memcpy() overlap */ +#endif /* !NOMEMCPY */ + do { + window[w++] = window[d++]; + } while (--e); + if (w == WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + } while (n); + } + } + + /* restore the globals from the locals */ + outcnt = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + + /* done */ + return 0; +} + +/* + * decompress an inflated block + * e: last block flag + * + * GLOBAL VARIABLES: bb, kk, + */ +static int inflate_block(int *e) +{ + unsigned t; /* block type */ + register unsigned long b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + static unsigned short cplens[] = { /* Copy lengths for literal codes 257..285 */ + 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. */ + static unsigned short cplext[] = { /* Extra bits for literal codes 257..285 */ + 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 */ + static unsigned short cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 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 + }; + static unsigned short cpdext[] = { /* Extra bits for distance 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 + }; + + /* make local bit buffer */ + b = bb; + k = bk; + + /* read in last block bit */ + while (k < 1) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + *e = (int) b & 1; + b >>= 1; + k -= 1; + + /* read in block type */ + while (k < 2) { + b |= ((unsigned long)fgetc(in_file)) << k; + k += 8; + } + t = (unsigned) b & 3; + b >>= 2; + k -= 2; + + /* restore the global bit buffer */ + bb = b; + bk = k; + + /* inflate that block type */ + switch (t) { + case 0: /* Inflate stored */ + { + unsigned long n; /* number of bytes in block */ + unsigned long w; /* current window position */ + register unsigned long b_stored; /* bit buffer */ + register unsigned long k_stored; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b_stored = bb; /* initialize bit buffer */ + k_stored = bk; + w = outcnt; /* initialize window position */ + + /* go to byte boundary */ + n = k_stored & 7; + b_stored >>= n; + k_stored -= n; + + /* get the length and its complement */ + while (k_stored < 16) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + n = ((unsigned) b_stored & 0xffff); + b_stored >>= 16; + k_stored -= 16; + while (k_stored < 16) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + if (n != (unsigned) ((~b_stored) & 0xffff)) { + return 1; /* error in compressed data */ + } + b_stored >>= 16; + k_stored -= 16; + + /* read and output the compressed data */ + while (n--) { + while (k_stored < 8) { + b_stored |= ((unsigned long)fgetc(in_file)) << k_stored; + k_stored += 8; + } + window[w++] = (unsigned char) b_stored; + if (w == (unsigned long)WSIZE) { + outcnt=(w), + flush_window(); + w = 0; + } + b_stored >>= 8; + k_stored -= 8; + } + + /* restore the globals from the locals */ + outcnt = w; /* restore global window pointer */ + bb = b_stored; /* restore global bit buffer */ + bk = k_stored; + return 0; + } + 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 */ + int bl; /* lookup bits for tl */ + 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 */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; + } + case 2: /* Inflate dynamic */ + { + /* Tables for deflate from PKZIP's appnote.txt. */ + static unsigned border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + int dbits = 6; /* bits in base distance lookup table */ + int lbits = 9; /* bits in base literal/length lookup table */ + + int i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + huft_t *tl; /* literal/length code table */ + huft_t *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ + + unsigned ll[286 + 30]; /* literal/length and distance code lengths */ + register unsigned long b_dynamic; /* bit buffer */ + register unsigned k_dynamic; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b_dynamic = bb; + k_dynamic = bk; + + /* read in table lengths */ + while (k_dynamic < 5) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nl = 257 + ((unsigned) b_dynamic & 0x1f); /* number of literal/length codes */ + b_dynamic >>= 5; + k_dynamic -= 5; + while (k_dynamic < 5) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nd = 1 + ((unsigned) b_dynamic & 0x1f); /* number of distance codes */ + b_dynamic >>= 5; + k_dynamic -= 5; + while (k_dynamic < 4) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + nb = 4 + ((unsigned) 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++) { + while (k_dynamic < 3) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + ll[border[j]] = (unsigned) b_dynamic & 7; + b_dynamic >>= 3; + k_dynamic -= 3; + } + for (; j < 19; j++) { + ll[border[j]] = 0; + } + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 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) i < n) { + while (k_dynamic < (unsigned) bl) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = (td = tl + ((unsigned) 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 */ + while (k_dynamic < 2) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 3 + ((unsigned) b_dynamic & 3); + b_dynamic >>= 2; + k_dynamic -= 2; + if ((unsigned) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = l; + } + } else if (j == 17) { /* 3 to 10 zero length codes */ + while (k_dynamic < 3) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 3 + ((unsigned) b_dynamic & 7); + b_dynamic >>= 3; + k_dynamic -= 3; + if ((unsigned) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } else { /* j == 18: 11 to 138 zero length codes */ + while (k_dynamic < 7) { + b_dynamic |= ((unsigned long)fgetc(in_file)) << k_dynamic; + k_dynamic += 8; + } + j = 11 + ((unsigned) b_dynamic & 0x7f); + b_dynamic >>= 7; + k_dynamic -= 7; + if ((unsigned) 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 */ + bb = b_dynamic; + 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) { + error_msg("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) { + error_msg("incomplete distance tree"); + huft_free(td); + } + huft_free(tl); + return i; /* incomplete code set */ + } + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; + } + default: + /* bad block type */ + return 2; + } +} + +/* + * decompress an inflated entry + * + * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr + */ +static int inflate() +{ + int e; /* last block flag */ + int r; /* result code */ + unsigned h = 0; /* maximum struct huft's malloc'ed */ + + /* initialize window, bit buffer */ + outcnt = 0; + bk = 0; + bb = 0; + + /* decompress until the last block */ + do { + hufts = 0; + if ((r = inflate_block(&e)) != 0) { + return r; + } + if (hufts > h) { + h = hufts; + } + } while (!e); + + /* flush out window */ + flush_window(); + + /* return success */ + return 0; +} + +/* =========================================================================== + * Unzip in to out. This routine works on both gzip and pkzip files. + * + * IN assertions: the buffer inbuf contains already the beginning of + * the compressed data, from offsets inptr to insize-1 included. + * The magic header has already been checked. The output buffer is cleared. + * in, out: input and output file descriptors + */ +extern int unzip(FILE *l_in_file, FILE *l_out_file) +{ + const int extra_field = 0x04; /* bit 2 set: extra field present */ + const int orig_name = 0x08; /* bit 3 set: original file name present */ + const int comment = 0x10; /* bit 4 set: file comment present */ + unsigned char buf[8]; /* extended local header */ + unsigned char flags; /* compression flags */ + char magic[2]; /* magic header */ + int method; + typedef void (*sig_type) (int); + int exit_code=0; /* program exit code */ + int i; + + in_file = l_in_file; + out_file = l_out_file; + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { + (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 + + /* Allocate all global buffers (for DYN_ALLOC option) */ + window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); + outcnt = 0; + bytes_out = 0L; + + magic[0] = fgetc(in_file); + magic[1] = fgetc(in_file); + + /* Magic header for gzip files, 1F 8B = \037\213 */ + if (memcmp(magic, "\037\213", 2) != 0) { + error_msg("Invalid gzip magic"); + return EXIT_FAILURE; + } + + method = (int) fgetc(in_file); + if (method != 8) { + error_msg("unknown method %d -- get newer version of gzip", method); + exit_code = 1; + return -1; + } + + flags = (unsigned char) fgetc(in_file); + + /* Ignore time stamp(4), extra flags(1), OS type(1) */ + for (i = 0; i < 6; i++) + fgetc(in_file); + + if ((flags & extra_field) != 0) { + size_t extra; + extra = fgetc(in_file); + extra += fgetc(in_file) << 8; + + for (i = 0; i < extra; i++) + fgetc(in_file); + } + + /* Discard original name if any */ + if ((flags & orig_name) != 0) { + while (fgetc(in_file) != 0); /* null */ + } + + /* Discard file comment if any */ + if ((flags & comment) != 0) { + while (fgetc(in_file) != 0); /* null */ + } + + if (method < 0) { + printf("it failed\n"); + return(exit_code); /* error message already emitted */ + } + + make_crc_table(); + + /* Decompress */ + if (method == 8) { + + int res = inflate(); + + if (res == 3) { + error_msg(memory_exhausted); + } else if (res != 0) { + error_msg("invalid compressed data--format violated"); + } + + } else { + error_msg("internal error, invalid method"); + } + + /* Get the crc and original length + * crc32 (see algorithm.doc) + * uncompressed input size modulo 2^32 + */ + fread(buf, 1, 8, in_file); + + /* Validate decompression - crc */ + if ((unsigned int)((buf[0] | (buf[1] << 8)) |((buf[2] | (buf[3] << 8)) << 16)) != (crc ^ 0xffffffffL)) { + error_msg("invalid compressed data--crc error"); + } + /* Validate decompression - size */ + if (((buf[4] | (buf[5] << 8)) |((buf[6] | (buf[7] << 8)) << 16)) != (unsigned long) bytes_out) { + error_msg("invalid compressed data--length error"); + } + + free(window); + free(crc_table); + + return 0; +} + +/* + * This needs access to global variables wondow and crc_table, so its not in its own file. + */ +extern void gz_close(int gunzip_pid) +{ + if (kill(gunzip_pid, SIGTERM) == -1) { + error_msg_and_die("*** Couldnt kill old gunzip process *** aborting"); + } + + if (waitpid(gunzip_pid, NULL, 0) == -1) { + printf("Couldnt wait ?"); + } + free(window); + free(crc_table); +} diff --git a/busybox/libbb/vdprintf.c b/busybox/libbb/vdprintf.c new file mode 100644 index 000000000..8c3e32a7a --- /dev/null +++ b/busybox/libbb/vdprintf.c @@ -0,0 +1,53 @@ +/* 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 +#include +#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 index 000000000..b34821561 --- /dev/null +++ b/busybox/libbb/verror_msg.c @@ -0,0 +1,49 @@ +/* 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 +#include +#include +#include +#include "libbb.h" + +extern void verror_msg(const char *s, va_list p) +{ + fflush(stdout); + fprintf(stderr, "%s: ", 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/vherror_msg.c b/busybox/libbb/vherror_msg.c new file mode 100644 index 000000000..ee0bb5009 --- /dev/null +++ b/busybox/libbb/vherror_msg.c @@ -0,0 +1,44 @@ +/* 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 +#include +extern int h_errno; + +#include + +#include "libbb.h" + +extern void vherror_msg(const char *s, va_list p) +{ + if(s == 0) + s = ""; + 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 index 000000000..ca9361e45 --- /dev/null +++ b/busybox/libbb/vperror_msg.c @@ -0,0 +1,51 @@ +/* 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 +#include +#include +#include +#include "libbb.h" + +extern void vperror_msg(const char *s, va_list p) +{ + int err=errno; + if(s == 0) s = ""; + 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/wfopen.c b/busybox/libbb/wfopen.c new file mode 100644 index 000000000..8b074d2f7 --- /dev/null +++ b/busybox/libbb/wfopen.c @@ -0,0 +1,50 @@ +/* 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 +#include +#include "libbb.h" + +FILE *wfopen(const char *path, const char *mode) +{ + FILE *fp; + if ((fp = fopen(path, mode)) == NULL) { + 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/xfuncs.c b/busybox/libbb/xfuncs.c new file mode 100644 index 000000000..eb93bf139 --- /dev/null +++ b/busybox/libbb/xfuncs.c @@ -0,0 +1,112 @@ +/* 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 +#include +#include +#include +#include "libbb.h" + + +#ifndef DMALLOC +extern void *xmalloc(size_t size) +{ + void *ptr = malloc(size); + + if (!ptr) + error_msg_and_die(memory_exhausted); + return ptr; +} + +extern void *xrealloc(void *old, size_t size) +{ + void *ptr; + + /* SuS2 says "If size is 0 and ptr is not a null pointer, the + * object pointed to is freed." Do that here, in case realloc + * returns a NULL, since we don't want to choke in that case. */ + if (size==0 && old) { + free(old); + return NULL; + } + + ptr = realloc(old, size); + if (!ptr) + error_msg_and_die(memory_exhausted); + return ptr; +} + +extern void *xcalloc(size_t nmemb, size_t size) +{ + void *ptr = calloc(nmemb, size); + if (!ptr) + error_msg_and_die(memory_exhausted); + return ptr; +} + +extern char * xstrdup (const char *s) { + char *t; + + if (s == NULL) + return NULL; + + t = strdup (s); + + if (t == NULL) + error_msg_and_die(memory_exhausted); + + return t; +} +#endif + +extern char * xstrndup (const char *s, int n) { + char *t; + + if (s == NULL) + error_msg_and_die("xstrndup bug"); + + t = xmalloc(++n); + + return safe_strncpy(t,s,n); +} + +FILE *xfopen(const char *path, const char *mode) +{ + FILE *fp; + if ((fp = fopen(path, mode)) == NULL) + perror_msg_and_die("%s", path); + return fp; +} + +/* 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 index 000000000..274668166 --- /dev/null +++ b/busybox/libbb/xgetcwd.c @@ -0,0 +1,52 @@ +/* + * xgetcwd.c -- return current directory with unlimited length + * Copyright (C) 1992, 1996 Free Software Foundation, Inc. + * Written by David MacKenzie . + * + * Special function for busybox written by Vladimir Oleynik +*/ + +#include +#include +#include +#include +#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; + + errno = 0; + path_max = (unsigned) PATH_MAX; + path_max += 2; /* The getcwd docs say to do this. */ + + if(cwd==0) + cwd = xmalloc (path_max); + + errno = 0; + while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) { + path_max += PATH_INCR; + cwd = xrealloc (cwd, path_max); + errno = 0; + } + + if (ret == NULL) { + int save_errno = errno; + free (cwd); + errno = save_errno; + perror_msg("getcwd()"); + return NULL; + } + + return cwd; +} diff --git a/busybox/libbb/xgethostbyname.c b/busybox/libbb/xgethostbyname.c new file mode 100644 index 000000000..258510332 --- /dev/null +++ b/busybox/libbb/xgethostbyname.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini xgethostbyname implementation. + * + * + * Copyright (C) 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 +extern int h_errno; + +#include "libbb.h" + +struct hostent *xgethostbyname(const char *name) +{ + struct hostent *retval; + + if ((retval = gethostbyname(name)) == NULL) + herror_msg_and_die("%s", name); + + return retval; +} diff --git a/busybox/libbb/xreadlink.c b/busybox/libbb/xreadlink.c new file mode 100644 index 000000000..932e487a5 --- /dev/null +++ b/busybox/libbb/xreadlink.c @@ -0,0 +1,37 @@ +/* + * xreadlink.c - safe implementation of readlink. + * Returns a NULL on failure... + */ + +#include + +/* + * NOTE: This function returns a malloced char* that you will have to free + * yourself. You have been warned. + */ + +#include +#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) { + perror_msg("%s:%s", applet_name, path); + 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 index 000000000..6f5e2f0cb --- /dev/null +++ b/busybox/libbb/xregcomp.c @@ -0,0 +1,53 @@ +/* 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 +#include "libbb.h" +#include + + + +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); + 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/ln.c b/busybox/ln.c new file mode 100644 index 000000000..7412a86fd --- /dev/null +++ b/busybox/ln.c @@ -0,0 +1,131 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ln implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include "busybox.h" + + +static const int LN_SYMLINK = 1; +static const int LN_FORCE = 2; +static const int LN_NODEREFERENCE = 4; + +/* + * linkDestName is where the link points to, + * linkSrcName is the name of the link to be created. + */ +static int fs_link(const char *link_destname, const char *link_srcname, + const int flag) +{ + int status; + int src_is_dir; + char *src_name; + + if (link_destname==NULL) + return(FALSE); + + src_name = (char *) xmalloc(strlen(link_srcname)+strlen(link_destname)+1); + + if (link_srcname==NULL) + strcpy(src_name, link_destname); + else + strcpy(src_name, link_srcname); + + if (flag&LN_NODEREFERENCE) + src_is_dir = is_directory(src_name, TRUE, NULL); + else + src_is_dir = is_directory(src_name, FALSE, NULL); + + if ((src_is_dir==TRUE)&&((flag&LN_NODEREFERENCE)==0)) { + char* srcdir_name; + + srcdir_name = xstrdup(link_destname); + strcat(src_name, "/"); + strcat(src_name, get_last_path_component(srcdir_name)); + free(srcdir_name); + } + + if (flag&LN_FORCE) + unlink(src_name); + + if (flag&LN_SYMLINK) + status = symlink(link_destname, src_name); + else + status = link(link_destname, src_name); + + if (status != 0) { + perror_msg(src_name); + return(FALSE); + } + return(TRUE); +} + +extern int ln_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int flag = 0; + int opt; + + /* Parse any options */ + while ((opt=getopt(argc, argv, "sfn")) != -1) { + switch(opt) { + case 's': + flag |= LN_SYMLINK; + break; + case 'f': + flag |= LN_FORCE; + break; + case 'n': + flag |= LN_NODEREFERENCE; + break; + default: + show_usage(); + } + } + if (optind > (argc-1)) { + show_usage(); + } + if (optind == (argc-1)) { + if (fs_link(argv[optind], + get_last_path_component(argv[optind]), flag)==FALSE) + status = EXIT_FAILURE; + } + while(optind<(argc-1)) { + if (fs_link(argv[optind], argv[argc-1], flag)==FALSE) + status = EXIT_FAILURE; + optind++; + } + exit(status); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/loadacm.c b/busybox/loadacm.c new file mode 100644 index 000000000..3fb4e7665 --- /dev/null +++ b/busybox/loadacm.c @@ -0,0 +1,357 @@ +/* vi: set sw=4 ts=4: */ +/* + * Derived from + * mapscrn.c - version 0.92 + * + * Was taken from console-tools and adapted by + * Peter Novodvorsky + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +typedef unsigned short unicode; + +static long int ctoi(unsigned char *s, int *is_unicode); +static int old_screen_map_read_ascii(FILE * fp, unsigned char buf[]); +static int uni_screen_map_read_ascii(FILE * fp, unicode buf[], int *is_unicode); +static unicode utf8_to_ucs2(char *buf); +static int screen_map_load(int fd, FILE * fp); + +int loadacm_main(int argc, char **argv) +{ + int fd; + + if (argc>=2 && *argv[1]=='-') { + show_usage(); + } + + fd = open(CURRENT_VC, O_RDWR); + if (fd < 0) { + perror_msg_and_die("Error opening " CURRENT_VC); + } + + if (screen_map_load(fd, stdin)) { + perror_msg_and_die("Error loading acm"); + } + + write(fd, "\033(K", 3); + + return EXIT_SUCCESS; +} + +static int screen_map_load(int fd, FILE * fp) +{ + struct stat stbuf; + unicode wbuf[E_TABSZ]; + unsigned char buf[E_TABSZ]; + int parse_failed = 0; + int is_unicode; + + if (fstat(fileno(fp), &stbuf)) + perror_msg_and_die("Cannot stat map file"); + + /* first try a UTF screen-map: either ASCII (no restriction) or binary (regular file) */ + if (! + (parse_failed = + (-1 == uni_screen_map_read_ascii(fp, wbuf, &is_unicode))) +|| (S_ISREG(stbuf.st_mode) && (stbuf.st_size == (sizeof(unicode) * E_TABSZ)))) { /* test for binary UTF map by size */ + if (parse_failed) { + if (-1 == fseek(fp, 0, SEEK_SET)) { + if (errno == ESPIPE) + error_msg_and_die("16bit screen-map MUST be a regular file."); + else + perror_msg_and_die("fseek failed reading binary 16bit screen-map"); + } + + if (fread(wbuf, sizeof(unicode) * E_TABSZ, 1, fp) != 1) + perror_msg_and_die("Cannot read [new] map from file"); +#if 0 + else + error_msg("Input screen-map is binary."); +#endif + } + + /* if it was effectively a 16-bit ASCII, OK, else try to read as 8-bit map */ + /* same if it was binary, ie. if parse_failed */ + if (parse_failed || is_unicode) { + if (ioctl(fd, PIO_UNISCRNMAP, wbuf)) + perror_msg_and_die("PIO_UNISCRNMAP ioctl"); + else + return 0; + } + } + + /* rewind... */ + if (-1 == fseek(fp, 0, SEEK_SET)) { + if (errno == ESPIPE) + error_msg("Assuming 8bit screen-map - MUST be a regular file."), + exit(1); + else + perror_msg_and_die("fseek failed assuming 8bit screen-map"); + } + + /* ... and try an old 8-bit screen-map */ + if (!(parse_failed = (-1 == old_screen_map_read_ascii(fp, buf))) || + (S_ISREG(stbuf.st_mode) && (stbuf.st_size == E_TABSZ))) { /* test for binary old 8-bit map by size */ + if (parse_failed) { + if (-1 == fseek(fp, 0, SEEK_SET)) { + if (errno == ESPIPE) + /* should not - it succedeed above */ + error_msg_and_die("fseek() returned ESPIPE !"); + else + perror_msg_and_die("fseek for binary 8bit screen-map"); + } + + if (fread(buf, E_TABSZ, 1, fp) != 1) + perror_msg_and_die("Cannot read [old] map from file"); +#if 0 + else + error_msg("Input screen-map is binary."); +#endif + } + + if (ioctl(fd, PIO_SCRNMAP, buf)) + perror_msg_and_die("PIO_SCRNMAP ioctl"); + else + return 0; + } + error_msg("Error parsing symbolic map"); + return(1); +} + + +/* + * - reads `fp' as a 16-bit ASCII SFM file. + * - returns -1 on error. + * - returns it in `unicode' in an E_TABSZ-elements array. + * - sets `*is_unicode' flagiff there were any non-8-bit + * (ie. real 16-bit) mapping. + * + * FIXME: ignores everything after second word + */ +static int uni_screen_map_read_ascii(FILE * fp, unicode buf[], int *is_unicode) +{ + char buffer[256]; /* line buffer reading file */ + char *p, *q; /* 1st + 2nd words in line */ + int in, on; /* the same, as numbers */ + int tmp_is_unicode; /* tmp for is_unicode calculation */ + int i; /* loop index - result holder */ + int ret_code = 0; /* return code */ + sigset_t acmsigset, old_sigset; + + assert(is_unicode); + + *is_unicode = 0; + + /* first 128 codes defaults to ASCII */ + for (i = 0; i < 128; i++) + buf[i] = i; + /* remaining defaults to replacement char (usually E_TABSZ = 256) */ + for (; i < E_TABSZ; i++) + buf[i] = 0xfffd; + + /* block SIGCHLD */ + sigemptyset(&acmsigset); + sigaddset(&acmsigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &acmsigset, &old_sigset); + + do { + if (NULL == fgets(buffer, sizeof(buffer), fp)) { + if (feof(fp)) + break; + else + perror_msg_and_die("uni_screen_map_read_ascii() can't read line"); + } + + /* get "charset-relative charcode", stripping leading spaces */ + p = strtok(buffer, " \t\n"); + + /* skip empty lines and comments */ + if (!p || *p == '#') + continue; + + /* get unicode mapping */ + q = strtok(NULL, " \t\n"); + if (q) { + in = ctoi(p, NULL); + if (in < 0 || in > 255) { + ret_code = -1; + break; + } + + on = ctoi(q, &tmp_is_unicode); + if (in < 0 && on > 65535) { + ret_code = -1; + break; + } + + *is_unicode |= tmp_is_unicode; + buf[in] = on; + } else { + ret_code = -1; + break; + } + } + while (1); /* terminated by break on feof() */ + + /* restore sig mask */ + sigprocmask(SIG_SETMASK, &old_sigset, NULL); + + return ret_code; +} + + +static int old_screen_map_read_ascii(FILE * fp, unsigned char buf[]) +{ + char buffer[256]; + int in, on; + char *p, *q; + + for (in = 0; in < 256; in++) + buf[in] = in; + + while (fgets(buffer, sizeof(buffer) - 1, fp)) { + p = strtok(buffer, " \t\n"); + + if (!p || *p == '#') + continue; + + q = strtok(NULL, " \t\n#"); + if (q) { + in = ctoi(p, NULL); + if (in < 0 || in > 255) + return -1; + + on = ctoi(q, NULL); + if (in < 0 && on > 255) + return -1; + + buf[in] = on; + } else + return -1; + } + + return (0); +} + + +/* + * - converts a string into an int. + * - supports dec and hex bytes, hex UCS2, single-quoted byte and UTF8 chars. + * - returns the converted value + * - if `is_unicode != NULL', use it to tell whether it was unicode + * + * CAVEAT: will report valid UTF mappings using only 1 byte as 8-bit ones. + */ +static long int ctoi(unsigned char *s, int *is_unicode) +{ + int i; + size_t ls; + + ls = strlen(s); + if (is_unicode) + *is_unicode = 0; + + /* hex-specified UCS2 */ + if ((strncmp(s, "U+", 2) == 0) && + (strspn(s + 2, "0123456789abcdefABCDEF") == ls - 2)) { + sscanf(s + 2, "%x", &i); + if (is_unicode) + *is_unicode = 1; + } + + /* hex-specified byte */ + else if ((ls <= 4) && (strncmp(s, "0x", 2) == 0) && + (strspn(s + 2, "0123456789abcdefABCDEF") == ls - 2)) + sscanf(s + 2, "%x", &i); + + /* oct-specified number (byte) */ + else if ((*s == '0') && (strspn(s, "01234567") == ls)) + sscanf(s, "%o", &i); + + /* dec-specified number (byte) */ + else if (strspn(s, "0123456789") == ls) + sscanf(s, "%d", &i); + + /* single-byte quoted char */ + else if ((strlen(s) == 3) && (s[0] == '\'') && (s[2] == '\'')) + i = s[1]; + + /* multi-byte UTF8 quoted char */ + else if ((s[0] == '\'') && (s[ls - 1] == '\'')) { + s[ls - 1] = 0; /* ensure we'll not "parse UTF too far" */ + i = utf8_to_ucs2(s + 1); + if (is_unicode) + *is_unicode = 1; + } else + return (-1); + + return (i); +} + + +static unicode utf8_to_ucs2(char *buf) +{ + int utf_count = 0; + long utf_char = 0; + unicode tc = 0; + unsigned char c; + + do { + c = *buf; + buf++; + + /* if byte should be part of multi-byte sequence */ + if (c & 0x80) { + /* if we have already started to parse a UTF8 sequence */ + if (utf_count > 0 && (c & 0xc0) == 0x80) { + utf_char = (utf_char << 6) | (c & 0x3f); + utf_count--; + if (utf_count == 0) + tc = utf_char; + else + continue; + } else { /* Possibly 1st char of a UTF8 sequence */ + + if ((c & 0xe0) == 0xc0) { + utf_count = 1; + utf_char = (c & 0x1f); + } else if ((c & 0xf0) == 0xe0) { + utf_count = 2; + utf_char = (c & 0x0f); + } else if ((c & 0xf8) == 0xf0) { + utf_count = 3; + utf_char = (c & 0x07); + } else if ((c & 0xfc) == 0xf8) { + utf_count = 4; + utf_char = (c & 0x03); + } else if ((c & 0xfe) == 0xfc) { + utf_count = 5; + utf_char = (c & 0x01); + } else + utf_count = 0; + continue; + } + } else { /* not part of multi-byte sequence - treat as ASCII + * this makes incomplete sequences to be ignored + */ + tc = c; + utf_count = 0; + } + } + while (utf_count); + + return tc; +} diff --git a/busybox/loadfont.c b/busybox/loadfont.c new file mode 100644 index 000000000..d66500195 --- /dev/null +++ b/busybox/loadfont.c @@ -0,0 +1,209 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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) + show_usage(); + + fd = open(CURRENT_VC, O_RDWR); + if (fd < 0) + perror_msg_and_die("Error opening " CURRENT_VC); + 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) + 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 */ + perror_msg("PIO_FONTX ioctl error (trying PIO_FONT)"); + } +#endif + if (ioctl(fd, PIO_FONT, buf)) + 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) { + error_msg("It seems this kernel is older than 1.1.92"); + error_msg_and_die("No Unicode mapping table loaded."); + } else +#endif + 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 + 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)) + 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)) + 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) + error_msg_and_die("Unsupported psf file mode"); + fontsize = ((psfhdr.mode & PSF_MODE512) ? 512 : 256); +#if !defined( PIO_FONTX ) || defined( __sparc__ ) + if (fontsize != 256) + 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)) + 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) + error_msg_and_die("Bad input file size"); + offset = 0; + unit = inputlth / 256; + } + do_loadfont(fd, inbuf + offset, unit, 256); +} diff --git a/busybox/loadkmap.c b/busybox/loadkmap.c new file mode 100644 index 000000000..4f217d630 --- /dev/null +++ b/busybox/loadkmap.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini loadkmap implementation for busybox + * + * Copyright (C) 1998 Enrique Zanardi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define BINARY_KEYMAP_MAGIC "bkeymap" + +/* From */ +struct kbentry { + unsigned char kb_table; + unsigned char kb_index; + unsigned short kb_value; +}; +static const int KDSKBENT = 0x4B47; /* sets one entry in translation table */ + +/* From */ +static const int NR_KEYS = 128; +static const int MAX_NR_KEYMAPS = 256; + +int loadkmap_main(int argc, char **argv) +{ + struct kbentry ke; + u_short *ibuff; + int i, j, fd, readsz, pos, ibuffsz = NR_KEYS * sizeof(u_short); + char flags[MAX_NR_KEYMAPS], buff[7]; + + if (argc != 1) + show_usage(); + + fd = open(CURRENT_VC, O_RDWR); + if (fd < 0) + perror_msg_and_die("Error opening " CURRENT_VC); + + read(0, buff, 7); + if (0 != strncmp(buff, BINARY_KEYMAP_MAGIC, 7)) + error_msg_and_die("This is not a valid binary keymap."); + + if (MAX_NR_KEYMAPS != read(0, flags, MAX_NR_KEYMAPS)) + perror_msg_and_die("Error reading keymap flags"); + + ibuff = (u_short *) xmalloc(ibuffsz); + + for (i = 0; i < MAX_NR_KEYMAPS; i++) { + if (flags[i] == 1) { + pos = 0; + while (pos < ibuffsz) { + if ((readsz = read(0, (char *) ibuff + pos, ibuffsz - pos)) < 0) + perror_msg_and_die("Error reading keymap"); + pos += readsz; + } + 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/logger.c b/busybox/logger.c new file mode 100644 index 000000000..9f730915f --- /dev/null +++ b/busybox/logger.c @@ -0,0 +1,200 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logger implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include + +#include "busybox.h" +#if !defined BB_SYSLOGD + +#define SYSLOG_NAMES +#include + +#else +#include +# 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) + error_msg_and_die("unknown facility name: %s", save); + *s++ = '.'; + } else { + s = save; + } + lev = decode(s, prioritynames); + if (lev < 0) + 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, len, opt; + char *message=NULL, buf[1024], name[128]; + + /* Fill out the name string early (may be overwritten later) */ + my_getpwuid(name, geteuid()); + + /* 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': + strncpy(name, optarg, sizeof(name)); + break; + default: + 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 { + len = 1; /* for the '\0' */ + message=xcalloc(1, 1); + for (i = optind; i < argc; i++) { + len += strlen(argv[i]); + len += 1; /* for the space between the args */ + message = xrealloc(message, len); + strcat(message, argv[i]); + strcat(message, " "); + } + message[strlen(message)-1] = '\0'; + 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. + * + * 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/logname.c b/busybox/logname.c new file mode 100644 index 000000000..0924b2471 --- /dev/null +++ b/busybox/logname.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logname implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int logname_main(int argc, char **argv) +{ + char user[9]; + + if (argc > 1) + show_usage(); + + my_getpwuid(user, geteuid()); + if (*user) { + puts(user); + return EXIT_SUCCESS; + } + error_msg_and_die("no login name"); +} diff --git a/busybox/logread.c b/busybox/logread.c new file mode 100644 index 000000000..d3349625c --- /dev/null +++ b/busybox/logread.c @@ -0,0 +1,144 @@ +/* vi: set sw=4 ts=4: */ +/* + * circular buffer syslog implementation for busybox + * + * Copyright (C) 2000 by Gennady Feldman + * + * Maintainer: Gennady Feldman 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#if __GNU_LIBRARY__ < 5 +#error Sorry. Looks like you are using libc5. +#error libc5 shm support isnt good enough. +#error Please disable BB_FEATURE_IPC_SYSLOG +#endif + + +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; + + /* no options, no getopt */ + if (argc > 1) + 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"); + + sem_down(log_semid); + // Read Memory + i=buf->head; + + //printf("head: %i tail: %i size: %i\n",buf->head,buf->tail,buf->size); + if (buf->head == buf->tail) { + printf("\n"); + } + + while ( i != buf->tail) { + printf("%s", buf->data+i); + i+= strlen(buf->data+i) + 1; + if (i >= buf->size ) + i=0; + } + sem_up(log_semid); + +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/ls.c b/busybox/ls.c new file mode 100644 index 000000000..8d0282dfe --- /dev/null +++ b/busybox/ls.c @@ -0,0 +1,937 @@ +/* vi: set sw=4 ts=4: */ +/* + * tiny-ls.c version 0.1.0: A minimalist 'ls' + * Copyright (C) 1996 Brian Candler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 " 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_WIDTH = 14, /* default if AUTOWIDTH not defined */ + COLUMN_GAP = 2, /* includes the file type char */ +}; + + +/************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#ifdef BB_FEATURE_LS_TIMESTAMPS +#include +#endif + +#ifndef MAJOR +#define MAJOR(dev) (((dev)>>8)&0xff) +#define MINOR(dev) ((dev)&0xff) +#endif + +/* what is the overall style of the listing */ +enum { +STYLE_AUTO = 0, +STYLE_LONG = 1, /* one record per line, extended info */ +STYLE_SINGLE = 2, /* one record per line */ +STYLE_COLUMNS = 3 /* fill columns */ +}; + +/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ +/* what file information will be listed */ +#define LIST_INO (1<<0) +#define LIST_BLOCKS (1<<1) +#define LIST_MODEBITS (1<<2) +#define LIST_NLINKS (1<<3) +#define LIST_ID_NAME (1<<4) +#define LIST_ID_NUMERIC (1<<5) +#define LIST_SIZE (1<<6) +#define LIST_DEV (1<<7) +#define LIST_DATE_TIME (1<<8) +#define LIST_FULLTIME (1<<9) +#define LIST_FILENAME (1<<10) +#define LIST_SYMLINK (1<<11) +#define LIST_FILETYPE (1<<12) +#define LIST_EXEC (1<<13) + +/* what files will be displayed */ +#define DISP_NORMAL (0) /* show normal filenames */ +#define DISP_DIRNAME (1<<0) /* 2 or more items? label directories */ +#define DISP_HIDDEN (1<<1) /* show filenames starting with . */ +#define DISP_DOT (1<<2) /* show . and .. */ +#define DISP_NOLIST (1<<3) /* show directory as itself, not contents */ +#define DISP_RECURSIVE (1<<4) /* show directory and everything below it */ +#define DISP_ROWS (1<<5) /* print across rows */ + +#ifdef BB_FEATURE_LS_SORTFILES +/* how will the files be sorted */ +static const int SORT_FORWARD = 0; /* sort in reverse order */ +static const int SORT_REVERSE = 1; /* sort in reverse order */ +static const int SORT_NAME = 2; /* sort by file name */ +static const int SORT_SIZE = 3; /* sort by file size */ +static const int SORT_ATIME = 4; /* sort by last access time */ +static const int SORT_CTIME = 5; /* sort by last change time */ +static const int SORT_MTIME = 6; /* sort by last modification time */ +static const int SORT_VERSION = 7; /* sort by version */ +static const int SORT_EXT = 8; /* sort by file name extension */ +static const int SORT_DIR = 9; /* sort by file or directory */ +#endif + +#ifdef BB_FEATURE_LS_TIMESTAMPS +/* which of the three times will be used */ +static const int TIME_MOD = 0; +static const int TIME_CHANGE = 1; +static const int TIME_ACCESS = 2; +#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) + +static const int SPLIT_DIR = 0; +static const int SPLIT_FILE = 1; +static const int SPLIT_SUBDIR = 2; + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) +#ifdef BB_FEATURE_LS_FILETYPES +#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [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 */ + struct dnode *next; /* point at the next node */ +}; +typedef struct dnode dnode_t; + +static struct dnode **list_dir(char *); +static struct dnode **dnalloc(int); +static int list_single(struct dnode *); + +static unsigned int disp_opts; +static unsigned int style_fmt; +static unsigned int list_fmt; +#ifdef BB_FEATURE_LS_SORTFILES +static unsigned int sort_opts; +static unsigned int sort_order; +#endif +#ifdef BB_FEATURE_LS_TIMESTAMPS +static unsigned int time_fmt; +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS +static unsigned int follow_links=FALSE; +#endif + +static unsigned short column = 0; +#ifdef BB_FEATURE_AUTOWIDTH +static unsigned short terminal_width = TERMINAL_WIDTH; +static unsigned short column_width = COLUMN_WIDTH; +static unsigned short tabstops = COLUMN_GAP; +#else +static unsigned short column_width = COLUMN_WIDTH; +#endif + +static int status = EXIT_SUCCESS; + +#ifdef BB_FEATURE_HUMAN_READABLE +static unsigned long ls_disp_hr = 0; +#endif + +static int my_stat(struct dnode *cur) +{ +#ifdef BB_FEATURE_LS_FOLLOWLINKS + if (follow_links == TRUE) { + if (stat(cur->fullname, &cur->dstat)) { + perror_msg("%s", cur->fullname); + status = EXIT_FAILURE; + free(cur->fullname); + free(cur); + return -1; + } + } else +#endif + if (lstat(cur->fullname, &cur->dstat)) { + perror_msg("%s", cur->fullname); + status = EXIT_FAILURE; + free(cur->fullname); + free(cur); + return -1; + } + return 0; +} + +static void newline(void) +{ + if (column > 0) { + putchar('\n'); + column = 0; + } +} + +/*----------------------------------------------------------------------*/ +#ifdef BB_FEATURE_LS_FILETYPES +static char append_char(mode_t mode) +{ + if ( !(list_fmt & LIST_FILETYPE)) + return '\0'; + if ((list_fmt & LIST_EXEC) && S_ISREG(mode) + && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; + return APPCHAR(mode); +} +#endif + +/*----------------------------------------------------------------------*/ +static void nexttabstop( void ) +{ + static short nexttab= 0; + int n=0; + + if (column > 0) { + n= nexttab - column; + if (n < 1) n= 1; + while (n--) { + putchar(' '); + column++; + } + } + nexttab= column + column_width + COLUMN_GAP; +} + +/*----------------------------------------------------------------------*/ +static int is_subdir(struct dnode *dn) +{ + return (S_ISDIR(dn->dstat.st_mode) && strcmp(dn->name, ".") != 0 && + strcmp(dn->name, "..") != 0); +} + +static int countdirs(struct dnode **dn, int nfiles) +{ + int i, dirs; + + if (dn==NULL || nfiles < 1) return(0); + dirs= 0; + for (i=0; idstat.st_mode)) dirs++; + } + return(dirs); +} + +static int countsubdirs(struct dnode **dn, int nfiles) +{ + int i, subdirs; + + if (dn == NULL || nfiles < 1) return 0; + subdirs = 0; + for (i = 0; i < nfiles; i++) + if (is_subdir(dn[i])) + subdirs++; + return subdirs; +} + +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 BB_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp) +{ + struct dnode *cur, *next; + + if(dnp == NULL) return; + + cur=dnp[0]; + while (cur != NULL) { + if (cur->fullname != 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; idstat.st_mode)) { + dnp[d++]= dn[i]; + } /* else skip the file */ + } else if (which == SPLIT_SUBDIR) { + if (is_subdir(dn[i])) { + dnp[d++]= dn[i]; + } /* else skip the file or dir */ + } else { + if (!(S_ISDIR(dn[i]->dstat.st_mode))) { + dnp[d++]= dn[i]; + } /* else skip the dir */ + } + } + return(dnp); +} + +/*----------------------------------------------------------------------*/ +#ifdef BB_FEATURE_LS_SORTFILES +static int sortcmp(struct dnode *d1, struct dnode *d2) +{ + int cmp, dif; + + cmp= 0; + if (sort_opts == SORT_SIZE) { + dif= (int)(d1->dstat.st_size - d2->dstat.st_size); + } else if (sort_opts == SORT_ATIME) { + dif= (int)(d1->dstat.st_atime - d2->dstat.st_atime); + } else if (sort_opts == SORT_CTIME) { + dif= (int)(d1->dstat.st_ctime - d2->dstat.st_ctime); + } else if (sort_opts == SORT_MTIME) { + dif= (int)(d1->dstat.st_mtime - d2->dstat.st_mtime); + } else if (sort_opts == SORT_DIR) { + dif= S_ISDIR(d1->dstat.st_mode) - S_ISDIR(d2->dstat.st_mode); + /* } else if (sort_opts == SORT_VERSION) { */ + /* } else if (sort_opts == SORT_EXT) { */ + } else { /* assume SORT_NAME */ + dif= 0; + } + + if (dif > 0) cmp= -1; + if (dif < 0) cmp= 1; + if (dif == 0) { + /* sort by name- may be a tie_breaker for time or size cmp */ + dif= strcmp(d1->name, d2->name); + if (dif > 0) cmp= 1; + if (dif < 0) cmp= -1; + } + + if (sort_order == SORT_REVERSE) { + cmp= -1 * cmp; + } + return(cmp); +} + +/*----------------------------------------------------------------------*/ +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=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; +#ifdef BB_FEATURE_AUTOWIDTH + int len; +#endif + + if(dn==NULL || nfiles < 1) return; + +#ifdef BB_FEATURE_AUTOWIDTH + /* find the longest file name- use that as the column width */ + column_width= 0; + for (i=0; iname) + + ((list_fmt & LIST_INO) ? 8 : 0) + + ((list_fmt & LIST_BLOCKS) ? 5 : 0) + ; + if (column_width < len) + column_width= len; + } + if (column_width >= 6) + ncols = (int)(terminal_width / (column_width + COLUMN_GAP)); + else { + ncols = 1; + column_width = COLUMN_WIDTH; + } +#else + ncols= TERMINAL_WIDTH; +#endif + switch (style_fmt) { + case STYLE_LONG: /* one record per line, extended info */ + case STYLE_SINGLE: /* one record per line */ + ncols= 1; + break; + } + + if (ncols > 1) { + nrows = nfiles / ncols; + } else { + nrows = nfiles; + ncols = 1; + } + if ((nrows * ncols) < nfiles) nrows++; /* round up fractionals */ + + if (nrows > nfiles) nrows= nfiles; + for (row=0; rowfullname); + } + subdnp= list_dir(dn[i]->fullname); + nfiles= countfiles(subdnp); + if (nfiles > 0) { + /* list all files at this level */ +#ifdef BB_FEATURE_LS_SORTFILES + shellsort(subdnp, nfiles); +#endif + showfiles(subdnp, nfiles); +#ifdef BB_FEATURE_LS_RECURSIVE + if (disp_opts & DISP_RECURSIVE) { + /* recursive- list the sub-dirs */ + dnd= splitdnarray(subdnp, nfiles, SPLIT_SUBDIR); + dndirs= countsubdirs(subdnp, nfiles); + if (dndirs > 0) { +#ifdef BB_FEATURE_LS_SORTFILES + shellsort(dnd, dndirs); +#endif + showdirs(dnd, dndirs); + 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(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) { + perror_msg("%s", path); + status = EXIT_FAILURE; + return(NULL); /* could not open the dir */ + } + while ((entry = readdir(dir)) != NULL) { + /* are we going to list the file- it may be . or .. or a hidden file */ + if ((strcmp(entry->d_name, ".")==0) && !(disp_opts & DISP_DOT)) + continue; + if ((strcmp(entry->d_name, "..")==0) && !(disp_opts & DISP_DOT)) + continue; + if ((entry->d_name[0] == '.') && !(disp_opts & DISP_HIDDEN)) + continue; + cur= (struct dnode *)xmalloc(sizeof(struct dnode)); + cur->fullname = concat_path_file(path, entry->d_name); + cur->name = cur->fullname + + (strlen(cur->fullname) - strlen(entry->d_name)); + if (my_stat(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 (nfiles < 1) return(NULL); + dnp= dnalloc(nfiles); + for (i=0, cur=dn; inext; + } + + return(dnp); +} + +/*----------------------------------------------------------------------*/ +static int list_single(struct dnode *dn) +{ + int i; + char scratch[BUFSIZ + 1]; +#ifdef BB_FEATURE_LS_TIMESTAMPS + char *filetime; + time_t ttime, age; +#endif +#if defined (BB_FEATURE_LS_FILETYPES) + struct stat info; +#endif +#ifdef BB_FEATURE_LS_FILETYPES + char append; +#endif + + if (dn==NULL || dn->fullname==NULL) return(0); + +#ifdef BB_FEATURE_LS_TIMESTAMPS + ttime= dn->dstat.st_mtime; /* the default time */ + if (time_fmt & TIME_ACCESS) ttime= dn->dstat.st_atime; + if (time_fmt & TIME_CHANGE) ttime= dn->dstat.st_ctime; + filetime= ctime(&ttime); +#endif +#ifdef BB_FEATURE_LS_FILETYPES + append = append_char(dn->dstat.st_mode); +#endif + + for (i=0; i<=31; i++) { + switch (list_fmt & (1<dstat.st_ino); + column += 8; + break; + case LIST_BLOCKS: +#ifdef BB_FEATURE_HUMAN_READABLE + fprintf(stdout, "%6s ", make_human_readable_str(dn->dstat.st_blocks>>1, + KILOBYTE, (ls_disp_hr==TRUE)? 0: KILOBYTE)); +#else +#if _FILE_OFFSET_BITS == 64 + printf("%4lld ", dn->dstat.st_blocks>>1); +#else + printf("%4ld ", dn->dstat.st_blocks>>1); +#endif +#endif + column += 5; + break; + case LIST_MODEBITS: + printf("%-10s ", (char *)mode_string(dn->dstat.st_mode)); + column += 10; + break; + case LIST_NLINKS: + printf("%4ld ", (long)dn->dstat.st_nlink); + column += 10; + break; + case LIST_ID_NAME: +#ifdef BB_FEATURE_LS_USERNAME + my_getpwuid(scratch, dn->dstat.st_uid); + printf("%-8.8s ", scratch); + my_getgrgid(scratch, dn->dstat.st_gid); + printf("%-8.8s", scratch); + column += 17; + break; +#endif + case LIST_ID_NUMERIC: + printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid); + column += 17; + break; + case LIST_SIZE: + case LIST_DEV: + if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { + printf("%4d, %3d ", (int)MAJOR(dn->dstat.st_rdev), (int)MINOR(dn->dstat.st_rdev)); + } else { +#ifdef BB_FEATURE_HUMAN_READABLE + if (ls_disp_hr==TRUE) { + fprintf(stdout, "%8s ", make_human_readable_str(dn->dstat.st_size, 1, 0)); + } else +#endif + { +#if _FILE_OFFSET_BITS == 64 + printf("%9lld ", (long long)dn->dstat.st_size); +#else + printf("%9ld ", dn->dstat.st_size); +#endif + } + } + column += 10; + break; +#ifdef BB_FEATURE_LS_TIMESTAMPS + case LIST_FULLTIME: + case LIST_DATE_TIME: + if (list_fmt & LIST_FULLTIME) { + printf("%24.24s ", filetime); + column += 25; + break; + } + 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 + case LIST_FILENAME: + printf("%s", dn->name); + column += strlen(dn->name); + break; + case LIST_SYMLINK: + if (S_ISLNK(dn->dstat.st_mode)) { + char *lpath = xreadlink(dn->fullname); + if (lpath) { + printf(" -> %s", lpath); +#ifdef BB_FEATURE_LS_FILETYPES + if (!stat(dn->fullname, &info)) { + append = append_char(info.st_mode); + } +#endif + column += strlen(lpath) + 4; + free(lpath); + } + } + break; +#ifdef BB_FEATURE_LS_FILETYPES + case LIST_FILETYPE: + if (append != '\0') { + printf("%1c", append); + column++; + } + break; +#endif + } + } + + return(0); +} + +/*----------------------------------------------------------------------*/ +extern int ls_main(int argc, char **argv) +{ + struct dnode **dnf, **dnd; + int dnfiles, dndirs; + struct dnode *dn, *cur, **dnp; + int i, nfiles; + int opt; + int oi, ac; + char **av; +#ifdef BB_FEATURE_AUTOWIDTH + struct winsize win = { 0, 0, 0, 0 }; +#endif + + disp_opts= DISP_NORMAL; + style_fmt= STYLE_AUTO; + list_fmt= LIST_SHORT; +#ifdef BB_FEATURE_LS_SORTFILES + sort_opts= SORT_NAME; + sort_order= SORT_FORWARD; +#endif +#ifdef BB_FEATURE_LS_TIMESTAMPS + time_fmt= TIME_MOD; +#endif +#ifdef BB_FEATURE_AUTOWIDTH + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (win.ws_row > 4) + column_width = win.ws_row - 2; + if (win.ws_col > 0) + terminal_width = win.ws_col - 1; +#endif + nfiles=0; + + /* process options */ + while ((opt = getopt(argc, argv, "1AaCdgilnsx" +#ifdef BB_FEATURE_AUTOWIDTH +"T:w:" +#endif +#ifdef BB_FEATURE_LS_FILETYPES +"Fp" +#endif +#ifdef BB_FEATURE_LS_RECURSIVE +"R" +#endif +#ifdef BB_FEATURE_LS_SORTFILES +"rSvX" +#endif +#ifdef BB_FEATURE_LS_TIMESTAMPS +"cetu" +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS +"L" +#endif +#ifdef BB_FEATURE_HUMAN_READABLE +"h" +#endif +"k")) > 0) { + switch (opt) { + case '1': style_fmt = STYLE_SINGLE; break; + case 'A': disp_opts |= DISP_HIDDEN; break; + case 'a': disp_opts |= DISP_HIDDEN | DISP_DOT; break; + case 'C': style_fmt = STYLE_COLUMNS; break; + case 'd': disp_opts |= DISP_NOLIST; break; + case 'g': /* ignore -- for ftp servers */ break; + case 'i': list_fmt |= LIST_INO; break; + case 'l': + style_fmt = STYLE_LONG; + list_fmt |= LIST_LONG; +#ifdef BB_FEATURE_HUMAN_READABLE + ls_disp_hr = FALSE; +#endif + break; + case 'n': list_fmt |= LIST_ID_NUMERIC; break; + case 's': list_fmt |= LIST_BLOCKS; break; + case 'x': disp_opts = DISP_ROWS; break; +#ifdef BB_FEATURE_LS_FILETYPES + case 'F': list_fmt |= LIST_FILETYPE | LIST_EXEC; break; + case 'p': list_fmt |= LIST_FILETYPE; break; +#endif +#ifdef BB_FEATURE_LS_RECURSIVE + case 'R': disp_opts |= DISP_RECURSIVE; break; +#endif +#ifdef BB_FEATURE_LS_SORTFILES + case 'r': sort_order |= SORT_REVERSE; break; + case 'S': sort_opts= SORT_SIZE; break; + case 'v': sort_opts= SORT_VERSION; break; + case 'X': sort_opts= SORT_EXT; break; +#endif +#ifdef BB_FEATURE_LS_TIMESTAMPS + case 'e': list_fmt |= LIST_FULLTIME; break; + case 'c': + time_fmt = TIME_CHANGE; +#ifdef BB_FEATURE_LS_SORTFILES + sort_opts= SORT_CTIME; +#endif + break; + case 'u': + time_fmt = TIME_ACCESS; +#ifdef BB_FEATURE_LS_SORTFILES + sort_opts= SORT_ATIME; +#endif + break; + case 't': +#ifdef BB_FEATURE_LS_SORTFILES + sort_opts= SORT_MTIME; +#endif + break; +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS + case 'L': follow_links= TRUE; break; +#endif +#ifdef BB_FEATURE_AUTOWIDTH + case 'T': tabstops= atoi(optarg); break; + case 'w': terminal_width= atoi(optarg); break; +#endif +#ifdef BB_FEATURE_HUMAN_READABLE + case 'h': ls_disp_hr = TRUE; break; +#endif + case 'k': break; + default: + goto print_usage_message; + } + } + + /* sort out which command line options take precedence */ +#ifdef BB_FEATURE_LS_RECURSIVE + if (disp_opts & DISP_NOLIST) + disp_opts &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ +#endif +#if defined (BB_FEATURE_LS_TIMESTAMPS) && defined (BB_FEATURE_LS_SORTFILES) + if (time_fmt & TIME_CHANGE) sort_opts= SORT_CTIME; + if (time_fmt & TIME_ACCESS) sort_opts= SORT_ATIME; +#endif + if (style_fmt != STYLE_LONG) + list_fmt &= ~LIST_ID_NUMERIC; /* numeric uid only for long list */ +#ifdef BB_FEATURE_LS_USERNAME + if (style_fmt == STYLE_LONG && (list_fmt & LIST_ID_NUMERIC)) + list_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ +#endif + + /* choose a display format */ + if (style_fmt == STYLE_AUTO) + style_fmt = isatty(fileno(stdout)) ? STYLE_COLUMNS : STYLE_SINGLE; + + /* + * 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]= 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) + disp_opts |= 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++) { + cur= (struct dnode *)xmalloc(sizeof(struct dnode)); + cur->fullname= xstrdup(av[oi]); + cur->name= cur->fullname; + if (my_stat(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; inext; + } + + + if (disp_opts & DISP_NOLIST) { +#ifdef BB_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 BB_FEATURE_LS_SORTFILES + shellsort(dnf, dnfiles); +#endif + showfiles(dnf, dnfiles); + } + if (dndirs > 0) { +#ifdef BB_FEATURE_LS_SORTFILES + shellsort(dnd, dndirs); +#endif + showdirs(dnd, dndirs); + } + } + return(status); + + print_usage_message: + show_usage(); +} diff --git a/busybox/lsmod.c b/busybox/lsmod.c new file mode 100644 index 000000000..76ed2fdd8 --- /dev/null +++ b/busybox/lsmod.c @@ -0,0 +1,166 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini lsmod implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * Modified by Alcove, Julien Gaulmin and + * Nicolas Ferre 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + + +#ifdef BB_FEATURE_NEW_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; + +static 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; +} + +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)) { + perror_msg_and_die("QM_MODULES"); + } + + deps = xmalloc(depsize = 256); + printf("Module Size Used by\n"); + 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 */ + 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; + } + 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"); + } + + + return( 0); +} + +#else /*BB_FEATURE_OLD_MODULE_INTERFACE*/ + +extern int lsmod_main(int argc, char **argv) +{ + int fd, i; + char line[128]; + + puts("Module Size Used by"); + fflush(stdout); + + if ((fd = open("/proc/modules", O_RDONLY)) >= 0 ) { + while ((i = read(fd, line, sizeof(line))) > 0) { + write(fileno(stdout), line, i); + } + close(fd); + return 0; + } + perror_msg_and_die("/proc/modules"); + return 1; +} + +#endif /*BB_FEATURE_OLD_MODULE_INTERFACE*/ diff --git a/busybox/makedevs.c b/busybox/makedevs.c new file mode 100644 index 000000000..b8c6dd1d8 --- /dev/null +++ b/busybox/makedevs.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +/* + * public domain -- Dave 'Kill a Cop' Cinege + * + * makedevs + * Make ranges of device files quickly. + * known bugs: can't deal with alpha ranges + */ + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +int makedevs_main(int argc, char **argv) +{ + + const char *basedev = argv[1]; + const char *type = argv[2]; + int major = atoi(argv[3]); + int Sminor = atoi(argv[4]); + int S = atoi(argv[5]); + int E = atoi(argv[6]); + int sbase = argc == 8 ? 1 : 0; + + mode_t mode = 0; + dev_t dev = 0; + char devname[255]; + char buf[255]; + + if (argc < 7 || *argv[1]=='-') + show_usage(); + + switch (type[0]) { + case 'c': + mode = S_IFCHR; + break; + case 'b': + mode = S_IFBLK; + break; + case 'f': + mode = S_IFIFO; + break; + default: + show_usage(); + } + mode |= 0660; + + while (S <= E) { + + if (type[0] != 'f') + dev = (major << 8) | Sminor; + strcpy(devname, basedev); + + if (sbase == 0) { + sprintf(buf, "%d", S); + strcat(devname, buf); + } else { + sbase = 0; + } + + if (mknod(devname, mode, dev)) + printf("Failed to create: %s\n", devname); + + 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/md5sum.c b/busybox/md5sum.c new file mode 100644 index 000000000..bb4d115ca --- /dev/null +++ b/busybox/md5sum.c @@ -0,0 +1,1074 @@ +/* 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Written by Ulrich Drepper */ +/* Hacked to work with BusyBox by Alfred M. Szmidt */ + +/* + * 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 + +/**********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined HAVE_LIMITS_H +# include +#endif +#include "busybox.h" + +/* For some silly reason, this file uses backwards TRUE and FALSE conventions */ +#undef TRUE +#undef FALSE +#define FALSE ((int) 1) +#define TRUE ((int) 0) + +//---------------------------------------------------------------------------- +//--------md5.c +//---------------------------------------------------------------------------- + +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + * according to the definition of MD5 in RFC 1321 from April 1992. + */ + +/* Written by Ulrich Drepper , 1995. */ + +//---------------------------------------------------------------------------- +//--------md5.h +//---------------------------------------------------------------------------- + +/* md5.h - Declaration of functions and data types used for MD5 sum + computing library functions. */ + +typedef u_int32_t md5_uint32; + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +static void md5_init_ctx __P ((struct md5_ctx *ctx)); + +/* 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_process_block __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* 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_process_bytes __P ((const void *buffer, size_t len, + struct md5_ctx *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_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf)); + + + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +static int md5_stream __P ((FILE *stream, void *resblock)); + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. 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. */ +static void *md5_buffer __P ((const char *buffer, size_t len, void *resblock)); + +//---------------------------------------------------------------------------- +//--------end of md5.h +//---------------------------------------------------------------------------- + +/* Handle endian-ness */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + #define SWAP(n) (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 + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void md5_init_ctx(struct md5_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +static void *md5_finish_ctx(struct md5_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 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 + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) & ctx->buffer[bytes + pad] = SWAP(ctx->total[0] << 3); + *(md5_uint32 *) & ctx->buffer[bytes + pad + 4] = + SWAP( ((ctx->total[1] << 3) | (ctx->total[0] >> 29)) ); + + /* Process last bytes. */ + md5_process_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. */ + ((md5_uint32 *) resbuf)[0] = SWAP(ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP(ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP(ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP(ctx->D); + + return resbuf; +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +static int md5_stream(FILE *stream, void *resblock) +{ + /* Important: BLOCKSIZE must be a multiple of 64. */ +static const int BLOCKSIZE = 4096; + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx(&ctx); + + /* Iterate over full file contents. */ + while (1) { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + do { + n = fread(buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + } + while (sum < BLOCKSIZE && n != 0); + if (n == 0 && ferror(stream)) + return 1; + + /* If end of file is reached, end the loop. */ + if (n == 0) + break; + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block(buffer, BLOCKSIZE, &ctx); + } + + /* Add the last bytes if necessary. */ + if (sum > 0) + md5_process_bytes(buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx(&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. 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. */ +static void *md5_buffer(const char *buffer, size_t len, void *resblock) +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx(&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes(buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx(&ctx, resblock); +} + +static void md5_process_bytes(const void *buffer, size_t len, struct md5_ctx *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_process_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_process_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; + } +} + +/* 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)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ +static void md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx) +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof(md5_uint32); + const md5_uint32 *endp = words + nwords; +#if MD5SUM_SIZE_VS_SPEED > 0 + static const md5_uint32 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 + 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 +#endif + + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 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) { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + +#if MD5SUM_SIZE_VS_SPEED > 1 +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + const md5_uint32 *pc; + const char *pp; + const char *ps; + int i; + md5_uint32 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++; + temp = 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++; + temp = 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++; + temp = 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++; + temp = 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++; + temp = CYCLIC (temp, ps[i&3]); + temp += B; + A = D; D = C; C = B; B = temp; + } + +#endif +#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 md5_uint32 *pc; + const char *pp; + int i; +#endif + + /* 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 + + /* 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 + + /* 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 + + /* 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 +#endif + + /* 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; +} + +//---------------------------------------------------------------------------- +//--------end of md5.c +//---------------------------------------------------------------------------- + +#define ISWHITE(c) ((c) == ' ' || (c) == '\t') +#define ISXDIGIT(c) (isxdigit (c)) + +/* The minimum length of a valid digest line in a file produced + by `md5sum FILE' and read by `md5sum -c'. This length does + not include any newline character at the end of a line. */ +static const int MIN_DIGEST_LINE_LENGTH = 35; /* 32 - message digest length + 2 - blank and binary indicator + 1 - minimum filename length */ + +static int have_read_stdin; /* Nonzero if any of the files read were + the standard input. */ + +static int status_only = 0; /* With -c, don't generate any output. + The exit code indicates success or failure */ +static int warn = 0; /* With -w, print a message to standard error warning + about each improperly formatted MD5 checksum line */ + +static int split_3(char *s, + size_t s_len, + unsigned char **u, + char **w) +{ + size_t i = 0; + int escaped_filename = 0; + + while (ISWHITE(s[i])) + ++i; + + /* The line must have at least 35 (36 if the first is a backslash) + more characters to contain correct message digest information. + Ignore this line if it is too short. */ + if (!(s_len - i >= MIN_DIGEST_LINE_LENGTH + || (s[i] == '\\' && s_len - i >= 1 + MIN_DIGEST_LINE_LENGTH))) + return FALSE; + + if (s[i] == '\\') { + ++i; + escaped_filename = 1; + } + *u = (unsigned char *) &s[i]; + + /* The first field has to be the 32-character hexadecimal + representation of the message digest. If it is not followed + immediately by a white space it's an error. */ + i += 32; + if (!ISWHITE(s[i])) + return FALSE; + + s[i++] = '\0'; + + if (s[i] != ' ' && s[i++] != '*') + return FALSE; + + /* All characters between the type indicator and end of line are + significant -- that includes leading and trailing white space. */ + *w = &s[i]; + + if (escaped_filename) { + /* Translate each `\n' string in the file name to a NEWLINE, + and each `\\' string to a backslash. */ + + char *dst = &s[i]; + + while (i < s_len) { + switch (s[i]) { + case '\\': + if (i == s_len - 1) { + /* A valid line does not end with a backslash. */ + return FALSE; + } + ++i; + switch (s[i++]) { + case 'n': + *dst++ = '\n'; + break; + case '\\': + *dst++ = '\\'; + break; + default: + /* Only `\' or `n' may follow a backslash. */ + return FALSE; + } + break; + + case '\0': + /* The file name may not contain a NUL. */ + return FALSE; + break; + + default: + *dst++ = s[i++]; + break; + } + } + *dst = '\0'; + } + return TRUE; +} + +static inline int hex_digits(unsigned char const *s) +{ + while (*s) { + if (!ISXDIGIT(*s)) + return TRUE; + ++s; + } + return FALSE; +} + +/* An interface to md5_stream. Operate on FILENAME (it may be "-") and + put the result in *MD5_RESULT. Return non-zero upon failure, zero + to indicate success. */ +static int md5_file(const char *filename, + unsigned char *md5_result) +{ + FILE *fp; + + if (filename[0] == '-' && filename[1] == '\0') { + have_read_stdin = 1; + fp = stdin; + } else { + fp = wfopen(filename, "r"); + if (fp == NULL) + return FALSE; + } + + if (md5_stream(fp, md5_result)) { + perror_msg("%s", filename); + + if (fp != stdin) + fclose(fp); + return FALSE; + } + + if (fp != stdin && fclose(fp) == EOF) { + perror_msg("%s", filename); + return FALSE; + } + + return TRUE; +} + +static int md5_check(const char *checkfile_name) +{ + FILE *checkfile_stream; + int n_properly_formated_lines = 0; + int n_mismatched_checksums = 0; + int n_open_or_read_failures = 0; + unsigned char md5buffer[16]; + size_t line_number; + char line[BUFSIZ]; + + if (checkfile_name[0] == '-' && checkfile_name[1] == '\0') { + have_read_stdin = 1; + checkfile_stream = stdin; + } else { + checkfile_stream = wfopen(checkfile_name, "r"); + if (checkfile_stream == NULL) + return FALSE; + } + + line_number = 0; + + do { + char *filename; + unsigned char *md5num; + int line_length; + + ++line_number; + + fgets(line, BUFSIZ-1, checkfile_stream); + line_length = strlen(line); + + if (line_length <= 0 || line==NULL) + break; + + /* Ignore comment lines, which begin with a '#' character. */ + if (line[0] == '#') + continue; + + /* Remove any trailing newline. */ + if (line[line_length - 1] == '\n') + line[--line_length] = '\0'; + + if (split_3(line, line_length, &md5num, &filename) + || !hex_digits(md5num)) { + if (warn) { + error_msg("%s: %lu: improperly formatted MD5 checksum line", + checkfile_name, (unsigned long) line_number); + } + } else { + static const char bin2hex[] = { + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f' + }; + + ++n_properly_formated_lines; + + if (md5_file(filename, md5buffer)) { + ++n_open_or_read_failures; + if (!status_only) { + printf("%s: FAILED open or read\n", filename); + fflush(stdout); + } + } else { + size_t cnt; + /* Compare generated binary number with text representation + in check file. Ignore case of hex digits. */ + for (cnt = 0; cnt < 16; ++cnt) { + if (tolower(md5num[2 * cnt]) + != bin2hex[md5buffer[cnt] >> 4] + || (tolower(md5num[2 * cnt + 1]) + != (bin2hex[md5buffer[cnt] & 0xf]))) + break; + } + if (cnt != 16) + ++n_mismatched_checksums; + + if (!status_only) { + printf("%s: %s\n", filename, + (cnt != 16 ? "FAILED" : "OK")); + fflush(stdout); + } + } + } + } + + while (!feof(checkfile_stream) && !ferror(checkfile_stream)); + + if (ferror(checkfile_stream)) { + error_msg("%s: read error", checkfile_name); + return FALSE; + } + + if (checkfile_stream != stdin && fclose(checkfile_stream) == EOF) { + perror_msg("md5sum: %s", checkfile_name); + return FALSE; + } + + if (n_properly_formated_lines == 0) { + /* Warn if no tests are found. */ + error_msg("%s: no properly formatted MD5 checksum lines found", + checkfile_name); + return FALSE; + } else { + if (!status_only) { + int n_computed_checkums = (n_properly_formated_lines + - n_open_or_read_failures); + + if (n_open_or_read_failures > 0) { + error_msg("WARNING: %d of %d listed files could not be read", + n_open_or_read_failures, n_properly_formated_lines); + return FALSE; + } + + if (n_mismatched_checksums > 0) { + error_msg("WARNING: %d of %d computed checksums did NOT match", + n_mismatched_checksums, n_computed_checkums); + return FALSE; + } + } + } + + return ((n_properly_formated_lines > 0 && n_mismatched_checksums == 0 + && n_open_or_read_failures == 0) ? 0 : 1); +} + +int md5sum_main(int argc, + char **argv) +{ + unsigned char md5buffer[16]; + int do_check = 0; + int opt; + char **string = NULL; + size_t n_strings = 0; + size_t err = 0; + char file_type_specified = 0; + char binary = 0; + + while ((opt = getopt(argc, argv, "g:bcstw")) != -1) { + switch (opt) { + case 'g': { /* read a string */ + if (string == NULL) + string = (char **) xmalloc ((argc - 1) * sizeof (char *)); + + string[n_strings++] = optarg; + break; + } + + case 'b': /* read files in binary mode */ + file_type_specified = 1; + binary = 1; + break; + + case 'c': /* check MD5 sums against given list */ + do_check = 1; + break; + + case 's': /* don't output anything, status code shows success */ + status_only = 1; + warn = 0; + break; + + case 't': /* read files in text mode (default) */ + file_type_specified = 1; + binary = 0; + break; + + case 'w': /* warn about improperly formated MD5 checksum lines */ + status_only = 0; + warn = 1; + break; + + default: + show_usage(); + } + } + + if (file_type_specified && do_check) { + error_msg_and_die("the -b and -t options are meaningless when verifying checksums"); + } + + if (n_strings > 0 && do_check) { + error_msg_and_die("the -g and -c options are mutually exclusive"); + } + + if (status_only && !do_check) { + error_msg_and_die("the -s option is meaningful only when verifying checksums"); + } + + if (warn && !do_check) { + error_msg_and_die("the -w option is meaningful only when verifying checksums"); + } + + if (n_strings > 0) { + size_t i; + + if (optind < argc) { + error_msg_and_die("no files may be specified when using -g"); + } + for (i = 0; i < n_strings; ++i) { + size_t cnt; + md5_buffer (string[i], strlen (string[i]), md5buffer); + + for (cnt = 0; cnt < 16; ++cnt) + printf ("%02x", md5buffer[cnt]); + + printf (" \"%s\"\n", string[i]); + } + } else if (do_check) { + if (optind + 1 < argc) { + error_msg("only one argument may be specified when using -c"); + } + + err = md5_check ((optind == argc) ? "-" : argv[optind]); + } else { + if (optind == argc) + argv[argc++] = "-"; + + for (; optind < argc; ++optind) { + int fail; + char *file = argv[optind]; + + fail = md5_file (file, md5buffer); + err |= fail; + if (!fail && file[0]=='-' && file[1] == '\0') { + size_t i; + for (i = 0; i < 16; ++i) + printf ("%02x", md5buffer[i]); + putchar ('\n'); + } else if (!fail) { + size_t i; + /* Output a leading backslash if the file name contains + a newline or backslash. */ + if (strchr (file, '\n') || strchr (file, '\\')) + putchar ('\\'); + + for (i = 0; i < 16; ++i) + printf ("%02x", md5buffer[i]); + + putchar (' '); + if (binary) + putchar ('*'); + else + putchar (' '); + + /* Translate each NEWLINE byte to the string, "\\n", + and each backslash to "\\\\". */ + for (i = 0; i < strlen (file); ++i) { + switch (file[i]) { + case '\n': + fputs ("\\n", stdout); + break; + + case '\\': + fputs ("\\\\", stdout); + break; + + default: + putchar (file[i]); + break; + } + } + putchar ('\n'); + } + } + } + + if (fclose (stdout) == EOF) { + error_msg_and_die("write error"); + } + + if (have_read_stdin && fclose (stdin) == EOF) { + error_msg_and_die("standard input"); + } + + if (err == 0) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} diff --git a/busybox/miscutils/adjtimex.c b/busybox/miscutils/adjtimex.c new file mode 100644 index 000000000..e3c160d87 --- /dev/null +++ b/busybox/miscutils/adjtimex.c @@ -0,0 +1,176 @@ +/* + * 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 + * + * 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 and Jim Van Zandt + * (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 + * It will autosense if it is built in a busybox environment, based + * on the BB_VER preprocessor macro. + */ + +#include +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +#include +extern int adjtimex(struct timex *buf); +#else +#include +#endif + +#ifdef BB_VER +#include "busybox.h" +#endif + +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 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: + show_usage(); + exit(1); + } + } + if (argc != optind) { /* no valid non-option parameters */ + 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/dc.c b/busybox/miscutils/dc.c new file mode 100644 index 000000000..8d7a92a28 --- /dev/null +++ b/busybox/miscutils/dc.c @@ -0,0 +1,182 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */ + +static double stack[100]; +static unsigned int pointer; + +static void push(double a) +{ + if (pointer >= (sizeof(stack) / sizeof(*stack))) + error_msg_and_die("stack overflow"); + stack[pointer++] = a; +} + +static double pop() +{ + if (pointer == 0) + error_msg_and_die("stack underflow"); + return stack[--pointer]; +} + +static void add() +{ + push(pop() + pop()); +} + +static void sub() +{ + double subtrahend = pop(); + + push(pop() - subtrahend); +} + +static void mul() +{ + push(pop() * pop()); +} + +static void divide() +{ + double divisor = pop(); + + push(pop() / divisor); +} + +static void and() +{ + push((unsigned int) pop() & (unsigned int) pop()); +} + +static void or() +{ + push((unsigned int) pop() | (unsigned int) pop()); +} + +static void eor() +{ + push((unsigned int) pop() ^ (unsigned int) pop()); +} + +static void not() +{ + push(~(unsigned int) pop()); +} + +static void print() +{ + printf("%g\n", pop()); +} + +struct op { + const char *name; + void (*function) (); +}; + +static const struct op operators[] = { + {"+", add}, + {"add", add}, + {"-", sub}, + {"sub", sub}, + {"*", mul}, + {"mul", mul}, + {"/", divide}, + {"div", divide}, + {"and", and}, + {"or", or}, + {"not", not}, + {"eor", eor}, + {0, 0} +}; + +static void stack_machine(const char *argument) +{ + char *endPointer = 0; + double d; + const struct op *o = operators; + + if (argument == 0) { + print(); + 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++; + } + 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 = get_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]=='-') + show_usage(); + while (argc >= 2) { + stack_machine(argv[1]); + argv++; + argc--; + } + } + stack_machine(0); + return EXIT_SUCCESS; +} diff --git a/busybox/miscutils/dutmp.c b/busybox/miscutils/dutmp.c new file mode 100644 index 000000000..df7f64d30 --- /dev/null +++ b/busybox/miscutils/dutmp.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * public domain -- Dave 'Kill a Cop' Cinege + * + * dutmp + * Takes utmp formated file on stdin and dumps it's contents + * out in colon delimited fields. Easy to 'cut' for shell based + * versions of 'who', 'last', etc. IP Addr is output in hex, + * little endian on x86. + * + * Modified to support all sorts of libcs by + * Erik Andersen + */ + +#include +#include + +#include +#include +#include +#include +#include "busybox.h" + +extern int dutmp_main(int argc, char **argv) +{ + + int file; + struct utmp ut; + + if (argc<2) { + file = fileno(stdin); + } else if (*argv[1] == '-' ) { + show_usage(); + } else { + file = open(argv[1], O_RDONLY); + if (file < 0) { + perror_msg_and_die(io_error, argv[1]); + } + } + +/* Kludge around the fact that the binary format for utmp has changed. */ +#if __GNU_LIBRARY__ < 5 || defined __UCLIBC__ + /* Linux libc5 */ + while (read(file, (void*)&ut, sizeof(struct utmp))) { + printf("%d|%d|%s|%s|%s|%s|%s|%lx\n", + ut.ut_type, ut.ut_pid, ut.ut_line, + ut.ut_id, ut.ut_user, ut.ut_host, + ctime(&(ut.ut_time)), + (long)ut.ut_addr); + } +#else + /* Glibc, uClibc, etc. */ + while (read(file, (void*)&ut, sizeof(struct utmp))) { + printf("%d|%d|%s|%s|%s|%s|%d|%d|%ld|%ld|%ld|%x\n", + ut.ut_type, ut.ut_pid, ut.ut_line, + ut.ut_id, ut.ut_user, ut.ut_host, + ut.ut_exit.e_termination, ut.ut_exit.e_exit, + ut.ut_session, + ut.ut_tv.tv_sec, ut.ut_tv.tv_usec, + ut.ut_addr); + } +#endif + return EXIT_SUCCESS; +} diff --git a/busybox/miscutils/makedevs.c b/busybox/miscutils/makedevs.c new file mode 100644 index 000000000..b8c6dd1d8 --- /dev/null +++ b/busybox/miscutils/makedevs.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +/* + * public domain -- Dave 'Kill a Cop' Cinege + * + * makedevs + * Make ranges of device files quickly. + * known bugs: can't deal with alpha ranges + */ + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +int makedevs_main(int argc, char **argv) +{ + + const char *basedev = argv[1]; + const char *type = argv[2]; + int major = atoi(argv[3]); + int Sminor = atoi(argv[4]); + int S = atoi(argv[5]); + int E = atoi(argv[6]); + int sbase = argc == 8 ? 1 : 0; + + mode_t mode = 0; + dev_t dev = 0; + char devname[255]; + char buf[255]; + + if (argc < 7 || *argv[1]=='-') + show_usage(); + + switch (type[0]) { + case 'c': + mode = S_IFCHR; + break; + case 'b': + mode = S_IFBLK; + break; + case 'f': + mode = S_IFIFO; + break; + default: + show_usage(); + } + mode |= 0660; + + while (S <= E) { + + if (type[0] != 'f') + dev = (major << 8) | Sminor; + strcpy(devname, basedev); + + if (sbase == 0) { + sprintf(buf, "%d", S); + strcat(devname, buf); + } else { + sbase = 0; + } + + if (mknod(devname, mode, dev)) + printf("Failed to create: %s\n", devname); + + 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/mktemp.c b/busybox/miscutils/mktemp.c new file mode 100644 index 000000000..bc47d0af0 --- /dev/null +++ b/busybox/miscutils/mktemp.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mktemp implementation for busybox + * + * + * Copyright (C) 2000 by Daniel Jacobowitz + * Written by Daniel Jacobowitz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "busybox.h" + +extern int mktemp_main(int argc, char **argv) +{ + if (argc != 2 && (argc != 3 || strcmp(argv[1], "-q"))) + show_usage(); + if(mkstemp(argv[argc-1]) < 0) + return EXIT_FAILURE; + (void) puts(argv[argc-1]); + return EXIT_SUCCESS; +} diff --git a/busybox/miscutils/mt.c b/busybox/miscutils/mt.c new file mode 100644 index 000000000..49dc70ac6 --- /dev/null +++ b/busybox/miscutils/mt.c @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#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) { + show_usage(); + } + + if (strcmp(argv[1], "-f") == 0) { + if (argc < 4) { + 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) { + 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) + perror_msg_and_die("%s", file); + + switch (code->value) { + case MTTELL: + if (ioctl(fd, MTIOCPOS, &position) < 0) + perror_msg_and_die("%s", file); + printf ("At block %d.\n", (int) position.mt_blkno); + break; + + default: + if (ioctl(fd, MTIOCTOP, &op) != 0) + perror_msg_and_die("%s", file); + break; + } + + return EXIT_SUCCESS; +} diff --git a/busybox/miscutils/readlink.c b/busybox/miscutils/readlink.c new file mode 100644 index 000000000..c46ebd108 --- /dev/null +++ b/busybox/miscutils/readlink.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini readlink implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include "busybox.h" + +int readlink_main(int argc, char **argv) +{ + char *buf = NULL; + + /* no options, no getopt */ + + if (argc != 2) + show_usage(); + + buf = xreadlink(argv[1]); + if (!buf) + return EXIT_FAILURE; + puts(buf); +#ifdef BB_FEATURE_CLEAN_UP + free(buf); +#endif + + return EXIT_SUCCESS; +} diff --git a/busybox/miscutils/update.c b/busybox/miscutils/update.c new file mode 100644 index 000000000..27a04ddee --- /dev/null +++ b/busybox/miscutils/update.c @@ -0,0 +1,112 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini update implementation for busybox; much pasted from update-2.11 + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * Copyright (c) 1996, 1997, 1999 Torsten Poulin. + * Copyright (c) 2000 by Karl M. Hegbloom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Note: This program is only necessary if you are running a 2.0.x (or + * earlier) kernel. 2.2.x and higher flush filesystem buffers automatically. + */ + +#include +#include +#include /* for getopt() */ +#include + +#if __GNU_LIBRARY__ > 5 + #include +#else + extern int bdflush (int func, long int data); +#endif + +#include "busybox.h" + +static unsigned int sync_duration = 30; +static unsigned int flush_duration = 5; +static int use_sync = 0; + +extern int update_main(int argc, char **argv) +{ + int pid; + int opt; + + while ((opt = getopt(argc, argv, "Ss:f:")) > 0) { + switch (opt) { + case 'S': + use_sync = 1; + break; + case 's': + sync_duration = atoi(optarg); + break; + case 'f': + flush_duration = atoi(optarg); + break; + default: + show_usage(); + } + } + + if (daemon(0, 1) < 0) + perror_msg_and_die("daemon"); + +#ifdef OPEN_MAX + for (pid = 0; pid < OPEN_MAX; pid++) close(pid); +#else + /* glibc 2.1.92 requires using sysconf(_SC_OPEN_MAX) */ + for (pid = 0; pid < sysconf(_SC_OPEN_MAX); pid++) close(pid); +#endif + + /* This is no longer necessary since 1.3.5x, but it will harmlessly + * exit if that is the case. + */ + + /* set the program name that will show up in a 'ps' listing */ + argv[0] = "bdflush (update)"; + argv[1] = NULL; + argv[2] = NULL; + for (;;) { + if (use_sync) { + sleep(sync_duration); + sync(); + } else { + sleep(flush_duration); + if (bdflush(1, 0) < 0) { + openlog("update", LOG_CONS, LOG_DAEMON); + syslog(LOG_INFO, + "This kernel does not need update(8). Exiting."); + closelog(); + return EXIT_SUCCESS; + } + } + } + + return EXIT_SUCCESS; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/miscutils/watchdog.c b/busybox/miscutils/watchdog.c new file mode 100644 index 000000000..f0b0ebd0e --- /dev/null +++ b/busybox/miscutils/watchdog.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini watchdog implementation for busybox + * + * Copyright (C) 2000 spoon . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +extern int watchdog_main(int argc, char **argv) +{ + int fd; + + if (argc != 2) { + show_usage(); + } + + if ((fd=open(argv[1], O_WRONLY)) == -1) { + perror_msg_and_die(argv[1]); + } + + while (1) { + sleep(30); + write(fd, "\0", 1); + } + + return EXIT_FAILURE; +} diff --git a/busybox/mk_loop_h.sh b/busybox/mk_loop_h.sh new file mode 100755 index 000000000..71c987376 --- /dev/null +++ b/busybox/mk_loop_h.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Figure out (i) the type of dev_t (ii) the defines for loop stuff +# +# Output of this script is normally redirected to "loop.h". + +# Since 1.3.79 there is an include file +# that defines __kernel_dev_t. +# (The file itself appeared in 1.3.78, but there it defined __dev_t.) +# If it exists, we use it, or, rather, which +# avoids namespace pollution. Otherwise we guess that __kernel_dev_t +# is an unsigned short (which is true on i386, but false on alpha). + +# BUG: This test is actually broken if your gcc is not configured to +# search /usr/include, as may well happen with cross-compilers. +# It would be better to ask $(CC) if these files can be found. + +if [ -f /usr/include/linux/posix_types.h ]; then + echo '#include ' + echo '#undef dev_t' + echo '#define dev_t __kernel_dev_t' +else + echo '#undef dev_t' + echo '#define dev_t unsigned short' +fi + +# Next we have to find the loop stuff itself. +# First try kernel source, then a private version. + +if [ -f /usr/include/linux/loop.h ]; then + echo '#include ' +else + echo '#include "real_loop.h"' +fi + +echo '#undef dev_t' + diff --git a/busybox/mkdir.c b/busybox/mkdir.c new file mode 100644 index 000000000..03c49f098 --- /dev/null +++ b/busybox/mkdir.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mkdir implementation for busybox + * + * Copyright (C) 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 +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +extern int mkdir_main (int argc, char **argv) +{ + mode_t mode = -1; + int flags = 0; + int status = 0; + int i, opt; + + while ((opt = getopt (argc, argv, "m:p")) != -1) { + switch (opt) { + case 'm': + mode = 0777; + if (!parse_mode (optarg, &mode)) + error_msg_and_die ("invalid mode `%s'", optarg); + break; + case 'p': + flags |= FILEUTILS_RECUR; + break; + default: + show_usage (); + } + } + + if (optind == argc) + show_usage (); + + for (i = optind; i < argc; i++) + if (make_directory (argv[i], mode, flags) < 0) + status = 1; + + return status; +} diff --git a/busybox/mkfifo.c b/busybox/mkfifo.c new file mode 100644 index 000000000..ca217fa23 --- /dev/null +++ b/busybox/mkfifo.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mkfifo implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +extern int mkfifo_main(int argc, char **argv) +{ + char *thisarg; + mode_t mode = 0666; + + argc--; + argv++; + + /* Parse any options */ + while (argc > 1) { + if (**argv != '-') + show_usage(); + thisarg = *argv; + thisarg++; + switch (*thisarg) { + case 'm': + argc--; + argv++; + parse_mode(*argv, &mode); + break; + default: + show_usage(); + } + argc--; + argv++; + } + if (argc < 1 || *argv[0] == '-') + show_usage(); + if (mkfifo(*argv, mode) < 0) + perror_msg_and_die("mkfifo"); + return EXIT_SUCCESS; +} diff --git a/busybox/mkfs_minix.c b/busybox/mkfs_minix.c new file mode 100644 index 000000000..70374eae9 --- /dev/null +++ b/busybox/mkfs_minix.c @@ -0,0 +1,853 @@ +/* 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 readablility 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 -- + * removed getopt based parser and added a hand rolled one. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + + +#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 { + u16 i_mode; + u16 i_uid; + u32 i_size; + u32 i_time; + u8 i_gid; + u8 i_nlinks; + u16 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 { + u16 i_mode; + u16 i_nlinks; + u16 i_uid; + u16 i_gid; + u32 i_size; + u32 i_atime; + u32 i_mtime; + u32 i_ctime; + u32 i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + u16 s_ninodes; + u16 s_nzones; + u16 s_imap_blocks; + u16 s_zmap_blocks; + u16 s_firstdatazone; + u16 s_log_zone_size; + u32 s_max_size; + u16 s_magic; + u16 s_state; + u32 s_zones; +}; + +struct minix_dir_entry { + u16 inode; + char name[0]; +}; + +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<> 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 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; + + 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 int count_blocks(int fd) +{ + int high, low; + + low = 0; + for (high = 1; valid_offset(fd, high); high *= 2) + low = high; + while (low < high - 1) { + const int mid = (low + high) / 2; + + if (valid_offset(fd, mid)) + low = mid; + else + high = mid; + } + valid_offset(fd, 0); + return (low + 1); +} + +static int get_size(const char *file) +{ + int fd; + long size; + + if ((fd = open(file, O_RDWR)) < 0) + 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 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)) + error_msg_and_die("seek to boot block failed in write_tables"); + if (512 != write(DEV, boot_block_buffer, 512)) + error_msg_and_die("unable to clear boot sector"); + if (BLOCK_SIZE != lseek(DEV, BLOCK_SIZE, SEEK_SET)) + error_msg_and_die("seek failed in write_tables"); + if (BLOCK_SIZE != write(DEV, super_block_buffer, BLOCK_SIZE)) + error_msg_and_die("unable to write super-block"); + if (IMAPS * BLOCK_SIZE != write(DEV, inode_map, IMAPS * BLOCK_SIZE)) + error_msg_and_die("unable to write inode map"); + if (ZMAPS * BLOCK_SIZE != write(DEV, zone_map, ZMAPS * BLOCK_SIZE)) + error_msg_and_die("unable to write zone map"); + if (INODE_BUFFER_SIZE != write(DEV, inode_buffer, INODE_BUFFER_SIZE)) + 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)) + error_msg_and_die("seek failed in write_block"); + if (BLOCK_SIZE != write(DEV, buffer, BLOCK_SIZE)) + 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) + 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) + error_msg_and_die("not enough good blocks"); + good_blocks_table[used_good_blocks] = blk; + used_good_blocks++; + return blk; +} + +static 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 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; + } + } + 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 BB_FEATURE_MINIX2 +static 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 */ + 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 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 BB_FEATURE_MINIX2 +static 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 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; + ZONES = 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 BB_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) { + 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", 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\n", MAXSIZE); +} + +/* + * Perform a test of a block; return the number of + * blocks readable/writeable. + */ +static 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) { + 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) + 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) + 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(filename) +char *filename; + +{ + FILE *listfile; + unsigned long blockno; + + listfile = 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) + error_msg_and_die("bad inode size"); +#ifdef BB_FEATURE_MINIX2 + if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) + 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) + 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) + show_usage(); + if (i == 14) + magic = MINIX_SUPER_MAGIC; + else if (i == 30) + magic = MINIX_SUPER_MAGIC2; + else + show_usage(); + namelen = i; + dirsize = i + 2; + stopIt=TRUE; + break; + } + case 'v': +#ifdef BB_FEATURE_MINIX2 + version2 = 1; +#else + error_msg("%s: not compiled with minix v2 support", + device_name); + exit(-1); +#endif + break; + case '-': + case 'h': + default: +goodbye: + 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) { + show_usage(); + } +#ifdef BB_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) + error_msg_and_die("unable to open %s", device_name); + if (fstat(DEV, &statbuf) < 0) + 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) + 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 BB_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/mknod.c b/busybox/mknod.c new file mode 100644 index 000000000..b4d4b82a1 --- /dev/null +++ b/busybox/mknod.c @@ -0,0 +1,92 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mknod implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +int mknod_main(int argc, char **argv) +{ + char *thisarg; + mode_t mode = 0; + mode_t perm = 0666; + dev_t dev = 0; + + argc--; + argv++; + + /* Parse any options */ + while (argc > 1) { + if (**argv != '-') + break; + thisarg = *argv; + thisarg++; + switch (*thisarg) { + case 'm': + argc--; + argv++; + parse_mode(*argv, &perm); + umask(0); + break; + default: + show_usage(); + } + argc--; + argv++; + } + if (argc != 4 && argc != 2) { + show_usage(); + } + switch (argv[1][0]) { + case 'c': + case 'u': + mode = S_IFCHR; + break; + case 'b': + mode = S_IFBLK; + break; + case 'p': + mode = S_IFIFO; + if (argc!=2) { + show_usage(); + } + break; + default: + show_usage(); + } + + if (mode == S_IFCHR || mode == S_IFBLK) { + dev = (atoi(argv[2]) << 8) | atoi(argv[3]); + } + + mode |= perm; + + if (mknod(argv[0], mode, dev) != 0) + perror_msg_and_die("%s", argv[0]); + return EXIT_SUCCESS; +} + diff --git a/busybox/mkswap.c b/busybox/mkswap.c new file mode 100644 index 000000000..f72c7009a --- /dev/null +++ b/busybox/mkswap.c @@ -0,0 +1,422 @@ +/* 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 + * - added Native Language Support + * + * from util-linux -- adapted for busybox by + * Erik Andersen . I ripped out Native Language + * Support, made some stuff smaller, and fitted for life in busybox. + * + */ + +#include +#include +#include +#include +#include +#include /* for _IO */ +#include +#include /* 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 void init_signature_page() +{ + pagesize = getpagesize(); + +#ifdef PAGE_SIZE + if (pagesize != PAGE_SIZE) + 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 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 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 void page_bad(int page) +{ + if (version == 0) + bit_test_and_clear(signature_page, page); + else { + if (badpages == MAX_BADPAGES) + 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) + 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) + 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) + 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: + show_usage(); + } + } + } + if (!device_name) { + error_msg("error: Nowhere to set up swap on?"); + show_usage(); + } + sz = get_size(device_name); + if (!PAGES) { + PAGES = sz; + } else if (PAGES > sz && !force) { + error_msg("error: size %ld is larger than device size %d", + PAGES * (pagesize / 1024), sz * (pagesize / 1024)); + return EXIT_FAILURE; + } + + if (version == -1) { + if (PAGES <= V0_MAX_PAGES) + version = 0; + else if (get_kernel_revision() < MAKE_VERSION(2, 1, 117)) + version = 0; + else if (pagesize < 2048) + version = 0; + else + version = 1; + } + if (version != 0 && version != 1) { + error_msg("error: unknown version %d", version); + show_usage(); + } + if (PAGES < 10) { + error_msg("error: swap area needs to be at least %ldkB", + (long) (10 * pagesize / 1024)); + 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; + error_msg("warning: truncating swap area to %ldkB", + PAGES * pagesize / 1024); + } + + DEV = open(device_name, O_RDWR); + if (DEV < 0 || fstat(DEV, &statbuf) < 0) + 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) + 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) + 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) { + 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)) + 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) + 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) + error_msg_and_die("unable to rewind swap-device"); + if (write(DEV, (char *) signature_page + offset, pagesize - offset) + != pagesize - offset) + 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)) + error_msg_and_die("fsync failed"); + return EXIT_SUCCESS; +} diff --git a/busybox/mktemp.c b/busybox/mktemp.c new file mode 100644 index 000000000..bc47d0af0 --- /dev/null +++ b/busybox/mktemp.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mktemp implementation for busybox + * + * + * Copyright (C) 2000 by Daniel Jacobowitz + * Written by Daniel Jacobowitz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "busybox.h" + +extern int mktemp_main(int argc, char **argv) +{ + if (argc != 2 && (argc != 3 || strcmp(argv[1], "-q"))) + show_usage(); + if(mkstemp(argv[argc-1]) < 0) + return EXIT_FAILURE; + (void) puts(argv[argc-1]); + return EXIT_SUCCESS; +} diff --git a/busybox/modprobe.c b/busybox/modprobe.c new file mode 100644 index 000000000..05b40c53f --- /dev/null +++ b/busybox/modprobe.c @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +/* + * really dumb modprobe implementation for busybox + * Copyright (C) 2001 Lineo, davidm@lineo.com + */ + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +static char cmd[128]; + +extern int modprobe_main(int argc, char** argv) +{ + int ch, rc = 0; + int loadall = 0, showconfig = 0, debug = 0, autoclean = 0, list = 0; + int show_only = 0, quiet = 0, remove_opt = 0, do_syslog = 0, verbose = 0; + char *load_type = NULL, *config = NULL; + + while ((ch = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) + switch(ch) { + case 'a': + loadall++; + break; + case 'c': + showconfig++; + break; + case 'd': + debug++; + break; + case 'k': + autoclean++; + break; + case 'l': + list++; + break; + case 'n': + show_only++; + break; + case 'q': + quiet++; + break; + case 'r': + remove_opt++; + break; + case 's': + do_syslog++; + break; + case 't': + load_type = optarg; + break; + case 'v': + verbose++; + break; + case 'C': + config = optarg; + break; + case 'V': + default: + show_usage(); + break; + } + + if (load_type || config) { + fprintf(stderr, "-t and -C not supported\n"); + exit(EXIT_FAILURE); + } + + if (showconfig) + exit(EXIT_SUCCESS); + + if (list) + exit(EXIT_SUCCESS); + + if (remove_opt) { + do { + sprintf(cmd, "rmmod %s %s %s", + optind >= argc ? "-a" : "", + do_syslog ? "-s" : "", + optind < argc ? argv[optind] : ""); + if (do_syslog) + syslog(LOG_INFO, "%s", cmd); + if (show_only || verbose) + printf("%s\n", cmd); + if (!show_only) + rc = system(cmd); + } while (++optind < argc); + exit(EXIT_SUCCESS); + } + + if (optind >= argc) { + fprintf(stderr, "No module or pattern provided\n"); + exit(EXIT_FAILURE); + } + + sprintf(cmd, "insmod %s %s %s", + do_syslog ? "-s" : "", + quiet ? "-q" : "", + autoclean ? "-k" : ""); + while (optind < argc) { + strcat(cmd, argv[optind]); + strcat(cmd, " "); + optind++; + } + if (do_syslog) + syslog(LOG_INFO, "%s", cmd); + if (show_only || verbose) + printf("%s\n", cmd); + if (!show_only) + rc = system(cmd); + else + rc = 0; + + exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); +} + + diff --git a/busybox/modutils/insmod.c b/busybox/modutils/insmod.c new file mode 100644 index 000000000..1a63ecb2a --- /dev/null +++ b/busybox/modutils/insmod.c @@ -0,0 +1,3503 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini insmod implementation for busybox + * + * This version of insmod supports x86, ARM, SH3/4, powerpc, m68k, + * and MIPS. + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen + * and Ron Alder + * + * Modified by Bryan Rittmeyer to support SH4 + * and (theoretically) SH3. I have only tested SH4 in little endian mode. + * + * Modified by Alcove, Julien Gaulmin and + * Nicolas Ferre to support ARM7TDMI. Only + * very minor changes required to also work with StrongArm and presumably + * all ARM based systems. + * + * Magnus Damm 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 BB_USE_xxx_ENTRIES... + * + * Quinn Jensen 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 + * + * Based almost entirely on the Linux modutils-2.3.11 implementation. + * Copyright 1996, 1997 Linux International. + * New implementation contributed by Richard Henderson + * Based on original work by Bjorn Ekwall + * Restructured (and partly rewritten) by: + * Björn Ekwall 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#ifdef BB_FEATURE_NEW_MODULE_INTERFACE +# define new_sys_init_module init_module +#else +# define old_sys_init_module init_module +#endif + +#ifdef BB_FEATURE_INSMOD_LOADINKMEM +#define LOADBITS 0 +#else +#define LOADBITS 1 +#endif + +#if defined(__powerpc__) +#define BB_USE_PLT_ENTRIES +#define BB_PLT_ENTRY_SIZE 16 +#endif + +#if defined(__arm__) +#define BB_USE_PLT_ENTRIES +#define BB_PLT_ENTRY_SIZE 8 +#define BB_USE_GOT_ENTRIES +#define BB_GOT_ENTRY_SIZE 8 +#endif + +#if defined(__sh__) +#define BB_USE_GOT_ENTRIES +#define BB_GOT_ENTRY_SIZE 4 +#endif + +#if defined(__i386__) +#define BB_USE_GOT_ENTRIES +#define BB_GOT_ENTRY_SIZE 4 +#endif + +#if defined(__mips__) +// neither used +#endif + +//---------------------------------------------------------------------------- +//--------modutils module.h, lines 45-242 +//---------------------------------------------------------------------------- + +/* Definitions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson + + 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.70 2001/07/31 22:51:49 andersen Exp $" + +/* This file contains the structures used by the 2.0 and 2.1 kernels. + We do not use the kernel headers directly because we do not wish + to be dependant on a particular kernel version to compile insmod. */ + + +/*======================================================================*/ +/* The structures used by Linux 2.0. */ + +/* The symbol format used by get_kernel_syms(2). */ +struct old_kernel_sym +{ + unsigned long value; + char name[60]; +}; + +struct old_module_ref +{ + unsigned long module; /* kernel addresses */ + unsigned long next; +}; + +struct old_module_symbol +{ + unsigned long addr; + unsigned long name; +}; + +struct old_symbol_table +{ + int size; /* total, including string table!!! */ + int n_symbols; + int n_refs; + struct old_module_symbol symbol[0]; /* actual size defined by n_symbols */ + struct old_module_ref ref[0]; /* actual size defined by n_refs */ +}; + +struct old_mod_routines +{ + unsigned long init; + unsigned long cleanup; +}; + +struct old_module +{ + unsigned long next; + unsigned long ref; /* the list of modules that refer to me */ + unsigned long symtab; + unsigned long name; + int size; /* size of module in pages */ + unsigned long addr; /* address of module */ + int state; + unsigned long cleanup; /* cleanup routine */ +}; + +/* Sent to init_module(2) or'ed into the code size parameter. */ +static const int OLD_MOD_AUTOCLEAN = 0x40000000; /* big enough, but no sign problems... */ + +int get_kernel_syms(struct old_kernel_sym *); +int old_sys_init_module(const char *name, char *code, unsigned codesize, + struct old_mod_routines *, struct old_symbol_table *); + +/*======================================================================*/ +/* 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; +#ifdef BB_FEATURE_NEW_MODULE_INTERFACE + 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 */ +#endif +}; + +#define ARCHDATA_SEC_NAME "__archdata" +#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 new_sys_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 + + 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.70 2001/07/31 22:51:49 andersen Exp $" + +/* The relocatable object is manipulated using elfin types. */ + +#include +#include + + +/* Machine-specific elf macros for i386 et al. */ + +/* the SH changes have only been tested on the SH4 in =little endian= mode */ +/* I'm not sure about big endian, so let's warn: */ + +#if (defined(__SH4__) || defined(__SH3__)) && defined(__BIG_ENDIAN__) +#error insmod.c may require changes for use on big endian SH4/SH3 +#endif + +/* it may or may not work on the SH1/SH2... So let's error on those + also */ +#if (defined(__sh__) && (!(defined(__SH3__) || defined(__SH4__)))) +#error insmod.c may require changes for non-SH3/SH4 use +#endif + +#define ELFCLASSM ELFCLASS32 + +#if (defined(__mc68000__)) +#define ELFDATAM ELFDATA2MSB +#endif + + + +#if defined(__sh__) + +#define MATCH_MACHINE(x) (x == EM_SH) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFDATAM ELFDATA2LSB + +#elif defined(__arm__) + +#define MATCH_MACHINE(x) (x == EM_ARM) +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#define ELFDATAM ELFDATA2LSB + +#elif defined(__powerpc__) + +#define MATCH_MACHINE(x) (x == EM_PPC) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFDATAM ELFDATA2MSB + +#elif defined(__mips__) + +/* 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 MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE) +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#ifdef __MIPSEB__ +#define ELFDATAM ELFDATA2MSB +#endif +#ifdef __MIPSEL__ +#define ELFDATAM ELFDATA2LSB +#endif + +#elif defined(__i386__) + +/* presumably we can use these for anything but the SH and ARM*/ +/* this is the previous behavior, but it does result in + insmod.c being broken on anything except 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 ELFDATAM ELFDATA2LSB + +#elif defined(__mc68000__) + +#define MATCH_MACHINE(x) (x == EM_68K) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela + +#else +#error Sorry, but insmod.c does not yet support this architecture... +#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 libc5. */ +#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 BB_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 int arch_create_got (struct obj_file *f); + +static int arch_init_module (struct obj_file *f, struct new_module *); + +#endif /* obj.h */ +//---------------------------------------------------------------------------- +//--------end of modutils obj.h +//---------------------------------------------------------------------------- + + + + + +#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_export = 1; + + +/*======================================================================*/ + +/* previously, these were named i386_* but since we could be + compiling for the sh, I've renamed them to the more general + arch_* These structures are the same between the x86 and SH, + and we can't support anything else right now anyway. In the + future maybe they should be #if defined'd */ + +/* Done ;-) */ + + + +#if defined(BB_USE_PLT_ENTRIES) +struct arch_plt_entry +{ + int offset; + int allocated:1; + int inited:1; /* has been set up */ +}; +#endif + +#if defined(BB_USE_GOT_ENTRIES) +struct arch_got_entry { + int offset; + unsigned offset_done:1; + unsigned reloc_done: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(BB_USE_PLT_ENTRIES) + struct obj_section *plt; +#endif +#if defined(BB_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(BB_USE_PLT_ENTRIES) + struct arch_plt_entry pltent; +#endif +#if defined(BB_USE_GOT_ENTRIES) + struct arch_got_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[FILENAME_MAX + 1]; +static char m_fullName[FILENAME_MAX + 1]; + + + +/*======================================================================*/ + + +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 = strdup(filename); + tmp = get_last_path_component(tmp1); + if (strcmp(tmp, fullname) == 0) { + free(tmp1); + /* Stop searching if we find a match */ + safe_strncpy(m_filename, filename, sizeof(m_filename)); + return (TRUE); + } + free(tmp1); + } + return (FALSE); +} + + +/*======================================================================*/ + +static struct obj_file *arch_new_file(void) +{ + struct arch_file *f; + f = xmalloc(sizeof(*f)); + +#if defined(BB_USE_PLT_ENTRIES) + f->plt = NULL; +#endif +#if defined(BB_USE_GOT_ENTRIES) + f->got = NULL; +#endif +#if defined(__mips__) + f->mips_hi16_list = NULL; +#endif + + 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)); + +#if defined(BB_USE_PLT_ENTRIES) + memset(&sym->pltent, 0, sizeof(sym->pltent)); +#endif +#if defined(BB_USE_GOT_ENTRIES) + memset(&sym->gotent, 0, sizeof(sym->gotent)); +#endif + + 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; +#if !(defined(__mips__)) + struct arch_symbol *isym = (struct arch_symbol *) sym; +#endif + + ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset); + ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset; +#if defined(BB_USE_GOT_ENTRIES) + ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0; +#endif +#if defined(BB_USE_PLT_ENTRIES) + ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0; + struct arch_plt_entry *pe; + unsigned long *ip; +#endif + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) { + +/* even though these constants seem to be the same for + the i386 and the sh, we "#if define" them for clarity + and in case that ever changes */ +#if defined(__sh__) + case R_SH_NONE: +#elif defined(__arm__) + case R_ARM_NONE: +#elif defined(__i386__) + case R_386_NONE: +#elif defined(__mc68000__) + case R_68K_NONE: +#elif defined(__powerpc__) + case R_PPC_NONE: +#elif defined(__mips__) + case R_MIPS_NONE: +#endif + break; + +#if defined(__sh__) + case R_SH_DIR32: +#elif defined(__arm__) + case R_ARM_ABS32: +#elif defined(__i386__) + case R_386_32: +#elif defined(__mc68000__) + case R_68K_32: +#elif defined(__powerpc__) + case R_PPC_ADDR32: +#elif defined(__mips__) + case R_MIPS_32: +#endif + *loc += v; + break; +#if defined(__mc68000__) + 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; +#endif /* __mc68000__ */ + +#if 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; +#endif + +#if defined(__mips__) + 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; + } +#endif + +#if defined(__arm__) +#elif defined(__sh__) + case R_SH_REL32: + *loc += v - dot; + break; +#elif defined(__i386__) + case R_386_PLT32: + case R_386_PC32: + *loc += v - dot; + break; +#elif defined(__mc68000__) + 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; +#elif defined(__powerpc__) + case R_PPC_REL32: + *loc = v - dot; + break; +#endif + +#if defined(__sh__) + case R_SH_PLT32: + *loc = v - dot; + break; +#elif defined(__i386__) +#endif + +#if defined(BB_USE_PLT_ENTRIES) + +#if defined(__arm__) + case R_ARM_PC24: + case R_ARM_PLT32: +#endif +#if defined(__powerpc__) + case R_PPC_REL24: +#endif + /* find the plt entry and initialize it if necessary */ + assert(isym != NULL); + + pe = (struct arch_plt_entry*) &isym->pltent; + + 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 + pe->inited = 1; + } + + /* relative distance to target */ + v -= dot; + /* if the target is too far away.... */ + if ((int)v < -0x02000000 || (int)v >= 0x02000000) { + /* go via the plt */ + v = plt + pe->offset - dot; + } + if (v & 3) + 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 + break; +#endif /* BB_USE_PLT_ENTRIES */ + +#if defined(__arm__) +#elif defined(__sh__) + case R_SH_GLOB_DAT: + case R_SH_JMP_SLOT: + *loc = v; + break; +#elif defined(__i386__) + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + *loc = v; + break; +#elif defined(__mc68000__) + case R_68K_GLOB_DAT: + case R_68K_JMP_SLOT: + *loc = v; + break; +#endif + +#if defined(__arm__) +#elif defined(__sh__) + case R_SH_RELATIVE: + *loc += f->baseaddr + rel->r_addend; + break; +#elif defined(__i386__) + case R_386_RELATIVE: + *loc += f->baseaddr; + break; +#elif defined(__mc68000__) + case R_68K_RELATIVE: + *(int *)loc += f->baseaddr; + break; +#endif + +#if defined(BB_USE_GOT_ENTRIES) + +#if !defined(__68k__) +#if defined(__sh__) + case R_SH_GOTPC: +#elif defined(__arm__) + case R_ARM_GOTPC: +#elif defined(__i386__) + case R_386_GOTPC: +#endif + assert(got != 0); +#if defined(__sh__) + *loc += got - dot + rel->r_addend;; +#elif defined(__i386__) || defined(__arm__) || defined(__m68k_) + *loc += got - dot; +#endif + break; +#endif // __68k__ + +#if defined(__sh__) + case R_SH_GOT32: +#elif defined(__arm__) + case R_ARM_GOT32: +#elif defined(__i386__) + case R_386_GOT32: +#elif defined(__mc68000__) + case R_68K_GOT32: +#endif + assert(isym != NULL); + /* needs an entry in the .got: set it, once */ + if (!isym->gotent.reloc_done) { + isym->gotent.reloc_done = 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; + + /* address relative to the got */ +#if !defined(__mc68000__) +#if defined(__sh__) + case R_SH_GOTOFF: +#elif defined(__arm__) + case R_ARM_GOTOFF: +#elif defined(__i386__) + case R_386_GOTOFF: +#elif defined(__mc68000__) + case R_68K_GOTOFF: +#endif + assert(got != 0); + *loc += v - got; + break; +#endif // __mc68000__ + +#endif /* BB_USE_GOT_ENTRIES */ + + default: + printf("Warning: unhandled reloc %d\n",(int)ELF32_R_TYPE(rel->r_info)); + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +static int arch_create_got(struct obj_file *f) +{ +#if defined(BB_USE_GOT_ENTRIES) || defined(BB_USE_PLT_ENTRIES) + struct arch_file *ifile = (struct arch_file *) f; + int i; +#if defined(BB_USE_GOT_ENTRIES) + int got_offset = 0, gotneeded = 0; +#endif +#if defined(BB_USE_PLT_ENTRIES) + int plt_offset = 0, pltneeded = 0; +#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)]; + + switch (ELF32_R_TYPE(rel->r_info)) { +#if defined(__arm__) + case R_ARM_GOT32: + break; +#elif defined(__sh__) + case R_SH_GOT32: + break; +#elif defined(__i386__) + case R_386_GOT32: + break; +#elif defined(__mc68000__) + case R_68K_GOT32: + break; +#endif + +#if defined(__powerpc__) + case R_PPC_REL24: + pltneeded = 1; + break; +#endif + +#if defined(__arm__) + case R_ARM_PC24: + case R_ARM_PLT32: + pltneeded = 1; + break; + + case R_ARM_GOTPC: + case R_ARM_GOTOFF: + gotneeded = 1; + if (got_offset == 0) + got_offset = 4; +#elif defined(__sh__) + case R_SH_GOTPC: + case R_SH_GOTOFF: + gotneeded = 1; +#elif defined(__i386__) + case R_386_GOTPC: + case R_386_GOTOFF: + gotneeded = 1; +#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(BB_USE_GOT_ENTRIES) + if (!intsym->gotent.offset_done) { + intsym->gotent.offset_done = 1; + intsym->gotent.offset = got_offset; + got_offset += BB_GOT_ENTRY_SIZE; + } +#endif +#if defined(BB_USE_PLT_ENTRIES) + if (pltneeded && intsym->pltent.allocated == 0) { + intsym->pltent.allocated = 1; + intsym->pltent.offset = plt_offset; + plt_offset += BB_PLT_ENTRY_SIZE; + intsym->pltent.inited = 0; + pltneeded = 0; + } +#endif + } + } + +#if defined(BB_USE_GOT_ENTRIES) + if (got_offset) { + struct obj_section* myrelsec = obj_find_section(f, ".got"); + + if (myrelsec) { + obj_extend_section(myrelsec, got_offset); + } else { + myrelsec = obj_create_alloced_section(f, ".got", + BB_GOT_ENTRY_SIZE, + got_offset); + assert(myrelsec); + } + + ifile->got = myrelsec; + } +#endif + +#if defined(BB_USE_PLT_ENTRIES) + if (plt_offset) + ifile->plt = obj_create_alloced_section(f, ".plt", + BB_PLT_ENTRY_SIZE, + plt_offset); +#endif +#endif + return 1; +} + +static int arch_init_module(struct obj_file *f, struct new_module *mod) +{ + return 1; +} + + +/*======================================================================*/ + +/* 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 BB_FEATURE_INSMOD_VERSION_CHECKING +/* Get the kernel version in the canonical integer form. */ + +static int get_kernel_version(char str[STRVERSIONLEN]) +{ + struct utsname uts_info; + int kv; + + if (uname(&uts_info) < 0) + return -1; + strncpy(str, uts_info.release, STRVERSIONLEN); + + kv = get_kernel_revision(); + if(kv==0) + return -1; +} + +/* 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 /* BB_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) + 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) + 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; + + 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; + + sym = obj_find_symbol(f, (char *) s->name); + if (sym && !ELFW(ST_BIND) (sym->info) == STB_LOCAL) { + sym = obj_add_symbol(f, (char *) s->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 in pre 2.1 kernels. */ + +static int +old_process_module_arguments(struct obj_file *f, int argc, char **argv) +{ + while (argc > 0) { + char *p, *q; + struct obj_symbol *sym; + int *loc; + + p = *argv; + if ((q = strchr(p, '=')) == NULL) { + argc--; + continue; + } + *q++ = '\0'; + + sym = obj_find_symbol(f, p); + + /* Also check that the parameter was not resolved from the kernel. */ + if (sym == NULL || sym->secidx > SHN_HIRESERVE) { + error_msg("symbol for parameter %s not found", p); + return 0; + } + + loc = (int *) (f->sections[sym->secidx]->contents + sym->value); + + /* Do C quoting if we begin with a ". */ + if (*q == '"') { + char *r, *str; + + str = alloca(strlen(q)); + for (r = str, q++; *q != '"'; ++q, ++r) { + if (*q == '\0') { + error_msg("improperly terminated string argument for %s", p); + 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'; + obj_string_patch(f, sym->secidx, sym->value, str); + } else if (*q >= '0' && *q <= '9') { + do + *loc++ = strtoul(q, &q, 0); + while (*q++ == ','); + } else { + char *contents = f->sections[sym->secidx]->contents; + char *myloc = contents + sym->value; + char *r; /* To search for commas */ + + /* Break the string with comas */ + while ((r = strchr(q, ',')) != (char *) NULL) { + *r++ = '\0'; + obj_string_patch(f, sym->secidx, myloc - contents, q); + myloc += sizeof(char *); + q = r; + } + + /* last part */ + obj_string_patch(f, sym->secidx, myloc - contents, q); + } + + argc--, argv++; + } + + return 1; +} + +#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING +static int old_is_module_checksummed(struct obj_file *f) +{ + return obj_find_symbol(f, "Using_Versions") != NULL; +} +/* Get the module's kernel version in the canonical integer form. */ + +static int +old_get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) +{ + struct obj_symbol *sym; + char *p, *q; + int a, b, c; + + sym = obj_find_symbol(f, "kernel_version"); + if (sym == NULL) + return -1; + + p = f->sections[sym->secidx]->contents + sym->value; + 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 /* BB_FEATURE_INSMOD_VERSION_CHECKING */ + +#ifdef BB_FEATURE_OLD_MODULE_INTERFACE + +/* Fetch all the symbols and divvy them up as appropriate for the modules. */ + +static int old_get_kernel_symbols(const char *m_name) +{ + struct old_kernel_sym *ks, *k; + struct new_module_symbol *s; + struct external_module *mod; + int nks, nms, nmod, i; + + nks = get_kernel_syms(NULL); + if (nks <= 0) { + if (nks) + perror_msg("get_kernel_syms: %s", m_name); + else + error_msg("No kernel symbols"); + return 0; + } + + ks = k = xmalloc(nks * sizeof(*ks)); + + if (get_kernel_syms(ks) != nks) { + perror("inconsistency with get_kernel_syms -- is someone else " + "playing with modules?"); + free(ks); + return 0; + } + + /* Collect the module information. */ + + mod = NULL; + nmod = -1; + + while (k->name[0] == '#' && k->name[1]) { + struct old_kernel_sym *k2; + + /* Find out how many symbols this module has. */ + for (k2 = k + 1; k2->name[0] != '#'; ++k2) + continue; + nms = k2 - k - 1; + + mod = xrealloc(mod, (++nmod + 1) * sizeof(*mod)); + mod[nmod].name = k->name + 1; + mod[nmod].addr = k->value; + mod[nmod].used = 0; + mod[nmod].nsyms = nms; + mod[nmod].syms = s = (nms ? xmalloc(nms * sizeof(*s)) : NULL); + + for (i = 0, ++k; i < nms; ++i, ++s, ++k) { + s->name = (unsigned long) k->name; + s->value = k->value; + } + + k = k2; + } + + ext_modules = mod; + n_ext_modules = nmod + 1; + + /* Now collect the symbols for the kernel proper. */ + + if (k->name[0] == '#') + ++k; + + nksyms = nms = nks - (k - ks); + ksyms = s = (nms ? xmalloc(nms * sizeof(*s)) : NULL); + + for (i = 0; i < nms; ++i, ++s, ++k) { + s->name = (unsigned long) k->name; + s->value = k->value; + } + + return 1; +} + +/* Return the kernel symbol checksum version, or zero if not used. */ + +static int old_is_kernel_checksummed(void) +{ + /* Using_Versions is the first symbol. */ + if (nksyms > 0 + && strcmp((char *) ksyms[0].name, + "Using_Versions") == 0) return ksyms[0].value; + else + return 0; +} + + +static int old_create_mod_use_count(struct obj_file *f) +{ + struct obj_section *sec; + + sec = obj_create_alloced_section_first(f, ".moduse", sizeof(long), + sizeof(long)); + + obj_add_symbol(f, "mod_use_count_", -1, + ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), sec->idx, 0, + sizeof(long)); + + return 1; +} + +static int +old_init_module(const char *m_name, struct obj_file *f, + unsigned long m_size) +{ + char *image; + struct old_mod_routines routines; + struct old_symbol_table *symtab; + int ret; + + /* Create the symbol table */ + { + int nsyms = 0, strsize = 0, total; + + /* Size things first... */ + if (flag_export) { + int i; + for (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->ksymidx = nsyms++; + strsize += strlen(sym->name) + 1; + } + } + } + + total = (sizeof(struct old_symbol_table) + + nsyms * sizeof(struct old_module_symbol) + + n_ext_modules_used * sizeof(struct old_module_ref) + + strsize); + symtab = xmalloc(total); + symtab->size = total; + symtab->n_symbols = nsyms; + symtab->n_refs = n_ext_modules_used; + + if (flag_export && nsyms) { + struct old_module_symbol *ksym; + char *str; + int i; + + ksym = symtab->symbol; + str = ((char *) ksym + nsyms * sizeof(struct old_module_symbol) + + n_ext_modules_used * sizeof(struct old_module_ref)); + + for (i = 0; i < HASH_BUCKETS; ++i) { + struct obj_symbol *sym; + for (sym = f->symtab[i]; sym; sym = sym->next) + if (sym->ksymidx >= 0) { + ksym->addr = obj_symbol_final_value(f, sym); + ksym->name = + (unsigned long) str - (unsigned long) symtab; + + strcpy(str, sym->name); + str += strlen(sym->name) + 1; + ksym++; + } + } + } + + if (n_ext_modules_used) { + struct old_module_ref *ref; + int i; + + ref = (struct old_module_ref *) + ((char *) symtab->symbol + nsyms * sizeof(struct old_module_symbol)); + + for (i = 0; i < n_ext_modules; ++i) + if (ext_modules[i].used) + ref++->module = ext_modules[i].addr; + } + } + + /* Fill in routines. */ + + routines.init = + obj_symbol_final_value(f, obj_find_symbol(f, "init_module")); + routines.cleanup = + obj_symbol_final_value(f, obj_find_symbol(f, "cleanup_module")); + + /* 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); + + /* image holds the complete relocated module, accounting correctly for + mod_use_count. However the old module kernel support assume that + it is receiving something which does not contain mod_use_count. */ + ret = old_sys_init_module(m_name, image + sizeof(long), + m_size | (flag_autoclean ? OLD_MOD_AUTOCLEAN + : 0), &routines, symtab); + if (ret) + perror_msg("init_module: %s", m_name); + + free(image); + free(symtab); + + return ret == 0; +} + +#else + +#define old_create_mod_use_count(x) TRUE +#define old_init_module(x, y, z) TRUE + +#endif /* BB_FEATURE_OLD_MODULE_INTERFACE */ + + + +/*======================================================================*/ +/* 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; + 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) { + error_msg("invalid parameter %s", key); + return 0; + } + + sym = obj_find_symbol(f, key); + + /* Also check that the parameter was not resolved from the kernel. */ + if (sym == NULL || sym->secidx > SHN_HIRESERVE) { + 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') { + 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 usefull, as the previous case + doesn't null 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))) { + 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) { + 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: + 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) { + error_msg("too many values for %s (max %d)", key, max); + return 0; + } + ++q; + break; + + default: + error_msg("invalid argument syntax for %s", key); + return 0; + } + } + + end_of_arg: + if (n < min) { + error_msg("too few values for %s (min %d)", key, min); + return 0; + } + + argc--, argv++; + } + + return 1; +} + +#ifdef BB_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; + 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 /* BB_FEATURE_INSMOD_VERSION_CHECKING */ + + +#ifdef BB_FEATURE_NEW_MODULE_INTERFACE + +/* 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; + } + 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; + } + 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: + 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; + } + 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, "__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; +} + + +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, "__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) { + 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, "init_module")); + module->cleanup = + obj_symbol_final_value(f, obj_find_symbol(f, "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; + } + + if (!arch_init_module(f, module)) + return 0; + + /* 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 = new_sys_init_module(m_name, (struct new_module *) image); + if (ret) + perror_msg("init_module: %s", m_name); + + free(image); + + return ret == 0; +} + +#else + +#define new_init_module(x, y, z) TRUE +#define new_create_this_module(x, y) 0 +#define new_create_module_ksymtab(x) +#define query_module(v, w, x, y, z) -1 + +#endif /* BB_FEATURE_NEW_MODULE_INTERFACE */ + + +/*======================================================================*/ + +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 { + 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) { + error_msg("%s of type %ld for %s", errmsg, + (long) ELFW(R_TYPE) (rel->r_info), + strtab + extsym->st_name); + } else { + 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) { + 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) { + 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)) { + error_msg("ELF file not for this architecture"); + return NULL; + } + if (f->header.e_type != ET_REL) { + error_msg("ELF file not a relocatable object"); + return NULL; + } + + /* Read the section headers. */ + + if (f->header.e_shentsize != sizeof(ElfW(Shdr))) { + 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) { + 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) { + perror_msg("error reading ELF section data"); + return NULL; + } + } else { + sec->contents = NULL; + } + break; + +#if SHT_RELM == SHT_REL + case SHT_RELA: + error_msg("RELA relocations not supported on this architecture"); + return NULL; +#else + case SHT_REL: + 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; + } + + 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))) { + 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) { + const char *name; + if (sym->st_name) + name = strtab + sym->st_name; + else + name = f->sections[sym->st_shndx]->name; + + obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, + sym->st_value, sym->st_size); + } + } + break; + + case SHT_RELM: + if (sec->header.sh_entsize != sizeof(ElfW(RelM))) { + 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 BB_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 = (char*) f->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) { + errorMsg("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[] = { + "cleanup_module", + "init_module", + "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)); +} + + + +extern int insmod_main( int argc, char **argv) +{ + int opt; + int k_crcs; + int k_new_syscalls; + int len; + char *tmp; + unsigned long m_size; + ElfW(Addr) m_addr; + FILE *fp; + struct obj_file *f; + struct stat st; + char m_name[FILENAME_MAX + 1] = "\0"; + int exit_status = EXIT_FAILURE; + int m_has_modinfo; +#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING + int k_version; + char k_strversion[STRVERSIONLEN]; + char m_strversion[STRVERSIONLEN]; + int m_version; + int m_crcs; +#endif + + /* Parse any options */ + while ((opt = getopt(argc, argv, "fkvxLo:")) > 0) { + switch (opt) { + case 'f': /* force loading */ + flag_force_load = 1; + break; + case 'k': /* module loaded by kerneld, auto-cleanable */ + flag_autoclean = 1; + break; + case 'v': /* verbose output */ + flag_verbose = 1; + break; + case 'x': /* do not export externs */ + flag_export = 0; + break; + case 'o': /* name the output module */ + strncpy(m_name, optarg, FILENAME_MAX); + 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; + default: + show_usage(); + } + } + + if (argv[optind] == NULL) { + show_usage(); + } + + /* Grab the module name */ + if ((tmp = strrchr(argv[optind], '/')) != NULL) { + tmp++; + } else { + tmp = argv[optind]; + } + len = strlen(tmp); + + if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') + len -= 2; + memcpy(m_fullName, tmp, len); + m_fullName[len]='\0'; + if (*m_name == '\0') { + strcpy(m_name, m_fullName); + } + strcat(m_fullName, ".o"); + + /* 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) { + struct utsname myuname; + + /* 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 (uname(&myuname) == 0) { + char module_dir[FILENAME_MAX]; + char real_module_dir[FILENAME_MAX]; + snprintf (module_dir, sizeof(module_dir), "%s/%s", + _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 (module_dir, real_module_dir) == NULL) + strcpy(real_module_dir, module_dir); + recursive_action(real_module_dir, TRUE, FALSE, FALSE, + check_module_name_match, 0, m_fullName); + } + + /* Check if we have found anything yet */ + if (m_filename[0] == '\0' || ((fp = fopen(m_filename, "r")) == NULL)) + { + char module_dir[FILENAME_MAX]; + 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) == FALSE) + { + if (m_filename[0] == '\0' + || ((fp = fopen(m_filename, "r")) == NULL)) + { + error_msg("%s: no module by that name found", m_fullName); + return EXIT_FAILURE; + } + } else + error_msg_and_die("%s: no module by that name found", m_fullName); + } + } else + safe_strncpy(m_filename, argv[optind], sizeof(m_filename)); + + printf("Using %s\n", m_filename); + + if ((f = obj_load(fp, LOADBITS)) == NULL) + 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 BB_FEATURE_INSMOD_VERSION_CHECKING + /* Version correspondence? */ + + k_version = get_kernel_version(k_strversion); + if (m_has_modinfo) { + m_version = new_get_module_version(f, m_strversion); + } else { + m_version = old_get_module_version(f, m_strversion); + if (m_version == -1) { + error_msg("couldn't find the kernel version the module was " + "compiled for"); + goto out; + } + } + + if (strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0) { + if (flag_force_load) { + 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, k_strversion); + } else { + 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, k_strversion); + goto out; + } + } + k_crcs = 0; +#endif /* BB_FEATURE_INSMOD_VERSION_CHECKING */ + + k_new_syscalls = !query_module(NULL, 0, NULL, 0, NULL); + + if (k_new_syscalls) { +#ifdef BB_FEATURE_NEW_MODULE_INTERFACE + if (!new_get_kernel_symbols()) + goto out; + k_crcs = new_is_kernel_checksummed(); +#else + error_msg("Not configured to support new kernels"); + goto out; +#endif + } else { +#ifdef BB_FEATURE_OLD_MODULE_INTERFACE + if (!old_get_kernel_symbols(m_name)) + goto out; + k_crcs = old_is_kernel_checksummed(); +#else + error_msg("Not configured to support old kernels"); + goto out; +#endif + } + +#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING + if (m_has_modinfo) + m_crcs = new_is_module_checksummed(f); + else + m_crcs = old_is_module_checksummed(f); + + if (m_crcs != k_crcs) + obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash); +#endif /* BB_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 (k_new_syscalls + ? !new_create_this_module(f, m_name) + : !old_create_mod_use_count(f)) + { + goto out; + } + + if (!obj_check_undefineds(f)) { + goto out; + } + obj_allocate_commons(f); + + /* done with the module name, on to the optional var=value arguments */ + ++optind; + + if (optind < argc) { + if (m_has_modinfo + ? !new_process_module_arguments(f, argc - optind, argv + optind) + : !old_process_module_arguments(f, argc - optind, argv + optind)) + { + goto out; + } + } + + arch_create_got(f); + hide_special_symbols(f); + + if (k_new_syscalls) + 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: + error_msg("A module named %s already exists", m_name); + goto out; + case ENOMEM: + error_msg("Can't allocate kernel memory for module; needed %lu bytes", + m_size); + goto out; + default: + 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 + */ + // f->imagebase = (char*) m_addr; + f->imagebase = (ElfW(Addr)) m_addr; + if (!obj_load_progbits(fp, f)) { + delete_module(m_name); + goto out; + } +#endif + + if (!obj_relocate(f, m_addr)) { + delete_module(m_name); + goto out; + } + + if (k_new_syscalls + ? !new_init_module(m_name, f, m_size) + : !old_init_module(m_name, f, m_size)) + { + delete_module(m_name); + goto out; + } + + exit_status = EXIT_SUCCESS; + +out: + fclose(fp); + return(exit_status); +} diff --git a/busybox/modutils/lsmod.c b/busybox/modutils/lsmod.c new file mode 100644 index 000000000..76ed2fdd8 --- /dev/null +++ b/busybox/modutils/lsmod.c @@ -0,0 +1,166 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini lsmod implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * Modified by Alcove, Julien Gaulmin and + * Nicolas Ferre 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + + +#ifdef BB_FEATURE_NEW_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; + +static 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; +} + +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)) { + perror_msg_and_die("QM_MODULES"); + } + + deps = xmalloc(depsize = 256); + printf("Module Size Used by\n"); + 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 */ + 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; + } + 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"); + } + + + return( 0); +} + +#else /*BB_FEATURE_OLD_MODULE_INTERFACE*/ + +extern int lsmod_main(int argc, char **argv) +{ + int fd, i; + char line[128]; + + puts("Module Size Used by"); + fflush(stdout); + + if ((fd = open("/proc/modules", O_RDONLY)) >= 0 ) { + while ((i = read(fd, line, sizeof(line))) > 0) { + write(fileno(stdout), line, i); + } + close(fd); + return 0; + } + perror_msg_and_die("/proc/modules"); + return 1; +} + +#endif /*BB_FEATURE_OLD_MODULE_INTERFACE*/ diff --git a/busybox/modutils/modprobe.c b/busybox/modutils/modprobe.c new file mode 100644 index 000000000..05b40c53f --- /dev/null +++ b/busybox/modutils/modprobe.c @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +/* + * really dumb modprobe implementation for busybox + * Copyright (C) 2001 Lineo, davidm@lineo.com + */ + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +static char cmd[128]; + +extern int modprobe_main(int argc, char** argv) +{ + int ch, rc = 0; + int loadall = 0, showconfig = 0, debug = 0, autoclean = 0, list = 0; + int show_only = 0, quiet = 0, remove_opt = 0, do_syslog = 0, verbose = 0; + char *load_type = NULL, *config = NULL; + + while ((ch = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) + switch(ch) { + case 'a': + loadall++; + break; + case 'c': + showconfig++; + break; + case 'd': + debug++; + break; + case 'k': + autoclean++; + break; + case 'l': + list++; + break; + case 'n': + show_only++; + break; + case 'q': + quiet++; + break; + case 'r': + remove_opt++; + break; + case 's': + do_syslog++; + break; + case 't': + load_type = optarg; + break; + case 'v': + verbose++; + break; + case 'C': + config = optarg; + break; + case 'V': + default: + show_usage(); + break; + } + + if (load_type || config) { + fprintf(stderr, "-t and -C not supported\n"); + exit(EXIT_FAILURE); + } + + if (showconfig) + exit(EXIT_SUCCESS); + + if (list) + exit(EXIT_SUCCESS); + + if (remove_opt) { + do { + sprintf(cmd, "rmmod %s %s %s", + optind >= argc ? "-a" : "", + do_syslog ? "-s" : "", + optind < argc ? argv[optind] : ""); + if (do_syslog) + syslog(LOG_INFO, "%s", cmd); + if (show_only || verbose) + printf("%s\n", cmd); + if (!show_only) + rc = system(cmd); + } while (++optind < argc); + exit(EXIT_SUCCESS); + } + + if (optind >= argc) { + fprintf(stderr, "No module or pattern provided\n"); + exit(EXIT_FAILURE); + } + + sprintf(cmd, "insmod %s %s %s", + do_syslog ? "-s" : "", + quiet ? "-q" : "", + autoclean ? "-k" : ""); + while (optind < argc) { + strcat(cmd, argv[optind]); + strcat(cmd, " "); + optind++; + } + if (do_syslog) + syslog(LOG_INFO, "%s", cmd); + if (show_only || verbose) + printf("%s\n", cmd); + if (!show_only) + rc = system(cmd); + else + rc = 0; + + exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); +} + + diff --git a/busybox/modutils/rmmod.c b/busybox/modutils/rmmod.c new file mode 100644 index 000000000..7596d0232 --- /dev/null +++ b/busybox/modutils/rmmod.c @@ -0,0 +1,62 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rmmod implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include "busybox.h" + +extern int delete_module(const char * name); + + +extern int rmmod_main(int argc, char **argv) +{ + int n, ret = EXIT_SUCCESS; + + /* Parse command line. */ + while ((n = getopt(argc, argv, "a")) != EOF) { + switch (n) { + case 'a': + /* Unload _all_ unused modules via NULL delete_module() call */ + if (delete_module(NULL)) + perror_msg_and_die("rmmod"); + return EXIT_SUCCESS; + default: + show_usage(); + } + } + + if (optind == argc) + show_usage(); + + for (n = optind; n < argc; n++) { + if (delete_module(argv[n]) < 0) { + perror_msg("%s", argv[n]); + ret = EXIT_FAILURE; + } + } + + return(ret); +} diff --git a/busybox/more.c b/busybox/more.c new file mode 100644 index 000000000..780cddf66 --- /dev/null +++ b/busybox/more.c @@ -0,0 +1,217 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini more implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * Latest version blended together by Erik Andersen , + * based on the original more implementation by Bruce, and code from the + * Debian boot-floppies team. + * + * Termios corrects by Vladimir Oleynik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include "busybox.h" + +static FILE *cin; + +#ifdef BB_FEATURE_USE_TERMIOS +#include +#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(fileno(cin), &initial_settings); +} + +static void gotsig(int sig) +{ + putchar('\n'); + exit(EXIT_FAILURE); +} +#endif /* BB_FEATURE_USE_TERMIOS */ + + +static int terminal_width = 79; /* not 80 in case terminal has linefold bug */ +static int terminal_height = 24; + + +extern int more_main(int argc, char **argv) +{ + int c, lines, input = 0; + int please_display_more_prompt = -1; + struct stat st; + FILE *file; + int len, page_height; + +#if defined BB_FEATURE_AUTOWIDTH && defined BB_FEATURE_USE_TERMIOS + struct winsize win = { 0, 0, 0, 0 }; +#endif + + argc--; + argv++; + + + /* not use inputing from terminal if usage: more > outfile */ + if(isatty(fileno(stdout))) { + cin = fopen(CURRENT_TTY, "r"); + if (!cin) + cin = xfopen(CONSOLE_DEV, "r"); + please_display_more_prompt = 0; +#ifdef BB_FEATURE_USE_TERMIOS + getTermSettings(fileno(cin), &initial_settings); + new_settings = initial_settings; + new_settings.c_lflag &= ~ICANON; + new_settings.c_lflag &= ~ECHO; +#ifndef linux + /* Hmm, in linux c_cc[] not parsed if set ~ICANON */ + new_settings.c_cc[VMIN] = 1; + new_settings.c_cc[VTIME] = 0; +#endif + setTermSettings(fileno(cin), &new_settings); + atexit(set_tty_to_initial_mode); + (void) signal(SIGINT, gotsig); + (void) signal(SIGQUIT, gotsig); + (void) signal(SIGTERM, gotsig); +#endif + } + + do { + if (argc == 0) { + file = stdin; + } else + file = wfopen(*argv, "r"); + if(file==0) + goto loop; + + fstat(fileno(file), &st); + + if(please_display_more_prompt>0) + please_display_more_prompt = 0; + +#if defined BB_FEATURE_AUTOWIDTH && defined BB_FEATURE_USE_TERMIOS + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (win.ws_row > 4) + terminal_height = win.ws_row - 2; + if (win.ws_col > 0) + terminal_width = win.ws_col - 1; +#endif + len=0; + lines = 0; + page_height = terminal_height; + while ((c = getc(file)) != EOF) { + + if (please_display_more_prompt>0) { + len = printf("--More-- "); + if (file != stdin) { +#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 BB_FEATURE_USE_TERMIOS + printf("\033[A"); /* up cursor */ +#endif + /* Erase the "More" message */ + putc('\r', stdout); + while (--len >= 0) + putc(' ', stdout); + putc('\r', stdout); + fflush(stdout); + len=0; + lines = 0; + page_height = terminal_height; + please_display_more_prompt = 0; + + 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') + if(please_display_more_prompt==0) + 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) { + if(please_display_more_prompt==0) + 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/mount.c b/busybox/mount.c new file mode 100644 index 000000000..eb6091f30 --- /dev/null +++ b/busybox/mount.c @@ -0,0 +1,472 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mount implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + * 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 , . + * 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 suppport is back - andersee + * + * 2000-01-12 Ben Collins , Borrowed utils-linux's + * mount to add loop support. + * + * 2000-04-30 Dave Cinege + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#if defined BB_FEATURE_USE_DEVPS_PATCH +# include /* For Erik's nifty devmtab device driver */ +#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 */ +}; + + +#if defined BB_FEATURE_MOUNT_LOOP +#include +#include +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); + +extern const char mtab_file[]; /* Defined in utility.c */ + +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}, + {"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}, + {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 BB_FEATURE_MOUNT_LOOP + char *lofile = NULL; +#endif + + if (fakeIt == FALSE) + { +#if defined BB_FEATURE_MOUNT_LOOP + if (use_loop==TRUE) { + int loro = flags & MS_RDONLY; + + lofile = specialfile; + + specialfile = find_unused_loop_device(); + if (specialfile == NULL) { + error_msg_and_die("Could not find a spare loop device"); + } + if (set_loop(specialfile, lofile, 0, &loro)) { + error_msg_and_die("Could not setup loop device"); + } + if (!(flags & MS_RDONLY) && loro) { /* loop is ro, but wanted rw */ + 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) { + 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 BB_FEATURE_MTAB_SUPPORT + if (useMtab == TRUE) { + 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 BB_FEATURE_MOUNT_LOOP + if (lofile != NULL) { + del_loop(specialfile); + } +#endif + + if (errno == EPERM) { + error_msg_and_die("permission denied. Are you root?"); + } + + return (FALSE); +} + + + +/* 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 BB_FEATURE_MOUNT_LOOP + if (gotone == FALSE && !strcasecmp("loop", options)) { /* loop device support */ + use_loop = TRUE; + gotone = TRUE; + } +#endif + if (*strflags && strflags != '\0' && gotone == FALSE) { + char *temp = strflags; + + temp += strlen(strflags); + *temp++ = ','; + *temp++ = '\0'; + } + if (gotone == FALSE) + strcat(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) { + static const char *noauto_array[] = { "tmpfs", "shm", "proc", "ramfs", "devpts", "devfs", 0 }; + const char **noauto_fstype; + const int num_of_filesystems = sysfs(3, 0, 0); + char buf[255]; + int i=0; + + filesystemType=buf; + + while(i < num_of_filesystems) { + sysfs(2, i++, filesystemType); + for (noauto_fstype = noauto_array; *noauto_fstype; noauto_fstype++) { + if (!strcmp(filesystemType, *noauto_fstype)) { + break; + } + } + if (!*noauto_fstype) { + status = do_mount(blockDevice, directory, filesystemType, + flags | MS_MGC_VAL, string_flags, + useMtab, fakeIt, mtab_opts, mount_all); + if (status == TRUE) + break; + } + } + } else { + status = do_mount(blockDevice, directory, filesystemType, + flags | MS_MGC_VAL, string_flags, useMtab, + fakeIt, mtab_opts, mount_all); + } + + if (status == FALSE) { + if (whineOnErrors == TRUE) { + perror_msg("Mounting %s on %s failed", blockDevice, directory); + } + return (FALSE); + } + return (TRUE); +} + +void show_mounts() +{ +#if defined BB_FEATURE_USE_DEVPS_PATCH + int fd, i, numfilesystems; + char device[] = "/dev/mtab"; + struct k_mntent *mntentlist; + + /* open device */ + fd = open(device, O_RDONLY); + if (fd < 0) + perror_msg_and_die("open failed for `%s'", device); + + /* How many mounted filesystems? We need to know to + * allocate enough space for later... */ + numfilesystems = ioctl (fd, DEVMTAB_COUNT_MOUNTS); + if (numfilesystems<0) + perror_msg_and_die( "\nDEVMTAB_COUNT_MOUNTS"); + mntentlist = (struct k_mntent *) xcalloc ( numfilesystems, sizeof(struct k_mntent)); + + /* Grab the list of mounted filesystems */ + if (ioctl (fd, DEVMTAB_GET_MOUNTS, mntentlist)<0) + perror_msg_and_die( "\nDEVMTAB_GET_MOUNTS"); + + for( i = 0 ; i < numfilesystems ; i++) { + printf( "%s %s %s %s %d %d\n", mntentlist[i].mnt_fsname, + mntentlist[i].mnt_dir, mntentlist[i].mnt_type, + mntentlist[i].mnt_opts, mntentlist[i].mnt_freq, + mntentlist[i].mnt_passno); + } +#ifdef BB_FEATURE_CLEAN_UP + /* Don't bother to close files or free memory. Exit + * does that automagically, so we can save a few bytes */ + free( mntentlist); + close(fd); +#endif + exit(EXIT_SUCCESS); +#else + FILE *mountTable = setmntent(mtab_file, "r"); + + if (mountTable) { + struct mntent *m; + + while ((m = getmntent(mountTable)) != 0) { + char *blockDevice = m->mnt_fsname; + if (strcmp(blockDevice, "/dev/root") == 0) { + blockDevice = find_real_root_device_name(blockDevice); + } + printf("%s on %s type %s (%s)\n", blockDevice, m->mnt_dir, + m->mnt_type, m->mnt_opts); +#ifdef BB_FEATURE_CLEAN_UP + if(blockDevice != m->mnt_fsname) + free(blockDevice); +#endif + } + endmntent(mountTable); + } else { + perror_msg_and_die("%s", mtab_file); + } + exit(EXIT_SUCCESS); +#endif +} + +extern int mount_main(int argc, char **argv) +{ + struct stat statbuf; + char string_flags_buf[1024] = ""; + char *string_flags = string_flags_buf; + char *extra_opts = string_flags_buf; + int flags = 0; + char *filesystemType = "auto"; + char *device = xmalloc(PATH_MAX); + char *directory = xmalloc(PATH_MAX); + int all = FALSE; + int fakeIt = FALSE; + int useMtab = TRUE; + int rc = EXIT_FAILURE; + int fstabmount = FALSE; + 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; + break; + case 'w': + flags &= ~MS_RDONLY; + break; + case 'a': + all = TRUE; + break; + case 'f': + fakeIt = TRUE; + break; +#ifdef BB_FEATURE_MTAB_SUPPORT + case 'n': + useMtab = FALSE; + break; +#endif + case 'v': + break; /* ignore -v */ + } + } + + if (!all && optind == argc) + show_mounts(); + + if (optind < argc) { + /* if device is a filename get its real path */ + if (stat(argv[optind], &statbuf) == 0) { + realpath(argv[optind], device); + } else { + safe_strncpy(device, argv[optind], PATH_MAX); + } + } + + if (optind + 1 < argc) { + if (realpath(argv[optind + 1], directory) == NULL) { + perror_msg_and_die("%s", directory); + } + } + + if (all == TRUE || optind + 1 == argc) { + struct mntent *m = NULL; + FILE *f = setmntent("/etc/fstab", "r"); + fstabmount = TRUE; + + if (f == NULL) + perror_msg_and_die( "\nCannot read /etc/fstab"); + + while ((m = getmntent(f)) != NULL) { + if (all == FALSE && optind + 1 == argc && ( + (strcmp(device, m->mnt_fsname) != 0) && + (strcmp(device, m->mnt_dir) != 0) ) ) { + continue; + } + + if (all == TRUE && ( // 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 or nfs, then mount it + (strstr(m->mnt_type, "nfs")) ) ) { + continue; + } + + if (all == TRUE || flags == 0) { // Allow single mount to override fstab flags + flags = 0; + *string_flags = '\0'; + parse_mount_options(m->mnt_opts, &flags, string_flags); + } + + strcpy(device, m->mnt_fsname); + strcpy(directory, m->mnt_dir); + filesystemType = strdup(m->mnt_type); +singlemount: + string_flags = strdup(string_flags); + rc = EXIT_SUCCESS; +#ifdef BB_NFSMOUNT + if (strchr(device, ':') != NULL) + filesystemType = "nfs"; + if (strcmp(filesystemType, "nfs") == 0) { + if (nfsmount (device, directory, &flags, &extra_opts, + &string_flags, 1)) { + 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 == FALSE) + break; + } + if (fstabmount == TRUE) + endmntent(f); + + if (all == FALSE && fstabmount == TRUE && m == NULL) + fprintf(stderr, "Can't find %s in /etc/fstab\n", device); + + return rc; + } + + goto singlemount; +} diff --git a/busybox/msh.c b/busybox/msh.c new file mode 100644 index 000000000..92a0f8536 --- /dev/null +++ b/busybox/msh.c @@ -0,0 +1,4868 @@ +/* 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmdedit.h" +#include "busybox.h" + + +/* -------- sh.h -------- */ +/* + * shell + */ + +#define LINELIM 2100 +#define NPUSH 8 /* limit to input nesting */ + +#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 defintions + */ +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 & */ + +/* + * actions determining the environment of a process + */ +#define BIT(i) (1<<(i)) +#define FEXEC BIT(0) /* execute without forking */ + +/* + * 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) + +static char **dolv; +static int dolc; +static int exstat; +static char gflg; +static int interactive; /* 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 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 */ + +static struct wdblock *wdlist; +static struct wdblock *iolist; + +/* + * parsing & execution environment + */ +static struct env { + char *linep; + struct io *iobase; + struct io *iop; + xint *errpt; + 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 ))(void); + +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 (unsigned u, 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 (void); +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 +#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 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 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 *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)()); + +/* + * 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 (*fn)()); +static int remap (int fd ); +static int openpipe (int *pv ); +static void closepipe (int *pv ); +static struct io *setbase (struct io *ip ); + +static struct ioarg temparg; /* temporary for PUSHIO */ +#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, int *pforked ); +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(void); +static int dohelp(void); +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, void (*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)(), int key); +static void badid(char *s ); +static int doset(struct op *t ); +static void varput (char *s, int out ); +static int dotimes(void); +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 (*fn)()); +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}, + {"{", '{'}, + {"}", '}'}, + {0, 0}, +}; + + +struct builtincmd { + const char *name; + int (*builtinfunc)(); +}; +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} +}; + +/* Globals */ +extern char **environ; /* environment pointer */ +static char **dolv; +static int dolc; +static int exstat; +static char gflg; +static int interactive; /* 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]; +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 struct ioarg ioargstack[NPUSH]; +static struct io iostack[NPUSH]; +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 *elinep = line+sizeof(line)-5; +static char *null = ""; +static int heedint =1; +static struct env e ={line, iostack, iostack-1, (xint *)NULL, FDBASE, (struct env *)NULL}; +static void (*qflag)(int) = SIG_IGN; +static char shellname[] = "/bin/sh"; +static char search[] = ":/bin:/usr/bin"; +static int startl; +static int peeksym; +static int nlseen; +static int iounit = IODEFAULT; +static YYSTYPE yylval; +static struct iobuf sharedbuf = {AFID_NOBUF}; +static struct iobuf mainbuf = {AFID_NOBUF}; +static unsigned bufid = AFID_ID; /* buffer id counter */ +static struct ioarg temparg = {0, 0, 0, AFID_NOBUF, 0}; +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; + + +#ifdef BB_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)(); + + 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, shellname); + export(shell); + + homedir = lookup("HOME"); + if (homedir->value == null) + setval(homedir, "/"); + export(homedir); + + setval(lookup("$"), itoa(getpid(), 5)); + + path = lookup("PATH"); + if (path->value == null) + setval(path, search); + export(path); + + ifs = lookup("IFS"); + if (ifs->value == null) + setval(ifs, " \t\n"); + + prompt = lookup("PS1"); +#ifdef BB_FEATURE_SH_FANCY_PROMPT + if (prompt->value == null) +#endif + setval(prompt, "$ "); + if (geteuid() == 0) { + setval(prompt, "# "); + prompt->status &= ~EXPORT; + } + cprompt = lookup("PS2"); +#ifdef BB_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; + if (newfile(name = *++argv)) + exit(1); + } + } + setdash(); + if (e.iop < iostack) { + PUSHIO(afile, 0, iof); + if (isatty(0) && isatty(1) && !cflag) { + interactive++; + printf( "\n\n" BB_BANNER " Built-in shell (msh)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); + } + } + 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)); + + for (;;) { + if (interactive && e.iop <= iostack) { +#ifdef BB_FEATURE_COMMAND_EDITING + current_prompt=prompt->value; +#else + prs(prompt->value); +#endif + } + onecommand(); + } +} + +static void +setdash() +{ + register char *cp; + register int c; + char m['z'-'a'+1]; + + cp = m; + for (c='a'; c<='z'; c++) + if (flag[c]) + *cp++ = c; + *cp = 0; + setval(lookup("-"), m); +} + +static int +newfile(s) +register char *s; +{ + register int f; + + if (strcmp(s, "-") != 0) { + f = open(s, 0); + if (f < 0) { + prs(s); + err(": cannot open"); + return(1); + } + } else + f = 0; + next(remap(f)); + return(0); +} + +static void +onecommand() +{ + register int i; + jmp_buf m1; + + 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) { + 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']) + 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() +{ + 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; + + 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; + + 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, -1)); +} + +static char * +itoa(u, n) +register unsigned u; +int n; +{ + register char *cp; + static char s[20]; + int m; + + m = 0; + if (n < 0 && (int) u < 0) { + m++; + u = -u; + } + cp = s+sizeof(s); + *--cp = 0; + do { + *--cp = u%10 + '0'; + u /= 10; + } while (--n > 0 || u); + if (m) + *--cp = '-'; + return(cp); +} + +static void +next(f) +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])) /* not an internal symbol ($# etc) */ + vp->status |= RONLY; +} + +static int +isassign(s) +register char *s; +{ + if (!isalpha((int)*s)) + return(0); + for (; *s != '='; s++) + if (*s == 0 || !isalnum(*s)) + return(0); + return(1); +} + +static int +assign(s, cf) +register char *s; +int cf; +{ + register char *cp; + struct var *vp; + + if (!isalpha(*s)) + return(0); + for (cp = s; *cp != '='; cp++) + if (*cp == 0 || !isalnum(*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; +{ + if (!isalpha(*cp++)) + return(0); + while (*cp) + if (!isalnum(*cp++)) + 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)) { + 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(65000); + brktop = brkaddr + 65000; + + 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() +{ + 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; + + t = command(cf); + if (t != NULL) { + while ((c = yylex(0)) == '|') { + if ((p = command(CONTIN)) == NULL) + SYNTAXERR; + if (t->type != TPAREN && t->type != TCOM) { + /* shell statement */ + t = block(TPAREN, t, NOBLOCK, NOWORDS); + } + t = block(TPIPE, t, p, NOWORDS); + } + peeksym = c; + } + return(t); +} + +static struct op * +andor() +{ + register struct op *t, *p; + register int c; + + t = pipeline(0); + if (t != NULL) { + while ((c = yylex(0)) == LOGAND || c == LOGOR) { + if ((p = pipeline(CONTIN)) == NULL) + SYNTAXERR; + t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); + } + peeksym = c; + } + return(t); +} + +static struct op * +c_list() +{ + register struct op *t, *p; + register int c; + + 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); + } + peeksym = c; + } + return(t); +} + + +static int +synio(cf) +int cf; +{ + register struct ioword *iop; + register int i; + register int c; + + 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); + return(1); +} + +static void +musthave(c, cf) +int c, cf; +{ + if ((peeksym = yylex(cf)) != c) + 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; + + 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; + + iosave = iolist; + iolist = NULL; + if (multiline) + cf |= CONTIN; + while (synio(cf)) + cf = 0; + switch (c = yylex(cf)) { + 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; + } + while (synio(0)) + ; + t = namelist(t); + iolist = iosave; + 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) + t = list(t, casepart()); + return(t); +} + +static struct op * +casepart() +{ + register struct op *t; + + t = newtp(); + t->type = TPAT; + t->words = pattern(); + musthave(')', 0); + t->left = c_list(); + if ((peeksym = yylex(CONTIN)) != ESAC) + musthave(BREAK, CONTIN); + 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; +{ + 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; + + t = newtp(); + t->type = type; + t->left = t1; + t->right = t2; + t->words = wp; + return(t); +} + +static int +rlookup(n) +register char *n; +{ + register struct res *rp; + + for (rp = restab; rp->r_name; rp++) + if (strcmp(rp->r_name, n) == 0) + return(rp->r_val); + return(0); +} + +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; + return(t); +} + +static struct op * +namelist(t) +register struct op *t; +{ + 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; + e.linep = line; + atstart = startl; + startl = 0; + yylval.i = 0; + +loop: + while ((c = my_getc(0)) == ' ' || c == '\t') + ; + 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 '#': + while ((c = my_getc(0)) != 0 && c != '\n') + ; + unget(c); + goto loop; + + case 0: + return(c); + + case '$': + *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 ';': + if ((c1 = dual(c)) != '\0') { + startl = 1; + return(c1); + } + startl = 1; + 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 BB_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]; + + *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 BB_FEATURE_COMMAND_EDITING + current_prompt=cprompt->value; +#else + prs(cprompt->value); +#endif + } + *e.linep++ = c; + } + *e.linep++ = c; + return(0); +} + +static int +dual(c) +register int c; +{ + char s[3]; + register char *cp = s; + + *cp++ = c; + *cp++ = my_getc(0); + *cp = 0; + if ((c = rlookup(s)) == 0) + unget(*--cp); + return(c); +} + +static void +diag(ec) +register int ec; +{ + register int c; + + 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) { + 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 brkcon bc; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) ℘ +#endif + + + if (t == NULL) + return(0); + rv = 0; + a = areanum++; + wp = (wp2 = t->words) != NULL + ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY) + : NULL; + + switch(t->type) { + case TPAREN: + case TCOM: + { + int child; + rv = forkexec(t, pin, pout, act, wp, &child); + if (child) { + exstat = rv; + leave(); + } + } + 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; + + 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 = ""; + if ((t1 = findcase(t->left, cp)) != NULL) + rv = execute(t1, pin, pout, 0); + 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); + } + return(rv); +} + +static int +forkexec( register struct op *t, int *pin, int *pout, int act, char **wp, int *pforked) +{ + int i, rv; + int (*shcom)() = NULL; + register int f; + char *cp = NULL; + struct ioword **iopp; + int resetsig; + char **owp; + + int *hpin = pin; + int *hpout = pout; + int hforked; + char *hwp; + int hinteractive; + int hintr; + struct brkcon * hbrklist; + int hexecflg; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &pin; + (void) &pout; + (void) ℘ + (void) &shcom; + (void) &cp; + (void) &resetsig; + (void) &owp; +#endif + + owp = wp; + resetsig = 0; + *pforked = 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']) + echo (cp ? wp: owp); + if (cp == NULL && t->ioact == NULL) { + while ((cp = *owp++) != NULL && assign(cp, COPYV)) + ; + return(setstatus(0)); + } + else if (cp != NULL) + shcom = inbuilt(cp); + } + t->words = wp; + f = act; + if (shcom == NULL && (f & FEXEC) == 0) { + + hpin = pin; + hpout = pout; + hforked = *pforked; + hwp = *wp; + hinteractive = interactive; + hintr = intr; + hbrklist = brklist; + hexecflg = execflg; + + i = vfork(); + if (i != 0) { + /* who wrote this crappy non vfork safe shit? */ + pin = hpin; + pout = hpout; + *pforked = hforked; + *wp = hwp; + interactive = hinteractive; + intr = hintr; + brklist = hbrklist; + execflg = hexecflg; + + *pforked = 0; + if (i == -1) + return(rv); + if (pin != NULL) + closepipe(pin); + return(pout==NULL? setstatus(waitfor(i,0)): 0); + } + + if (interactive) { + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + resetsig = 1; + } + interactive = 0; + intr = 0; + (*pforked)++; + 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"); + 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"); + return(-1); + } + while (*iopp) + if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) + return(rv); + } + if (shcom) + return(setstatus((*shcom)(t))); + /* should use FIOCEXCL */ + for (i=FDBASE; itype == TPAREN) + exit(execute(t->left, NOPIPE, NOPIPE, FEXEC)); + if (wp[0] == NULL) + exit(0); + + cp = rexecve(wp[0], wp, makenv()); + prs(wp[0]); prs(": "); warn(cp); + if (!execflg) + trap[0] = NULL; + 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; + + 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) + return((struct op **)NULL); + if (t->type == TLIST) { + if ((tp = find1case(t->left, w)) != NULL) + return(tp); + t1 = t->right; /* TPAT */ + } else + t1 = t; + for (wp = t1->words; *wp;) + if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) + return(&t1->left); + 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 BB_FEATURE_SH_STANDALONE_SHELL + char *name = c; +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + name = get_last_path_component(name); +#endif + 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 + + 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';) + ; + + execve(e.linep, v, envp); + switch (errno) { + case ENOEXEC: + *v = e.linep; + tp = *--v; + *v = e.linep; + execve("/bin/sh", v, envp); + *v = tp; + return("no Shell"); + + case ENOMEM: + return("program too big"); + + 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(argp, f) +struct ioarg *argp; +int (*f)(); +{ + 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 + + 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(); + } + wdlist = swdlist; + iolist = siolist; + failpt = ofail; + outtree = otree; + freearea(areanum--); + return(rv); +} + +/* -------- do.c -------- */ + +/* + * built-in commands: doX + */ + +static int dohelp() +{ + 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 BB_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() +{ + 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()); + 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; + + if ((cp = t->words[1]) == NULL) + return(0); + sp = any('/', cp)? ":": path->value; + while (*sp) { + tp = e.linep; + while (*sp && (*tp = *sp++) != ':') + tp++; + if (tp != e.linep) + *tp++ = '/'; + for (i = 0; (*tp++ = cp[i++]) != '\0';) + ; + if ((i = open(e.linep, 0)) >= 0) { + exstat = 0; + next(remap(i)); + return(exstat); + } + } + 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, void (*f)(int)) +{ + 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)); + 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(wp, f, key) +register char **wp; +void (*f)(); +int key; +{ + 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)) { + write(out, s, strlen(s)); + write(out, "\n", 1); + } +} + + +/* + * Copyright (c) 1999 Herbert Xu + * This file contains code for the times builtin. + */ +static int dotimes () +{ + 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))() +{ + const struct builtincmd *bp; + + for (bp = builtincmds; bp->name != NULL; bp++) + if (strcmp(bp->name, s) == 0) + return(bp->builtinfunc); + + return((int(*)())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) ℘ + (void) ≈ +#endif + 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() + +{ + register struct wdblock *wb; + register struct var *vp; + + wb = NULL; + for (vp = vlist; vp; vp = vp->next) + if (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; + + 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 + 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; + + 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)) + 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)) + scanequals = 0; + } + *e.linep++ = c; + } + *e.linep++ = 0; + return(sp); +} + +/* + * Get characters, substituting for ` and $ + */ +static int +subgetc(ec, quoted) +register int ec; +int quoted; +{ + register char c; + +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; + + c = readc(); + s = e.linep; + if (c != '{') { + *e.linep++ = c; + if (isalpha(c)) { + while ((c = readc())!=0 && isalnum(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; +{ + register int i; + char *cp; + int pf[2]; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &cp; +#endif + for (cp = e.iop->argp->aword; *cp != '`'; cp++) + if (*cp == 0) { + err("no closing `"); + return(0); + } + if (openpipe(pf) < 0) + return(0); + if ((i = vfork()) == -1) { + closepipe(pf); + err("try again"); + return(0); + } + if (i != 0) { + e.iop->argp->aword = ++cp; + close(pf[1]); + PUSHIO(afile, remap(pf[0]), quoted? qgravechar: gravechar); + return(1); + } + *cp = 0; + /* allow trapped signals */ + for (i=0; i<=_NSIG; i++) + if (ourtrap[i] && signal(i, SIG_IGN) != SIG_IGN) + signal(i, SIG_DFL); + dup2(pf[1], 1); + closepipe(pf); + flag['e'] = 0; + flag['v'] = 0; + flag['n'] = 0; + cp = strsave(e.iop->argp->aword, 0); + areanum = 1; + freehere(areanum); + freearea(areanum); /* free old space */ + e.oenv = NULL; + e.iop = (e.iobase = iostack) - 1; + unquote(cp); + interactive = 0; + PUSHIO(aword, cp, nlchar); + onecommand(); + 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; iw_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; iw_nword; i++) + DELETE(cl->w_words[i]); + DELETE(cl); + } + for(i=0; iw_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; iw_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; kw_words; + for (i=0; iw_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 != '\'' && 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; + + for (; e.iop >= e.iobase; e.iop--) + 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 BB_FEATURE_COMMAND_EDITING + current_prompt=prompt->value; +#else + prs(prompt->value); +#endif + } + } + } + if (e.iop >= iostack) + return(0); + leave(); + /* NOTREACHED */ + return(0); +} + +static void +ioecho(c) +char c; +{ + if (flag['v']) + write(2, &c, sizeof c); +} + +static void +pushio(argp, fn) +struct ioarg *argp; +int (*fn)(); +{ + if (++e.iop >= &iostack[NPUSH]) { + e.iop--; + err("Shell input nested too deeply"); + gflg++; + return; + } + e.iop->iofn = fn; + + if (argp->afid != AFID_NOBUF) + e.iop->argp = argp; + else { + e.iop->argp = ioargstack + (e.iop - iostack); + *e.iop->argp = *argp; + e.iop->argp->afbuf = e.iop == &iostack[0] ? &mainbuf : &sharedbuf; + if (isatty(e.iop->argp->afile) == 0 && + (e.iop == &iostack[0] || + lseek(e.iop->argp->afile, 0L, 1) != -1)) { + if (++bufid == AFID_NOBUF) + bufid = AFID_ID; + e.iop->argp->afid = bufid; + } + } + + 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 == gravechar || fn == qgravechar) + e.iop->task = XGRAVE; + else + e.iop->task = XOTHER; +} + +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 BB_FEATURE_COMMAND_EDITING + if (interactive) { + 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&0177: (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; + + 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, 0)); +} + +static void +closef(i) +register int i; +{ + if (i > 2) + close(i); +} + +static void +closeall() +{ + register int u; + + for (u=NUFILE; u= 0 && fd < e.iofd); + for (i=0; ih_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; + + /* 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; + + tf = mkstemp(tname); + if (tf < 0) + return; + *name = strsave(tname, areanum); + if (newenv(setjmp(errpt = ev)) != 0) + unlink(tname); + else { + pushio(e.iop->argp, e.iop->iofn); + e.iobase = e.iop; + for (;;) { + if (interactive && e.iop <= iostack) { +#ifdef BB_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 == 0) + return(-1); + 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; + + 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; + + 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/mt.c b/busybox/mt.c new file mode 100644 index 000000000..49dc70ac6 --- /dev/null +++ b/busybox/mt.c @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#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) { + show_usage(); + } + + if (strcmp(argv[1], "-f") == 0) { + if (argc < 4) { + 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) { + 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) + perror_msg_and_die("%s", file); + + switch (code->value) { + case MTTELL: + if (ioctl(fd, MTIOCPOS, &position) < 0) + perror_msg_and_die("%s", file); + printf ("At block %d.\n", (int) position.mt_blkno); + break; + + default: + if (ioctl(fd, MTIOCTOP, &op) != 0) + perror_msg_and_die("%s", file); + break; + } + + return EXIT_SUCCESS; +} diff --git a/busybox/mv.c b/busybox/mv.c new file mode 100644 index 000000000..b890abf6e --- /dev/null +++ b/busybox/mv.c @@ -0,0 +1,168 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mv implementation for busybox + * + * + * Copyright (C) 2000 by 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 +#include +#include +#include +#include +#include + +#include "busybox.h" + +static int flags; + +static int manual_rename(const char *source, const char *dest) +{ + struct stat source_stat; + struct stat dest_stat; + int source_exists = 1; + int dest_exists = 1; + + if (stat(source, &source_stat) < 0) { + if (errno != ENOENT) { + perror_msg("unable to stat `%s'", source); + return -1; + } + source_exists = 0; + } + + if (stat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + perror_msg("unable to stat `%s'", dest); + return -1; + } + dest_exists = 0; + } + + if (dest_exists) { + if (S_ISDIR(dest_stat.st_mode) && + (!source_exists || !S_ISDIR(source_stat.st_mode))) { + error_msg("cannot overwrite directory with non-directory"); + return -1; + } + + if (!S_ISDIR(dest_stat.st_mode) && source_exists && + S_ISDIR(source_stat.st_mode)) { + error_msg("cannot overwrite non-directory with directory"); + return -1; + } + + if (unlink(dest) < 0) { + perror_msg("cannot remove `%s'", dest); + return -1; + } + } + + if (copy_file(source, dest, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS | + FILEUTILS_PRESERVE_SYMLINKS) < 0) + return -1; + + if (remove_file(source, FILEUTILS_RECUR | FILEUTILS_FORCE) < 0) + return -1; + + return 0; +} + +static int move_file(const char *source, const char *dest) +{ + struct stat dest_stat; + int dest_exists = 1; + + if (stat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + perror_msg("unable to stat `%s'", dest); + return -1; + } + dest_exists = 0; + } + + if (dest_exists && !(flags & FILEUTILS_FORCE) && + ((access(dest, W_OK) < 0 && isatty(0)) || + (flags & FILEUTILS_INTERACTIVE))) { + fprintf(stderr, "mv: overwrite `%s'? ", dest); + if (!ask_confirmation()) + return 0; + } + + if (rename(source, dest) < 0) { + if (errno == EXDEV) + return manual_rename(source, dest); + + perror_msg("unable to rename `%s'", source); + return -1; + } + + return 0; +} + +extern int mv_main(int argc, char **argv) +{ + int status = 0; + int opt; + int i; + + while ((opt = getopt(argc, argv, "fi")) != -1) + switch (opt) { + case 'f': + flags &= ~FILEUTILS_INTERACTIVE; + flags |= FILEUTILS_FORCE; + break; + case 'i': + flags &= ~FILEUTILS_FORCE; + flags |= FILEUTILS_INTERACTIVE; + break; + default: + show_usage(); + } + + if (optind + 2 > argc) + show_usage(); + + if (optind + 2 == argc) { + struct stat dest_stat; + int dest_exists = 1; + + if (stat(argv[optind + 1], &dest_stat) < 0) { + if (errno != ENOENT) + perror_msg_and_die("unable to stat `%s'", argv[optind + 1]); + dest_exists = 0; + } + + if (!dest_exists || !S_ISDIR(dest_stat.st_mode)) { + if (move_file(argv[optind], argv[optind + 1]) < 0) + status = 1; + return status; + } + } + + for (i = optind; i < argc - 1; i++) { + char *dest = concat_path_file(argv[argc - 1], + get_last_path_component(argv[i])); + if (move_file(argv[i], dest) < 0) + status = 1; + free(dest); + } + + return status; +} diff --git a/busybox/nc.c b/busybox/nc.c new file mode 100644 index 000000000..5335872e5 --- /dev/null +++ b/busybox/nc.c @@ -0,0 +1,137 @@ +/* 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +int nc_main(int argc, char **argv) +{ + int do_listen = 0, lport = 0, tmpfd, opt, sfd; + char buf[BUFSIZ]; + + struct sockaddr_in address; + struct hostent *hostinfo; + + fd_set readfds, testfds; + + while ((opt = getopt(argc, argv, "lp:")) > 0) { + switch (opt) { + case 'l': + do_listen++; + break; + case 'p': + lport = atoi(optarg); + break; + default: + show_usage(); + } + } + + if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc)) + show_usage(); + + if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + perror_msg_and_die("socket"); + + address.sin_family = AF_INET; + + if (lport != 0) { + memset(&address.sin_addr, 0, sizeof(address.sin_addr)); + address.sin_port = htons(lport); + + if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + perror_msg_and_die("bind"); + } + + if (do_listen) { + socklen_t addrlen = sizeof(address); + + if (listen(sfd, 1) < 0) + perror_msg_and_die("listen"); + + if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0) + 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 = htons(atoi(argv[optind+1])); + + if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + perror_msg_and_die("connect"); + } + + 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) + 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) + 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 (full_write(ofd, buf, nread) < 0) + perror_msg_and_die("write"); + } + } + } +} diff --git a/busybox/networking/hostname.c b/busybox/networking/hostname.c new file mode 100644 index 000000000..d87851509 --- /dev/null +++ b/busybox/networking/hostname.c @@ -0,0 +1,128 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: hostname.c,v 1.30 2001/06/26 02:06:08 bug1 Exp $ + * Mini hostname implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * adjusted by Erik Andersen 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 +#include +#include +#include +#include +#include +#include +#include "busybox.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) + error_msg_and_die("you must be root to change the hostname"); + else + perror_msg_and_die("sethostname"); + } + } else { + f = xfopen(s, "r"); + fgets(buf, 255, f); +#ifdef BB_FEATURE_CLEAN_UP + fclose(f); +#endif + chomp(buf); + do_sethostname(buf, 0); + } +} + +int hostname_main(int argc, char **argv) +{ + int opt_short = 0; + int opt_domain = 0; + int opt_ip = 0; + struct hostent *h; + char *filename = NULL; + char buf[255]; + char *s = NULL; + + if (argc < 1) + show_usage(); + + while (--argc > 0 && **(++argv) == '-') { + while (*(++(*argv))) { + switch (**argv) { + case 's': + opt_short = 1; + break; + case 'i': + opt_ip = 1; + break; + case 'd': + opt_domain = 1; + break; + case 'F': + if (--argc == 0) { + show_usage(); + } + filename = *(++argv); + break; + case '-': + if (strcmp(++(*argv), "file") || --argc ==0 ) { + show_usage(); + } + filename = *(++argv); + break; + default: + show_usage(); + } + if (filename != NULL) + break; + } + } + + if (argc >= 1) { + do_sethostname(*argv, 0); + } else if (filename != NULL) { + do_sethostname(filename, 1); + } else { + gethostname(buf, 255); + if (opt_short) { + s = strchr(buf, '.'); + if (!s) + s = buf; + *s = 0; + puts(buf); + } else if (opt_domain) { + s = strchr(buf, '.'); + puts(s ? s + 1 : ""); + } else if (opt_ip) { + h = xgethostbyname(buf); + puts(inet_ntoa(*(struct in_addr *) (h->h_addr))); + } else { + puts(buf); + } + } + return(0); +} diff --git a/busybox/networking/ifconfig.c b/busybox/networking/ifconfig.c new file mode 100644 index 000000000..7f3978a4a --- /dev/null +++ b/busybox/networking/ifconfig.c @@ -0,0 +1,504 @@ +/* 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, + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the 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.11 2001/07/07 05:19:52 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. + */ + +#include +#include +#include // strcmp and friends +#include // isdigit and friends +#include /* offsetof */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#ifdef BB_FEATURE_IFCONFIG_SLIP +#include +#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 + +/* + * 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. */ +#define A_HOSTNAME 0x100 /* Set if it is ip addr. */ +#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */ + +/* + * 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_USHORT) +#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_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) + + +/* + * 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; + const unsigned char flags; + const unsigned int arg_flags; + 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 BB_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 BB_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. */ + {"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 BB_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 BB_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 + {"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 BB_FEATURE_IFCONFIG_HW +static int in_ether(char *bufp, struct sockaddr *sap); +#endif + +#ifdef BB_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; + struct sockaddr_in sai_hostname, sai_netmask; +#ifdef BB_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; + char *p; + char host[128]; + unsigned int mask; + unsigned int did_flags; + + goterr = 0; + did_flags = 0; + + /* skip argv[0] */ + ++argv; + --argc; + +#ifdef BB_FEATURE_IFCONFIG_STATUS + if ((argc > 0) && (strcmp(*argv,"-a") == 0)) { + interface_opt_a = 1; + --argc; + ++argv; + } +#endif + + if(argc <= 1) { +#ifdef BB_FEATURE_IFCONFIG_STATUS + return display_interfaces(argc ? *argv : NULL); +#else + 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) { + 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) { + show_usage(); + } + if (*++argv == NULL) { + if (mask & A_ARG_REQ) { + 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 BB_FEATURE_IFCONFIG_HW + if (mask & A_CAST_RESOLVE) { +#endif + safe_strncpy(host, *argv, (sizeof host)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + if (!strcmp(host, "default")) { + /* Default is special, meaning 0.0.0.0. */ + sai.sin_addr.s_addr = INADDR_ANY; + } else if ((!strcmp(host, "+")) && (mask & A_BROADCAST) && + (did_flags & (A_NETMASK|A_HOSTNAME))) { + /* + is special, meaning broadcast is derived. */ + sai.sin_addr.s_addr = (~sai_netmask.sin_addr.s_addr) | + (sai_hostname.sin_addr.s_addr & sai_netmask.sin_addr.s_addr); + } else if (inet_aton(host, &sai.sin_addr) == 0) { + /* It's not a dotted quad. */ + ++goterr; + continue; + } + if(mask & A_HOSTNAME) + sai_hostname = sai; + if(mask & A_NETMASK) + sai_netmask = sai; + p = (char *) &sai; +#ifdef BB_FEATURE_IFCONFIG_HW + } else { /* A_CAST_HOST_COPY_IN_ETHER */ + /* This is the "hw" arg case. */ + if (strcmp("ether", *argv) || (*++argv == NULL)) { + show_usage(); + } + safe_strncpy(host, *argv, (sizeof host)); + if (in_ether(host, &sa)) { + fprintf(stderr, "invalid hw-addr %s\n", 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 BB_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: + } /* end of while-loop */ + + return goterr; +} + +#ifdef BB_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; + + for (i = 0 ; i < ETH_ALEN ; i++) { + val = 0; + + /* We might get a semicolon here - not required. */ + if (i && (*bufp == ':')) { + bufp++; + } + + for (j=0 ; j<2 ; j++) { + c = *bufp; + if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'a' && c <= 'f') { + c -= ('a' - 10); + } else if (c >= 'A' && c <= 'F') { + c -= ('A' - 10); + } else if (j && (c == ':' || c == 0)) { + break; + } else { + return -1; + } + ++bufp; + val <<= 4; + val += c; + } + *ptr++ = val; + } + + return (int) (*bufp); /* Error if we don't end at end of string. */ +} +#endif diff --git a/busybox/networking/nc.c b/busybox/networking/nc.c new file mode 100644 index 000000000..5335872e5 --- /dev/null +++ b/busybox/networking/nc.c @@ -0,0 +1,137 @@ +/* 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +int nc_main(int argc, char **argv) +{ + int do_listen = 0, lport = 0, tmpfd, opt, sfd; + char buf[BUFSIZ]; + + struct sockaddr_in address; + struct hostent *hostinfo; + + fd_set readfds, testfds; + + while ((opt = getopt(argc, argv, "lp:")) > 0) { + switch (opt) { + case 'l': + do_listen++; + break; + case 'p': + lport = atoi(optarg); + break; + default: + show_usage(); + } + } + + if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc)) + show_usage(); + + if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + perror_msg_and_die("socket"); + + address.sin_family = AF_INET; + + if (lport != 0) { + memset(&address.sin_addr, 0, sizeof(address.sin_addr)); + address.sin_port = htons(lport); + + if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + perror_msg_and_die("bind"); + } + + if (do_listen) { + socklen_t addrlen = sizeof(address); + + if (listen(sfd, 1) < 0) + perror_msg_and_die("listen"); + + if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0) + 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 = htons(atoi(argv[optind+1])); + + if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + perror_msg_and_die("connect"); + } + + 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) + 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) + 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 (full_write(ofd, buf, nread) < 0) + perror_msg_and_die("write"); + } + } + } +} diff --git a/busybox/networking/nslookup.c b/busybox/networking/nslookup.c new file mode 100644 index 000000000..9b7cb645c --- /dev/null +++ b/busybox/networking/nslookup.c @@ -0,0 +1,183 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini nslookup implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* + | I'm only implementing non-interactive mode; + | I totally forgot nslookup even had an interactive mode. + | + | [ TODO ] + | + find out how to use non-default name servers + */ + +/* only works for IPv4 */ +static int addr_fprint(char *addr) +{ + u_int8_t split[4]; + u_int32_t ip; + u_int32_t *x = (u_int32_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) +{ + if (host) { + printf("Name: %s\n", 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 u_int32_t + */ +static u_int32_t str_to_addr(const char *addr) +{ + u_int32_t split[4]; + u_int32_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 */ +} + +#ifdef __UCLIBC__ +#warning FIXME after fixing uClibc to define struct _res +static inline void server_print(void) +{ + printf("Server: %s\n", "default"); + printf("Address: %s\n\n", "default"); +} +#else +/* 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)); + printf("\n"); +} +#endif + +/* 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; + + if (argc < 2 || *argv[1]=='-') { + show_usage(); + } + + res_init(); + server_print(); + if (is_ip_address(argv[1])) { + host = gethostbyaddr_wrapper(argv[1]); + } else { + host = gethostbyname(argv[1]); + } + hostent_fprint(host); + return EXIT_SUCCESS; +} + +/* $Id: nslookup.c,v 1.24 2001/07/06 17:51:29 andersen Exp $ */ diff --git a/busybox/networking/ping.c b/busybox/networking/ping.c new file mode 100644 index 000000000..5ca5dd9e0 --- /dev/null +++ b/busybox/networking/ping.c @@ -0,0 +1,555 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: ping.c,v 1.46 2001/07/17 01:12:36 andersen Exp $ + * Mini ping implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +/* It turns out that libc5 doesn't have proper icmp support + * built into it header files, so we have to supplement it */ +#if __GNU_LIBRARY__ < 5 +static const int ICMP_MINLEN = 8; /* abs minimum */ + +struct icmp_ra_addr +{ + u_int32_t ira_addr; + u_int32_t ira_preference; +}; + + +struct icmp +{ + u_int8_t icmp_type; /* type of message, see below */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement checksum of struct */ + union + { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* gateway address */ + struct ih_idseq /* echo datagram */ + { + u_int16_t icd_id; + u_int16_t icd_seq; + } ih_idseq; + u_int32_t ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu + { + u_int16_t ipm_void; + u_int16_t ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv + { + u_int8_t irt_num_addrs; + u_int8_t irt_wpa; + u_int16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union + { + struct + { + u_int32_t its_otime; + u_int32_t its_rtime; + u_int32_t its_ttime; + } id_ts; + struct + { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + struct icmp_ra_addr id_radv; + u_int32_t id_mask; + u_int8_t id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +}; +#endif + +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 BB_FEATURE_FANCY_PING +static char *hostname = NULL; + +static void noresp(int ign) +{ + printf("No response from %s\n", hostname); + exit(0); +} + +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)) + 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; + 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) + show_usage(); + ping(*argv); + return EXIT_SUCCESS; +} + +#else /* ! BB_FEATURE_FANCY_PING */ +/* full(er) version */ +static char *hostname = NULL; +static struct sockaddr_in pingaddr; +static int pingsock = -1; +static int datalen; /* intentionally uninitialized to work around gcc bug */ + +static long ntransmitted = 0, nreceived = 0, nrepeats = 0, pingcount = 0; +static int myid = 0, options = 0; +static unsigned long tmin = ULONG_MAX, tmax = 0, tsum = 0; +static char rcvd_tbl[MAX_DUP_CHK / 8]; + +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", hostname); + 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) + perror_msg_and_die("sendto"); + else if ((size_t)i != sizeof(packet)) + 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) + error_msg("Warning: Got ICMP %d (%s)", + icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type)); +} + +static void ping(const char *host) +{ + struct hostent *h; + char buf[MAXHOSTNAMELEN]; + char packet[datalen + MAXIPLEN + MAXICMPLEN]; + int sockopt; + + pingsock = create_icmp_socket(); + + memset(&pingaddr, 0, sizeof(struct sockaddr_in)); + + pingaddr.sin_family = AF_INET; + h = xgethostbyname(host); + if (h->h_addrtype != AF_INET) + error_msg_and_die("unknown address type; only AF_INET is currently supported."); + + memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); + strncpy(buf, h->h_name, sizeof(buf) - 1); + hostname = buf; + + /* 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", + hostname, + 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; + 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) + show_usage(); + argv++; + pingcount = atoi(*argv); + break; + case 's': + if (--argc <= 0) + show_usage(); + argv++; + datalen = atoi(*argv); + break; + default: + show_usage(); + } + argc--; + argv++; + } + if (argc < 1) + show_usage(); + + myid = getpid() & 0xFFFF; + ping(*argv); + return EXIT_SUCCESS; +} +#endif /* ! BB_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. + * + * 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 index 000000000..d571fc5a3 --- /dev/null +++ b/busybox/networking/route.c @@ -0,0 +1,428 @@ +/* route + * + * Similar to the standard Unix route, but with only the necessary + * parts for AF_INET + * + * Bjorn Wesen, Axis Communications AB + * + * Author of the original route: + * Fred N. van Kempen, + * (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.10 2001/03/21 07:34:26 andersen Exp $ + * + * displayroute() code added by Vladimir N. Oleynik + * adjustments by Larry Doolittle + */ + +#include +#include +#include +#include +#include // HZ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define _(x) x + +#define RTACTION_ADD 1 +#define RTACTION_DEL 2 +#define RTACTION_HELP 3 +#define RTACTION_FLUSH 4 +#define RTACTION_SHOW 5 + +#define E_NOTFOUND 8 +#define E_SOCK 7 +#define E_LOOKUP 6 +#define E_VERSION 5 +#define E_USAGE 4 +#define E_OPTERR 3 +#define E_INTERN 2 +#define E_NOSUPP 1 + +/* resolve XXX.YYY.ZZZ.QQQ -> binary */ + +static int +INET_resolve(char *name, struct sockaddr *sa) +{ + struct sockaddr_in *s_in = (struct sockaddr_in *)sa; + + s_in->sin_family = AF_INET; + s_in->sin_port = 0; + + /* Default is special, meaning 0.0.0.0. */ + if (strcmp(name, "default")==0) { + s_in->sin_addr.s_addr = INADDR_ANY; + return 1; + } + /* Look to see if it's a dotted quad. */ + if (inet_aton(name, &s_in->sin_addr)) { + return 0; + } + /* guess not.. */ + return -1; +} + +#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */ +#define HAVE_NEW_ADDRT 1 +#endif +#ifdef RTF_IRTT /* route */ +#define HAVE_RTF_IRTT 1 +#endif +#ifdef RTF_REJECT /* route */ +#define HAVE_RTF_REJECT 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 + +/* add or delete a route depending on action */ + +static int +INET_setroute(int action, int options, char **args) +{ + struct rtentry rt; + char target[128], gateway[128] = "NONE", netmask[128] = "default"; + int xflag, isnet; + int skfd; + + xflag = 0; + + if (strcmp(*args, "-net")==0) { + xflag = 1; + args++; + } else if (strcmp(*args, "-host")==0) { + xflag = 2; + args++; + } + if (*args == NULL) + show_usage(); + + safe_strncpy(target, *args++, (sizeof target)); + + /* Clean out the RTREQ structure. */ + memset((char *) &rt, 0, sizeof(struct rtentry)); + + + if ((isnet = INET_resolve(target, &rt.rt_dst)) < 0) { + error_msg(_("can't resolve %s"), target); + return EXIT_FAILURE; /* XXX change to E_something */ + } + + switch (xflag) { + case 1: + isnet = 1; + break; + + case 2: + isnet = 0; + break; + + default: + break; + } + + /* Fill in the other fields. */ + rt.rt_flags = (RTF_UP | RTF_HOST); + if (isnet) + rt.rt_flags &= ~RTF_HOST; + + while (*args) { + if (strcmp(*args, "metric")==0) { + int metric; + + args++; + if (!*args || !isdigit(**args)) + show_usage(); + metric = atoi(*args); +#if HAVE_NEW_ADDRT + rt.rt_metric = metric + 1; +#else + ENOSUPP("inet_setroute", "NEW_ADDRT (metric)"); /* XXX Fixme */ +#endif + args++; + continue; + } + + if (strcmp(*args, "netmask")==0) { + struct sockaddr mask; + + args++; + if (!*args || mask_in_addr(rt)) + show_usage(); + safe_strncpy(netmask, *args, (sizeof netmask)); + if ((isnet = INET_resolve(netmask, &mask)) < 0) { + error_msg(_("can't resolve netmask %s"), netmask); + return E_LOOKUP; + } + rt.rt_genmask = full_mask(mask); + args++; + continue; + } + + if (strcmp(*args, "gw")==0 || strcmp(*args, "gateway")==0) { + args++; + if (!*args) + show_usage(); + if (rt.rt_flags & RTF_GATEWAY) + show_usage(); + safe_strncpy(gateway, *args, (sizeof gateway)); + if ((isnet = INET_resolve(gateway, &rt.rt_gateway)) < 0) { + error_msg(_("can't resolve gw %s"), gateway); + return E_LOOKUP; + } + if (isnet) { + error_msg( + _("%s: cannot use a NETWORK as gateway!"), + gateway); + return E_OPTERR; + } + rt.rt_flags |= RTF_GATEWAY; + args++; + continue; + } + + if (strcmp(*args, "mss")==0) { + args++; + rt.rt_flags |= RTF_MSS; + if (!*args) + show_usage(); + rt.rt_mss = atoi(*args); + args++; + if (rt.rt_mss < 64 || rt.rt_mss > 32768) { + error_msg(_("Invalid MSS.")); + return E_OPTERR; + } + continue; + } + + if (strcmp(*args, "window")==0) { + args++; + if (!*args) + show_usage(); + rt.rt_flags |= RTF_WINDOW; + rt.rt_window = atoi(*args); + args++; + if (rt.rt_window < 128) { + error_msg(_("Invalid window.")); + return E_OPTERR; + } + continue; + } + + if (strcmp(*args, "irtt")==0) { + args++; + if (!*args) + show_usage(); + args++; +#if HAVE_RTF_IRTT + rt.rt_flags |= RTF_IRTT; + rt.rt_irtt = atoi(*(args - 1)); + rt.rt_irtt *= (HZ / 100); /* FIXME */ +#if 0 /* FIXME: do we need to check anything of this? */ + if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) { + error_msg(_("Invalid initial rtt.")); + return E_OPTERR; + } +#endif +#else + ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */ +#endif + continue; + } + + if (strcmp(*args, "reject")==0) { + args++; +#if HAVE_RTF_REJECT + rt.rt_flags |= RTF_REJECT; +#else + ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */ +#endif + continue; + } + if (strcmp(*args, "mod")==0) { + args++; + rt.rt_flags |= RTF_MODIFIED; + continue; + } + if (strcmp(*args, "dyn")==0) { + args++; + rt.rt_flags |= RTF_DYNAMIC; + continue; + } + if (strcmp(*args, "reinstate")==0) { + args++; + rt.rt_flags |= RTF_REINSTATE; + continue; + } + if (strcmp(*args, "device")==0 || strcmp(*args, "dev")==0) { + args++; + if (rt.rt_dev || *args == NULL) + show_usage(); + rt.rt_dev = *args++; + continue; + } + /* nothing matches */ + if (!rt.rt_dev) { + rt.rt_dev = *args++; + if (*args) + show_usage(); /* must be last to catch typos */ + } else { + show_usage(); + } + } + +#if HAVE_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) { + error_msg( + _("netmask %.8x doesn't make sense with host route"), + (unsigned int)mask); + return E_OPTERR; + } + if (mask & (mask + 1)) { + error_msg(_("bogus netmask %s"), netmask); + return E_OPTERR; + } + mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr; + if (mask & ~mask_in_addr(rt)) { + error_msg(_("netmask doesn't match route address")); + return E_OPTERR; + } + } + /* 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) { + perror("socket"); + return E_SOCK; + } + /* Tell the kernel to accept this route. */ + if (action == RTACTION_DEL) { + if (ioctl(skfd, SIOCDELRT, &rt) < 0) { + perror("SIOCDELRT"); + close(skfd); + return E_SOCK; + } + } else { + if (ioctl(skfd, SIOCADDRT, &rt) < 0) { + perror("SIOCADDRT"); + close(skfd); + return E_SOCK; + } + } + + /* Close the socket. */ + (void) close(skfd); + return EXIT_SUCCESS; +} + +static void displayroutes(void) +{ + char buff[256]; + int nl = 0 ; + struct in_addr dest; + struct in_addr gw; + struct in_addr mask; + int flgs, ref, use, metric; + char flags[4]; + unsigned long int d,g,m; + + char sdest[16], sgw[16]; + + + FILE *fp = xfopen("/proc/net/route", "r"); + + while( fgets(buff, sizeof(buff), fp) != NULL ) { + if(nl) { + int ifl = 0; + while(buff[ifl]!=' ' && buff[ifl]!='\t' && buff[ifl]!='\0') + ifl++; + buff[ifl]=0; /* interface */ + if(sscanf(buff+ifl+1, "%lx%lx%d%d%d%d%lx", + &d, &g, &flgs, &ref, &use, &metric, &m)!=7) { + error_msg_and_die( "Unsuported kernel route format\n"); + } + if(nl==1) { + printf("Kernel IP routing table\n" +"Destination Gateway Genmask Flags Metric Ref Use Iface\n"); + } + + + ifl = 0; /* parse flags */ + if(flgs&1) + flags[ifl++]='U'; + if(flgs&2) + flags[ifl++]='G'; + if(flgs&4) + flags[ifl++]='H'; + flags[ifl]=0; + dest.s_addr = d; + gw.s_addr = g; + mask.s_addr = m; + strcpy(sdest, (dest.s_addr==0 ? "default" : + inet_ntoa(dest))); + strcpy(sgw, (gw.s_addr==0 ? "*" : + inet_ntoa(gw))); + printf("%-16s%-16s%-16s%-6s%-6d %-2d %7d %s\n", + sdest, sgw, + inet_ntoa(mask), + flags, metric, ref, use, buff); + } + nl++; + } +} + +int route_main(int argc, char **argv) +{ + int what = 0; + + argc--; + argv++; + + if (*argv == NULL) { + displayroutes(); + return EXIT_SUCCESS; + } else { + /* check verb */ + if (strcmp(*argv, "add")==0) + what = RTACTION_ADD; + else if (strcmp(*argv, "del")==0 || strcmp(*argv, "delete")==0) + what = RTACTION_DEL; + else if (strcmp(*argv, "flush")==0) + what = RTACTION_FLUSH; + else + show_usage(); + } + + return INET_setroute(what, 0, ++argv); +} diff --git a/busybox/networking/telnet.c b/busybox/networking/telnet.c new file mode 100644 index 000000000..ce82a0ee8 --- /dev/null +++ b/busybox/networking/telnet.c @@ -0,0 +1,711 @@ +/* vi: set sw=4 ts=4: */ +/* + * telnet implementation for busybox + * + * Author: Tomi Ollila + * 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 + * + * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#if 0 +static const int DOTRACE = 1; +#endif + +#ifdef DOTRACE +#include /* 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 +#else +#include +#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) +#else +static struct Globalvars G; +#endif + +static inline void iacflush() +{ + write(G.netfd, G.iacbuf, G.iaclen); + G.iaclen = 0; +} + +/* Function prototypes */ +static int getport(char * p); +static struct in_addr getserver(char * p); +static int create_socket(); +static void setup_sockaddr_in(struct sockaddr_in * addr, int port); +static int remote_connect(struct in_addr addr, int port); +static void rawmode(); +static void cookmode(); +static void do_linemode(); +static void will_charmode(); +static void telopt(byte c); +static int subneg(byte c); +#if 0 +static int local_bind(int port); +#endif + +/* Some globals */ +static int one = 1; + +#ifdef BB_FEATURE_TELNET_TTYPE +static char *ttype; +#endif + +static void doexit(int ev) +{ + cookmode(); + exit(ev); +} + +static void conescape() +{ + 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 */ + + int i; + byte * p = G.buf; + + for (i = len; i > 0; i--, p++) + { + if (*p == 0x1d) + { + conescape(); + return; + } + if (*p == 0xff) + *p = 0x7f; + } + write(G.netfd, G.buf, len); +} + + +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) == TRUE) + 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 BB_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 + +/* void putiacstring (subneg strings) */ + +/* ******************************* */ + +static char const escapecharis[] = "\r\nEscape character is "; + +static void setConMode() +{ + 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() +{ + G.charmode = CHM_TRY; + G.telflags |= (UF_ECHO | UF_SGA); + setConMode(); + + putiac2(DO, TELOPT_ECHO); + putiac2(DO, TELOPT_SGA); + iacflush(); +} + +static void do_linemode() +{ + 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() +{ + /* 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() +{ + /* 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 BB_FEATURE_TELNET_TTYPE +static inline void to_ttype() +{ + /* Tell server we will (or won't) do TTYPE */ + + if(ttype) + putiac2(WILL, TELOPT_TTYPE); + else + putiac2(WONT, TELOPT_TTYPE); + + return; +} +#endif + +static void telopt(byte c) +{ + switch (c) + { + case TELOPT_ECHO: to_echo(c); break; + case TELOPT_SGA: to_sga(c); break; +#ifdef BB_FEATURE_TELNET_TTYPE + case TELOPT_TTYPE: to_ttype(c); break; +#endif + default: to_notsup(c); break; + } +} + + +/* ******************************* */ + +/* subnegotiation -- ignore all (except TTYPE) */ + +static int subneg(byte c) +{ + switch (G.telstate) + { + case TS_SUB1: + if (c == IAC) + G.telstate = TS_SUB2; +#ifdef BB_FEATURE_TELNET_TTYPE + else + if (c == TELOPT_TTYPE) + putiac_subopt(TELOPT_TTYPE,ttype); +#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() +{ + tcsetattr(0, TCSADRAIN, &G.termios_raw); +} + +static void cookmode() +{ + tcsetattr(0, TCSADRAIN, &G.termios_def); +} + +extern int telnet_main(int argc, char** argv) +{ + struct in_addr host; + int port; + int len; +#ifdef USE_POLL + struct pollfd ufds[2]; +#else + fd_set readfds; + int maxfd; +#endif + +#ifdef BB_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) show_usage(); + port = (argc > 2)? getport(argv[2]): 23; + + host = getserver(argv[1]); + + G.netfd = remote_connect(host, port); + + 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); + } + } + } +} + +static int getport(char * p) +{ + unsigned int port = atoi(p); + + if ((unsigned)(port - 1 ) > 65534) + { + error_msg_and_die("%s: bad port number", p); + } + return port; +} + +static struct in_addr getserver(char * host) +{ + struct in_addr addr; + + struct hostent * he; + he = xgethostbyname(host); + memcpy(&addr, he->h_addr, sizeof addr); + + TRACE(1, ("addr: %s\n", inet_ntoa(addr))); + + return addr; +} + +static int create_socket() +{ + return socket(AF_INET, SOCK_STREAM, 0); +} + +static void setup_sockaddr_in(struct sockaddr_in * addr, int port) +{ + memset(addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; + addr->sin_port = htons(port); +} + +#if 0 +static int local_bind(int port) +{ + struct sockaddr_in s_addr; + int s = create_socket(); + + setup_sockaddr_in(&s_addr, port); + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); + + if (bind(s, &s_addr, sizeof s_addr) < 0) + { + char * e = sys_errlist[errno]; + syserrorexit("bind"); + exit(1); + } + listen(s, 1); + + return s; +} +#endif + +static int remote_connect(struct in_addr addr, int port) +{ + struct sockaddr_in s_addr; + int s = create_socket(); + + setup_sockaddr_in(&s_addr, port); + s_addr.sin_addr = addr; + + setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); + + if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0) + { + perror_msg_and_die("Unable to connect to remote host"); + } + return s; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + diff --git a/busybox/networking/tftp.c b/busybox/networking/tftp.c new file mode 100644 index 000000000..bb75c88ec --- /dev/null +++ b/busybox/networking/tftp.c @@ -0,0 +1,414 @@ +/* ------------------------------------------------------------------------- */ +/* tftp.c */ +/* */ +/* A simple tftp client for busybox. */ +/* Tries to follow RFC1350. */ +/* Only "octet" mode and 512-byte data blocks are supported. */ +/* */ +/* Copyright (C) 2001 Magnus Damm */ +/* */ +/* Parts of the code based on: */ +/* */ +/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre */ +/* and Remi Lefebvre */ +/* */ +/* utftp: Copyright (C) 1999 Uwe Ohse */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +//#define BB_FEATURE_TFTP_DEBUG + +/* we don't need #ifdefs with these constants and optimization... */ + +#ifdef BB_FEATURE_TFTP_GET +#define BB_TFTP_GET (1 << 0) +#else +#define BB_TFTP_GET 0 +#endif + +#ifdef BB_FEATURE_TFTP_PUT +#define BB_TFTP_PUT (1 << 1) +#else +#define BB_TFTP_PUT 0 +#endif + +#ifdef BB_FEATURE_TFTP_DEBUG +#define BB_TFTP_DEBUG 1 +#else +#define BB_TFTP_DEBUG 0 +#endif + +#define BB_TFTP_NO_RETRIES 5 +#define BB_TFTP_TIMEOUT 5 /* seconds */ + +#define RRQ 1 /* read request */ +#define WRQ 2 /* write request */ +#define DATA 3 /* data packet */ +#define ACK 4 /* acknowledgement */ +#define ERROR 5 /* error code */ + +#define BUFSIZE (512+4) + +static const char *tftp_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" +}; + +static inline int tftp(int cmd, struct hostent *host, + char *serverfile, int localfd, int port) +{ + struct sockaddr_in sa; + int socketfd; + struct timeval tv; + fd_set rfds; + struct sockaddr_in from; + socklen_t fromlen; + char *cp; + unsigned short tmp; + int len, opcode, finished; + int timeout, block_nr; + + RESERVE_BB_BUFFER(buf, BUFSIZE); + + opcode = finished = timeout = 0; + block_nr = 1; + + if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + 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 = htons(port); + memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, + sizeof(sa.sin_addr)); + + /* build opcode */ + + if (cmd & BB_TFTP_GET) { + opcode = RRQ; + } + + if (cmd & BB_TFTP_PUT) { + opcode = WRQ; + } + + while (1) { + + + /* build packet of type "opcode" */ + + + cp = buf; + + *((unsigned short *) cp) = htons(opcode); + + cp += 2; + + /* add filename and mode */ + + if ((BB_TFTP_GET && (opcode == RRQ)) || + (BB_TFTP_PUT && (opcode == WRQ))) { + + while (cp != &buf[BUFSIZE - 1]) { + if ((*cp = *serverfile++) == '\0') + break; + cp++; + } + + if ((*cp != '\0') || (&buf[BUFSIZE - 1] - cp) < 7) { + error_msg("too long server-filename"); + break; + } + + memcpy(cp + 1, "octet", 6); + cp += 7; + } + + /* add ack and data */ + + if ((BB_TFTP_GET && (opcode == ACK)) || + (BB_TFTP_PUT && (opcode == DATA))) { + + *((unsigned short *) cp) = htons(block_nr); + + cp += 2; + + block_nr++; + + if (BB_TFTP_PUT && (opcode == DATA)) { + len = read(localfd, cp, BUFSIZE - 4); + + if (len < 0) { + perror_msg("read"); + break; + } + + if (len != (BUFSIZE - 4)) { + finished++; + } + + cp += len; + } else if (finished) { + break; + } + } + + + /* send packet */ + + + do { + + len = cp - buf; + + if (BB_TFTP_DEBUG) { + printf("sending %u bytes\n", len); + + for (cp = buf; cp < &buf[len]; cp++) + printf("%02x ", *cp); + printf("\n"); + } + + if (sendto(socketfd, buf, len, 0, + (struct sockaddr *) &sa, sizeof(sa)) < 0) { + perror_msg("send"); + len = -1; + break; + } + + + /* receive packet */ + + + memset(&from, 0, sizeof(from)); + fromlen = sizeof(from); + + tv.tv_sec = BB_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, + BUFSIZE, 0, + (struct sockaddr *) &from, &fromlen); + + if (len < 0) { + perror_msg("recvfrom"); + break; + } + + timeout = 0; + + if (sa.sin_port == htons(port)) { + sa.sin_port = from.sin_port; + break; + } + + if (sa.sin_port == from.sin_port) { + break; + } + + /* fall-through for bad packets! */ + /* discard the packet - treat as timeout */ + + case 0: + error_msg("timeout"); + + if (!timeout) { + timeout = BB_TFTP_NO_RETRIES; + } else { + timeout--; + } + + if (!timeout) { + len = -1; + error_msg("last timeout"); + } + break; + + default: + perror_msg("select"); + len = -1; + } + + } while (timeout && (len >= 0)); + + if (len < 0) { + break; + } + + /* process received packet */ + + + opcode = ntohs(*((unsigned short *) buf)); + tmp = ntohs(*((unsigned short *) &buf[2])); + + if (BB_TFTP_DEBUG) { + printf("received %d bytes: %04x %04x\n", len, opcode, tmp); + } + + if (BB_TFTP_GET && (opcode == DATA)) { + + if (tmp == block_nr) { + len = write(localfd, &buf[4], len - 4); + + if (len < 0) { + perror_msg("write"); + break; + } + + if (len != (BUFSIZE - 4)) { + finished++; + } + + opcode = ACK; + continue; + } + } + + if (BB_TFTP_PUT && (opcode == ACK)) { + + if (tmp == (block_nr - 1)) { + if (finished) { + break; + } + + opcode = DATA; + continue; + } + } + + if (opcode == ERROR) { + char *msg = NULL; + + if (buf[4] != '\0') { + msg = &buf[4]; + buf[BUFSIZE - 1] = '\0'; + } else if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) { + msg = (char *) tftp_error_msg[tmp]; + } + + if (msg) { + error_msg("server says: %s", msg); + } + + break; + } + } + + close(socketfd); + + return finished ? EXIT_SUCCESS : EXIT_FAILURE; +} + +int tftp_main(int argc, char **argv) +{ + char *cp, *s; + char *serverstr; + struct hostent *host; + char *serverfile; + char *localfile; + int cmd, flags, fd, bad; + + host = (void *) serverstr = serverfile = localfile = NULL; + flags = cmd = 0; + bad = 1; + + if (argc > 3) { + if (BB_TFTP_GET && (strcmp(argv[1], "get") == 0)) { + cmd = BB_TFTP_GET; + flags = O_WRONLY | O_CREAT; + serverstr = argv[2]; + localfile = argv[3]; + } + + if (BB_TFTP_PUT && (strcmp(argv[1], "put") == 0)) { + cmd = BB_TFTP_PUT; + flags = O_RDONLY; + localfile = argv[2]; + serverstr = argv[3]; + } + + } + + if (!(cmd & (BB_TFTP_GET | BB_TFTP_PUT))) { + show_usage(); + } + + for (cp = serverstr; *cp != '\0'; cp++) + if (*cp == ':') + break; + + if (*cp == ':') { + + serverfile = cp + 1; + + s = xstrdup(serverstr); + s[cp - serverstr] = '\0'; + + host = xgethostbyname(s); + + free(s); + } + + if (BB_TFTP_DEBUG) { + printf("using server \"%s\", serverfile \"%s\"," + "localfile \"%s\".\n", + inet_ntoa(*((struct in_addr *) host->h_addr)), + serverfile, localfile); + } + + if ((fd = open(localfile, flags, 0644)) < 0) { + perror_msg_and_die("local file"); + } + + flags = tftp(cmd, host, serverfile, fd, 69); + + close(fd); + + return flags; +} diff --git a/busybox/networking/traceroute.c b/busybox/networking/traceroute.c new file mode 100644 index 000000000..a3abd0a00 --- /dev/null +++ b/busybox/networking/traceroute.c @@ -0,0 +1,652 @@ +/*- + * 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 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * 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 BB_FEATURE_TRACEROUTE_VERBOSE +//#define BB_FEATURE_TRACEROUTE_VERBOSE +#undef BB_FEATURE_TRACEROUTE_SO_DEBUG /* not in documentation man */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + /* It turns out that libc5 doesn't have proper icmp support + * built into it header files, so we have to supplement it */ +#if __GNU_LIBRARY__ < 5 +static const int ICMP_MINLEN = 8; /* abs minimum */ + +struct icmp_ra_addr +{ + u_int32_t ira_addr; + u_int32_t ira_preference; +}; + + +struct icmp +{ + u_int8_t icmp_type; /* type of message, see below */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement checksum of struct */ + union + { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* gateway address */ + struct ih_idseq /* echo datagram */ + { + u_int16_t icd_id; + u_int16_t icd_seq; + } ih_idseq; + u_int32_t ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu + { + u_int16_t ipm_void; + u_int16_t ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv + { + u_int8_t irt_num_addrs; + u_int8_t irt_wpa; + u_int16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union + { + struct + { + u_int32_t its_otime; + u_int32_t its_rtime; + u_int32_t its_ttime; + } id_ts; + struct + { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + struct icmp_ra_addr id_radv; + u_int32_t id_mask; + u_int8_t id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +}; + +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_UNREACH 3 /* dest unreachable, codes: */ +#define ICMP_TIMXCEED 11 /* time exceeded, code: */ +#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ +#define ICMP_UNREACH_NET 0 /* bad net */ +#define ICMP_UNREACH_HOST 1 /* bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ +#define ICMP_UNREACH_PORT 3 /* bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ +#endif + + +#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 BB_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; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + const char *ina; + + if (first && !nflag) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = strchr(domain, '.'))) + (void) strcpy(domain, cp + 1); + else + domain[0] = 0; + } + cp = 0; + if (!nflag && from->sin_addr.s_addr != INADDR_ANY) { + hp = gethostbyaddr((char *)&(from->sin_addr), sizeof (from->sin_addr), AF_INET); + if (hp) { + if ((cp = strchr(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = (char *)hp->h_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 BB_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 BB_FEATURE_TRACEROUTE_VERBOSE +/* + * Convert an ICMP "type" field to a printable string. + */ +static inline const char * +pr_type(t) + 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 BB_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 BB_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 BB_TRACEROUTE +main(argc, argv) +#else +traceroute_main(argc, argv) +#endif + int argc; + char *argv[]; +{ + 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 BB_FEATURE_TRACEROUTE_SO_DEBUG + options |= SO_DEBUG; +#endif + break; + case 'm': + max_ttl = atoi(optarg); + if (max_ttl <= 1) + error_msg_and_die("max ttl must be >1."); + break; + case 'n': + nflag++; + break; + case 'p': + port = atoi(optarg); + if (port < 1) + error_msg_and_die("port must be >0."); + break; + case 'q': + nprobes = atoi(optarg); + if (nprobes < 1) + 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) + error_msg_and_die("tos must be 0 to 255."); + break; + case 'v': +#ifdef BB_FEATURE_TRACEROUTE_VERBOSE + verbose++; +#endif + break; + case 'w': + waittime = atoi(optarg); + if (waittime <= 1) + error_msg_and_die("wait must be >1 sec."); + break; + default: + show_usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + 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)) + 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) + perror_msg_and_die(can_not_create_raw_socket); + + s = create_icmp_socket(); + +#ifdef BB_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) + perror_msg_and_die("SO_SNDBUF"); +#endif SO_SNDBUF +#ifdef IP_HDRINCL + if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on, + sizeof(on)) < 0) + perror_msg_and_die("IP_HDRINCL"); +#endif IP_HDRINCL +#ifdef BB_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) + 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) + perror_msg_and_die("bind"); +#endif IP_HDRINCL + } + + 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) + exit(0); + } + + return 0; +} diff --git a/busybox/networking/wget.c b/busybox/networking/wget.c new file mode 100644 index 000000000..59373d1d9 --- /dev/null +++ b/busybox/networking/wget.c @@ -0,0 +1,834 @@ +/* vi: set sw=4 ts=4: */ +/* + * wget - retrieve a file using HTTP or FTP + * + * Chip Rosenthal Covad Communications + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include + +#include "busybox.h" + +/* Stupid libc5 doesn't define this... */ +#ifndef timersub +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +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(char *host, int port); +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 BB_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); \ + error_msg_and_die(s); } + + +#ifdef BB_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(char *p, char *buf, int len) { + + char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + char *s = buf; + + while(*p) { + if (s >= buf+len-4) + 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 + +int wget_main(int argc, char **argv) +{ + int n, try=5, status; + int port; + char *proxy; + 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); + int which_long_opt = 0, option_index = -1; + struct host_info server, target; + + 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... */ + +#define LONG_HEADER 1 + struct option long_options[] = { + { "continue", 0, NULL, 'c' }, + { "quiet", 0, NULL, 'q' }, + { "output-document", 1, NULL, 'O' }, + { "header", 1, &which_long_opt, LONG_HEADER }, + { 0, 0, 0, 0 } + }; + /* + * Crack command line. + */ + while ((n = getopt_long(argc, argv, "cqO:P:", long_options, &option_index)) != EOF) { + switch (n) { + case 'c': + ++do_continue; + break; + case 'P': + dir_prefix = optarg; + break; + case 'q': + quiet_flag = TRUE; + break; + case 'O': + /* can't set fname_out to NULL if outputting to stdout, because + * this gets interpreted as the auto-gen output filename + * case below - tausq@debian.org + */ + fname_out = optarg; + break; + case 0: + switch (which_long_opt) { + case LONG_HEADER: { + int arglen = strlen(optarg); + if(extra_headers_left - arglen - 2 <= 0) + error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen); + strcpy(extra_headers_ptr, optarg); + extra_headers_ptr += arglen; + extra_headers_left -= ( arglen + 2 ); + *extra_headers_ptr++ = '\r'; + *extra_headers_ptr++ = '\n'; + *(extra_headers_ptr + 1) = 0; + break; + } + } + break; + default: + show_usage(); + } + } + + if (argc - optind != 1) + show_usage(); + + parse_url(argv[optind], &target); + server.host = target.host; + server.port = target.port; + + /* + * Use the proxy if necessary. + */ + proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); + if (proxy) + parse_url(xstrdup(proxy), &server); + + /* Guess an output filename */ + if (!fname_out) { + fname_out = +#ifdef BB_FEATURE_WGET_STATUSBAR + curfile = +#endif + get_last_path_component(target.path); + if (fname_out==NULL || strlen(fname_out)<1) { + fname_out = +#ifdef BB_FEATURE_WGET_STATUSBAR + curfile = +#endif + "index.html"; + } + if (dir_prefix != NULL) + fname_out = concat_path_file(dir_prefix, fname_out); +#ifdef BB_FEATURE_WGET_STATUSBAR + } else { + curfile = get_last_path_component(fname_out); +#endif + } + if (do_continue && !fname_out) + 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 = xfopen(fname_out, (do_continue ? "a" : "w")); + } + + /* + * Determine where to start transfer. + */ + if (do_continue) { + if (fstat(fileno(output), &sbuf) < 0) + perror_msg_and_die("fstat()"); + if (sbuf.st_size > 0) + beg_range = sbuf.st_size; + else + do_continue = 0; + } + + if (proxy || !target.is_ftp) { + /* + * HTTP session + */ + do { + if (! --try) + close_delete_and_die("too many redirections"); + + /* + * Open socket to http server + */ + if (sfp) fclose(sfp); + sfp = open_socket(server.host, server.port); + + /* + * Send HTTP request. + */ + if (proxy) { + fprintf(sfp, "GET %stp://%s:%d/%s HTTP/1.1\r\n", + target.is_ftp ? "f" : "ht", target.host, + 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 BB_FEATURE_WGET_AUTHENTICATION + if (target.user) { + fprintf(sfp, "Authorization: Basic %s\r\n", + base64enc(target.user, buf, sizeof(buf))); + } + if (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) { + filesize = atol(s); + 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 = xstrdup(s+1); + else { + parse_url(xstrdup(s), &target); + if (!proxy) { + server.host = target.host; + server.port = target.port; + } + } + } + } + } while(status >= 300); + + dfp = sfp; + } + else + { + /* + * FTP session + */ + if (! target.user) + target.user = xstrdup("anonymous:busybox@"); + + sfp = open_socket(server.host, server.port); + 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) { + filesize = atol(buf+4); + 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; + dfp = open_socket(server.host, port); + + 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 BB_FEATURE_WGET_STATUSBAR + if (quiet_flag==FALSE) + progressmeter(-1); +#endif + do { + while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, chunked ? (filesize > sizeof(buf) ? sizeof(buf) : filesize) : sizeof(buf), dfp)) > 0) { + safe_fwrite(buf, 1, n, output); +#ifdef BB_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)) + perror_msg_and_die("network read error"); + } while (chunked); +#ifdef BB_FEATURE_WGET_STATUSBAR + if (quiet_flag==FALSE) + progressmeter(1); +#endif + if (!proxy && target.is_ftp) { + fclose(dfp); + if (ftpcmd(NULL, NULL, sfp, buf) != 226) + 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; + + if (strncmp(url, "http://", 7) == 0) { + h->port = 80; + h->host = url + 7; + h->is_ftp = 0; + } else if (strncmp(url, "ftp://", 6) == 0) { + h->port = 21; + h->host = url + 6; + h->is_ftp = 1; + } else + error_msg_and_die("not an http or ftp url: %s", url); + + sp = strchr(h->host, '/'); + if (sp != NULL) { + *sp++ = '\0'; + h->path = sp; + } else + h->path = ""; + + up = strrchr(h->host, '@'); + if (up != NULL) { + h->user = h->host; + *up++ = '\0'; + h->host = up; + } else + h->user = NULL; + + cp = strchr(h->host, ':'); + if (cp != NULL) { + *cp++ = '\0'; + h->port = atoi(cp); + } + +} + + +FILE *open_socket(char *host, int port) +{ + struct sockaddr_in s_in; + struct hostent *hp; + int fd; + FILE *fp; + + memset(&s_in, 0, sizeof(s_in)); + s_in.sin_family = AF_INET; + hp = xgethostbyname(host); + memcpy(&s_in.sin_addr, hp->h_addr_list[0], hp->h_length); + s_in.sin_port = htons(port); + + /* + * Get the server onto a stdio stream. + */ + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + perror_msg_and_die("socket()"); + if (connect(fd, (struct sockaddr *) &s_in, sizeof(s_in)) < 0) + perror_msg_and_die("connect(%s)", host); + if ((fp = fdopen(fd, "r+")) == NULL) + 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 != ':') + 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) +{ + char *p; + + if (s1) { + if (!s2) s2=""; + fprintf(fp, "%s%s\n", s1, s2); + fflush(fp); + } + + do { + p = fgets(buf, 510, fp); + if (!p) + perror_msg_and_die("fgets()"); + } while (! isdigit(buf[0]) || buf[3] != ' '); + + return atoi(buf); +} + +#ifdef BB_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) +{ + struct winsize winsize; + + if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) + return (winsize.ws_col ? winsize.ws_col : 80); + else + return (80); +} + +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(fileno(stderr), 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 BB_FEATURE_WGET_STATUSBAR stuff, + * much of which was blatently 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. + * + * 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.45 2001/07/19 22:28:01 andersen Exp $ + */ + + + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + + + diff --git a/busybox/nfsmount.c b/busybox/nfsmount.c new file mode 100644 index 000000000..cd722acc3 --- /dev/null +++ b/busybox/nfsmount.c @@ -0,0 +1,977 @@ +/* vi: set sw=4 ts=4: */ +/* + * nfsmount.c -- Linux NFS mount + * Copyright (C) 1993 Rick Sladkey + * + * This program is free software; you can redistribute 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 : + * Omit the call to connect() for Linux version 1.3.11 or later. + * + * Wed Oct 1 23:55:28 1997: Dick Streefland + * Implemented the "bg", "fg" and "retry" mount options for NFS. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz + * - 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#undef TRUE +#undef FALSE +#include +#include +#include +#include /* For the kernels nfs stuff */ +#include "nfsmount.h" + +#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; + static 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)) { + 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'; + error_msg("warning: multiple hostnames not supported"); + } + } else { + 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) { + herror_msg("%s", hostname); + goto fail; + } else { + if (hp->h_length > sizeof(struct in_addr)) { + 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)) { + error_msg("excessively long option argument"); + goto fail; + } + sprintf(new_opts, "%s%saddr=%s", + old_opts, *old_opts ? "," : "", s); + *extra_opts = 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=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) { + error_msg("NFSv%d not supported!", nfsvers); + return 0; + } + if (mountvers > MAX_NFSPROT) { + 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; + *mount_opts = (char *) &data; + + if (*flags & MS_REMOUNT) + return 0; + + /* + * 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) { + herror_msg("%s", mounthost); + goto fail; + } else { + if (hp->h_length > sizeof(struct in_addr)) { + 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 = 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) { + 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) { + 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); + 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 : change errno: + * "after #include 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, (u_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, (u_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/nfsmount.h b/busybox/nfsmount.h new file mode 100644 index 000000000..b3d5a51e6 --- /dev/null +++ b/busybox/nfsmount.h @@ -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 + + +#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 +#define MOUNTPORT 635 +#define MNTPATHLEN 1024 +#define MNTNAMLEN 255 +#define FHSIZE 32 +#define FHSIZE3 64 + +typedef char fhandle[FHSIZE]; + +typedef struct { + u_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 { + u_int fhs_status; + union { + fhandle fhs_fhandle; + } fhstatus_u; +}; +typedef struct fhstatus fhstatus; + +struct mountres3_ok { + fhandle3 fhandle; + struct { + u_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/nslookup.c b/busybox/nslookup.c new file mode 100644 index 000000000..9b7cb645c --- /dev/null +++ b/busybox/nslookup.c @@ -0,0 +1,183 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini nslookup implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* + | I'm only implementing non-interactive mode; + | I totally forgot nslookup even had an interactive mode. + | + | [ TODO ] + | + find out how to use non-default name servers + */ + +/* only works for IPv4 */ +static int addr_fprint(char *addr) +{ + u_int8_t split[4]; + u_int32_t ip; + u_int32_t *x = (u_int32_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) +{ + if (host) { + printf("Name: %s\n", 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 u_int32_t + */ +static u_int32_t str_to_addr(const char *addr) +{ + u_int32_t split[4]; + u_int32_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 */ +} + +#ifdef __UCLIBC__ +#warning FIXME after fixing uClibc to define struct _res +static inline void server_print(void) +{ + printf("Server: %s\n", "default"); + printf("Address: %s\n\n", "default"); +} +#else +/* 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)); + printf("\n"); +} +#endif + +/* 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; + + if (argc < 2 || *argv[1]=='-') { + show_usage(); + } + + res_init(); + server_print(); + if (is_ip_address(argv[1])) { + host = gethostbyaddr_wrapper(argv[1]); + } else { + host = gethostbyname(argv[1]); + } + hostent_fprint(host); + return EXIT_SUCCESS; +} + +/* $Id: nslookup.c,v 1.24 2001/07/06 17:51:29 andersen Exp $ */ diff --git a/busybox/pidof.c b/busybox/pidof.c new file mode 100644 index 000000000..50dffd387 --- /dev/null +++ b/busybox/pidof.c @@ -0,0 +1,79 @@ +/* vi: set sw=4 ts=4: */ +/* + * pidof implementation for busybox + * + * Copyright (C) 2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +extern int pidof_main(int argc, char **argv) +{ + int opt; + + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "ne:f:")) > 0) { + switch (opt) { +#if 0 + case 'g': + break; + case 'e': + break; +#endif + default: + show_usage(); + } + } + + /* if we didn't get a process name, then we need to choke and die here */ + if (argv[optind] == NULL) + show_usage(); + + /* Looks like everything is set to go. */ + while(optind < argc) { + pid_t* pidList; + + pidList = find_pid_by_name( argv[optind]); + if (!pidList || *pidList<=0) { + break; + } + + for(; pidList && *pidList!=0; pidList++) { + printf("%ld ", (long)*pidList); + } + /* Note that we don't bother to free the memory + * allocated in find_pid_by_name(). It will be freed + * upon exit, so we can save a byte or two */ + optind++; + } + printf("\n"); + + return EXIT_SUCCESS; +} diff --git a/busybox/ping.c b/busybox/ping.c new file mode 100644 index 000000000..5ca5dd9e0 --- /dev/null +++ b/busybox/ping.c @@ -0,0 +1,555 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: ping.c,v 1.46 2001/07/17 01:12:36 andersen Exp $ + * Mini ping implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +/* It turns out that libc5 doesn't have proper icmp support + * built into it header files, so we have to supplement it */ +#if __GNU_LIBRARY__ < 5 +static const int ICMP_MINLEN = 8; /* abs minimum */ + +struct icmp_ra_addr +{ + u_int32_t ira_addr; + u_int32_t ira_preference; +}; + + +struct icmp +{ + u_int8_t icmp_type; /* type of message, see below */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement checksum of struct */ + union + { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* gateway address */ + struct ih_idseq /* echo datagram */ + { + u_int16_t icd_id; + u_int16_t icd_seq; + } ih_idseq; + u_int32_t ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu + { + u_int16_t ipm_void; + u_int16_t ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv + { + u_int8_t irt_num_addrs; + u_int8_t irt_wpa; + u_int16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union + { + struct + { + u_int32_t its_otime; + u_int32_t its_rtime; + u_int32_t its_ttime; + } id_ts; + struct + { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + struct icmp_ra_addr id_radv; + u_int32_t id_mask; + u_int8_t id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +}; +#endif + +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 BB_FEATURE_FANCY_PING +static char *hostname = NULL; + +static void noresp(int ign) +{ + printf("No response from %s\n", hostname); + exit(0); +} + +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)) + 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; + 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) + show_usage(); + ping(*argv); + return EXIT_SUCCESS; +} + +#else /* ! BB_FEATURE_FANCY_PING */ +/* full(er) version */ +static char *hostname = NULL; +static struct sockaddr_in pingaddr; +static int pingsock = -1; +static int datalen; /* intentionally uninitialized to work around gcc bug */ + +static long ntransmitted = 0, nreceived = 0, nrepeats = 0, pingcount = 0; +static int myid = 0, options = 0; +static unsigned long tmin = ULONG_MAX, tmax = 0, tsum = 0; +static char rcvd_tbl[MAX_DUP_CHK / 8]; + +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", hostname); + 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) + perror_msg_and_die("sendto"); + else if ((size_t)i != sizeof(packet)) + 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) + error_msg("Warning: Got ICMP %d (%s)", + icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type)); +} + +static void ping(const char *host) +{ + struct hostent *h; + char buf[MAXHOSTNAMELEN]; + char packet[datalen + MAXIPLEN + MAXICMPLEN]; + int sockopt; + + pingsock = create_icmp_socket(); + + memset(&pingaddr, 0, sizeof(struct sockaddr_in)); + + pingaddr.sin_family = AF_INET; + h = xgethostbyname(host); + if (h->h_addrtype != AF_INET) + error_msg_and_die("unknown address type; only AF_INET is currently supported."); + + memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); + strncpy(buf, h->h_name, sizeof(buf) - 1); + hostname = buf; + + /* 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", + hostname, + 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; + 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) + show_usage(); + argv++; + pingcount = atoi(*argv); + break; + case 's': + if (--argc <= 0) + show_usage(); + argv++; + datalen = atoi(*argv); + break; + default: + show_usage(); + } + argc--; + argv++; + } + if (argc < 1) + show_usage(); + + myid = getpid() & 0xFFFF; + ping(*argv); + return EXIT_SUCCESS; +} +#endif /* ! BB_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. + * + * 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/pivot_root.c b/busybox/pivot_root.c new file mode 100644 index 000000000..ba26b9c58 --- /dev/null +++ b/busybox/pivot_root.c @@ -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 +#include +#include +#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) + show_usage(); + + if (pivot_root(argv[1],argv[2]) < 0) + 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/poweroff.c b/busybox/poweroff.c new file mode 100644 index 000000000..8bb20e9bb --- /dev/null +++ b/busybox/poweroff.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini poweroff implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +extern int poweroff_main(int argc, char **argv) +{ +#ifdef BB_FEATURE_LINUXRC + /* don't assume init's pid == 1 */ + pid_t *pid = find_pid_by_name("init"); + if (!pid || *pid<=0) + error_msg_and_die("no process killed"); + return(kill(*pid, SIGUSR2)); +#else + return(kill(1, SIGUSR2)); +#endif +} diff --git a/busybox/printf.c b/busybox/printf.c new file mode 100644 index 000000000..d579a9b4e --- /dev/null +++ b/busybox/printf.c @@ -0,0 +1,455 @@ +/* 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 */ + + +// 19990508 Busy Boxed! Dave Cinege + +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +#ifndef S_IFMT +static const int S_IFMT = 0170000; +#endif +#if !defined(S_ISBLK) && defined(S_IFBLK) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +#define IN_CTYPE_DOMAIN(c) 1 + +#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) +#define ISDIGIT(c) (((unsigned char) (c)) - '0' <= 9) + +#define isodigit(c) ((c) >= '0' && (c) <= '7') +#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0') +#define octtobin(c) ((c) - '0') + +static double xstrtod __P((char *s)); +static int print_esc __P((char *escstart)); +static int print_formatted __P((char *format, int argc, char **argv)); +static long xstrtol __P((char *s)); +static unsigned long xstrtoul __P((char *s)); +static void print_direc +__P( + + (char *start, size_t length, int field_width, int precision, + char *argument)); +static void print_esc_char __P((int c)); +static void print_esc_string __P((char *str)); +static void verify __P((char *s, char *end)); + +/* The value to return to the calling program. */ +static int exit_status; + +int printf_main(int argc, char **argv) +{ + char *format; + int args_used; + + exit_status = 0; + if (argc <= 1 || **(argv + 1) == '-') { + 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_status); +} + +/* 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 '\\': + f += print_esc(f); + break; + + default: + putchar(*f); + } + } + + return save_argc - argc; +} + +/* Print a \ escape sequence starting at ESCSTART. + Return the number of characters in the escape sequence + besides the backslash. */ + +static int print_esc(char *escstart) +{ + register char *p = escstart + 1; + int esc_value = 0; /* Value of \nnn escape. */ + int esc_length; /* Length of \nnn escape. */ + + /* \0ooo and \xhhh escapes have maximum length of 3 chars. */ + if (*p == 'x') { + for (esc_length = 0, ++p; + esc_length < 3 && ISXDIGIT(*p); ++esc_length, ++p) + esc_value = esc_value * 16 + hextobin(*p); +/* if (esc_length == 0) + fprintf(stderr, "missing hex in esc"); +*/ + putchar(esc_value); + } else if (*p == '0') { + for (esc_length = 0, ++p; + esc_length < 3 && isodigit(*p); ++esc_length, ++p) + esc_value = esc_value * 8 + octtobin(*p); + putchar(esc_value); + } else if (strchr("\"\\abcfnrtv", *p)) + print_esc_char(*p++); +/* else + fprintf(stderr, "\\%c: invalid esc", *p); +*/ + return p - escstart - 1; +} + +/* Output a single-character \ escape. */ + +static void print_esc_char(int c) +{ + switch (c) { + case 'a': /* Alert. */ + putchar(7); + break; + case 'b': /* Backspace. */ + putchar(8); + break; + case 'c': /* Cancel the rest of the output. */ + exit(0); + break; + case 'f': /* Form feed. */ + putchar(12); + break; + case 'n': /* New line. */ + putchar(10); + break; + case 'r': /* Carriage return. */ + putchar(13); + break; + case 't': /* Horizontal tab. */ + putchar(9); + break; + case 'v': /* Vertical tab. */ + putchar(11); + break; + default: + putchar(c); + break; + } +} + +/* Print string STR, evaluating \ escapes. */ + +static void print_esc_string(char *str) +{ + for (; *str; str++) + if (*str == '\\') + str += print_esc(str); + else + putchar(*str); +} + +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 *s) +{ + char *end; + unsigned long val; + + errno = 0; + val = strtoul(s, &end, 0); + verify(s, end); + return val; +} + +static long xstrtol(char *s) +{ + char *end; + long val; + + errno = 0; + val = strtol(s, &end, 0); + verify(s, end); + return val; +} + +static double xstrtod(char *s) +{ + char *end; + double val; + + errno = 0; + val = strtod(s, &end); + verify(s, end); + return val; +} + +static void verify(char *s, char *end) +{ + if (errno) { + fprintf(stderr, "%s", s); + exit_status = 1; + } else if (*end) { + /* + if (s == end) + fprintf(stderr, "%s: expected numeric", s); + else + fprintf(stderr, "%s: not completely converted", s); + */ + exit_status = 1; + } +} diff --git a/busybox/pristine_setup.sh b/busybox/pristine_setup.sh new file mode 100755 index 000000000..9e638f96e --- /dev/null +++ b/busybox/pristine_setup.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# +# To compile BusyBox without touching the original sources +# (as might be interesting for multi-target builds), create +# an empty directory, cd into it, and run this program by +# giving its explicit path (kind of like how you would run +# configure, if BusyBox had one). Then you should be ready +# to "make". Files in the build tree, in particular Config.h, +# will override those in the pristine source tree. +# +# If you use a ? in your path name, you lose, see sed command below. + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +DIR=${0%%/pristine_setup.sh} +if [ ! -d $DIR ]; then + echo "unexpected problem: $DIR is not a directory. Aborting pristine setup" + exit +fi + +echo " " + +if [ -e ./Config.h ]; then + echo "./Config.h already exists: not overwriting" + exit +fi + +if [ -e ./Makefile ]; then + echo "./Makefile already exists: not overwriting" +fi + +sed -e "s?BB_SRC_DIR =.*?BB_SRC_DIR = $DIR?" <$DIR/Makefile >Makefile || exit +cp $DIR/Config.h Config.h || exit +#mkdir -p pwd_grp + +if [ ! -r $DIR/sh.c ]; then + echo "Warning: no shell selected. You must make the symlink (sh.c to either" + echo "lash.c or hush.c) in $DIR, not here." +fi + +echo " " +echo "You may now type 'make' to build busybox in this directory" +echo "($PWD) using the pristine sources in $DIR" +echo " " + diff --git a/busybox/procps/free.c b/busybox/procps/free.c new file mode 100644 index 000000000..2e34a972c --- /dev/null +++ b/busybox/procps/free.c @@ -0,0 +1,69 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini free implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ + +#include +#include +#include +#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; + } + 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; + info.totalswap/=info.mem_unit; + info.freeswap/=info.mem_unit; + info.sharedram/=info.mem_unit; + info.bufferram/=info.mem_unit; + + if (argc > 1 && **(argv + 1) == '-') + 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); + + 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); + return EXIT_SUCCESS; +} + + diff --git a/busybox/procps/kill.c b/busybox/procps/kill.c new file mode 100644 index 000000000..3884ebdf4 --- /dev/null +++ b/busybox/procps/kill.c @@ -0,0 +1,142 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini kill/killall implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +static const int KILL = 0; +static const int KILLALL = 1; + + +extern int kill_main(int argc, char **argv) +{ + int whichApp, sig = SIGTERM; + const char *name; + +#ifdef BB_KILLALL + /* Figure out what we are trying to do here */ + whichApp = (strcmp(applet_name, "killall") == 0)? KILLALL : KILL; +#else + whichApp = KILL; +#endif + + argc--; + argv++; + /* Parse any options */ + if (argc < 1) + show_usage(); + + while (argc > 0 && **argv == '-') { + while (*++(*argv)) { + switch (**argv) { + case 'l': + if(argc>1) { + for(argv++; *argv; argv++) { + name = u_signal_names(*argv, &sig, -1); + if(name!=NULL) + printf("%s\n", name); + } + } else { + int col = 0; + for(sig=1; sig < NSIG; sig++) { + name = u_signal_names(0, &sig, 1); + if(name==NULL) /* unnamed */ + continue; + col += printf("%2d) %-16s", sig, name); + if (col > 60) { + printf("\n"); + col = 0; + } + } + printf("\n"); + } + return EXIT_SUCCESS; + case '-': + show_usage(); + default: + name = u_signal_names(*argv, &sig, 0); + if(name==NULL) + error_msg_and_die( "bad signal name: %s", *argv); + argc--; + argv++; + goto do_it_now; + } + argc--; + argv++; + } + } + + do_it_now: + + if (whichApp == KILL) { + /* Looks like they want to do a kill. Do that */ + while (--argc >= 0) { + int pid; + + if (!isdigit(**argv)) + perror_msg_and_die( "Bad PID"); + pid = strtol(*argv, NULL, 0); + if (kill(pid, sig) != 0) + perror_msg_and_die( "Could not kill pid '%d'", pid); + argv++; + } + } +#ifdef BB_KILLALL + else { + int all_found = TRUE; + pid_t myPid=getpid(); + /* Looks like they want to do a killall. Do that */ + while (--argc >= 0) { + pid_t* pidList; + + pidList = find_pid_by_name( *argv); + if (!pidList || *pidList<=0) { + all_found = FALSE; + error_msg_and_die( "%s: no process killed", *argv); + } + + for(; pidList && *pidList!=0; pidList++) { + if (*pidList==myPid) + continue; + if (kill(*pidList, sig) != 0) + perror_msg_and_die( "Could not kill pid '%d'", *pidList); + } + /* Note that we don't bother to free the memory + * allocated in find_pid_by_name(). It will be freed + * upon exit, so we can save a byte or two */ + argv++; + } + if (all_found == FALSE) + return EXIT_FAILURE; + } +#endif + + return EXIT_SUCCESS; +} diff --git a/busybox/procps/pidof.c b/busybox/procps/pidof.c new file mode 100644 index 000000000..50dffd387 --- /dev/null +++ b/busybox/procps/pidof.c @@ -0,0 +1,79 @@ +/* vi: set sw=4 ts=4: */ +/* + * pidof implementation for busybox + * + * Copyright (C) 2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +extern int pidof_main(int argc, char **argv) +{ + int opt; + + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "ne:f:")) > 0) { + switch (opt) { +#if 0 + case 'g': + break; + case 'e': + break; +#endif + default: + show_usage(); + } + } + + /* if we didn't get a process name, then we need to choke and die here */ + if (argv[optind] == NULL) + show_usage(); + + /* Looks like everything is set to go. */ + while(optind < argc) { + pid_t* pidList; + + pidList = find_pid_by_name( argv[optind]); + if (!pidList || *pidList<=0) { + break; + } + + for(; pidList && *pidList!=0; pidList++) { + printf("%ld ", (long)*pidList); + } + /* Note that we don't bother to free the memory + * allocated in find_pid_by_name(). It will be freed + * upon exit, so we can save a byte or two */ + optind++; + } + printf("\n"); + + return EXIT_SUCCESS; +} diff --git a/busybox/procps/ps.c b/busybox/procps/ps.c new file mode 100644 index 000000000..9e96a5402 --- /dev/null +++ b/busybox/procps/ps.c @@ -0,0 +1,266 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ps implementation(s) for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * + * This contains _two_ implementations of ps for Linux. One uses the + * traditional /proc virtual filesystem, and the other use the devps kernel + * driver (written by Erik Andersen to avoid using /proc thereby saving 100k+). + * + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */ + + + +#if ! defined BB_FEATURE_USE_DEVPS_PATCH + +/* The following is the first ps implementation -- + * the one using the /proc virtual filesystem. + */ + +typedef struct proc_s { + char + cmd[16]; /* basename of executable file in call to exec(2) */ + int + ruid, /* real only (sorry) */ + pid, /* process id */ + ppid; /* pid of parent process */ + char + state; /* single-char code for process state (S=sleeping) */ +} proc_t; + + + +static int file2str(char *filename, char *ret, int cap) +{ + int fd, num_read; + + if ((fd = open(filename, O_RDONLY, 0)) == -1) + return -1; + if ((num_read = read(fd, ret, cap - 1)) <= 0) + return -1; + ret[num_read] = 0; + close(fd); + return num_read; +} + + +static void parse_proc_status(char *S, proc_t * P) +{ + char *tmp; + + memset(P->cmd, 0, sizeof P->cmd); + sscanf(S, "Name:\t%15c", P->cmd); + tmp = strchr(P->cmd, '\n'); + if (tmp) + *tmp = '\0'; + tmp = strstr(S, "State"); + sscanf(tmp, "State:\t%c", &P->state); + + tmp = strstr(S, "Pid:"); + if (tmp) + sscanf(tmp, "Pid:\t%d\n" "PPid:\t%d\n", &P->pid, &P->ppid); + else + error_msg("Internal error!"); + + /* For busybox, ignoring effective, saved, etc. */ + tmp = strstr(S, "Uid:"); + if (tmp) + sscanf(tmp, "Uid:\t%d", &P->ruid); + else + error_msg("Internal error!"); + + +} + +extern int ps_main(int argc, char **argv) +{ + proc_t p; + DIR *dir; + FILE *file; + struct dirent *entry; + char path[32], sbuf[512]; + char uidName[9]; + int len, i, c; +#ifdef BB_FEATURE_AUTOWIDTH + struct winsize win = { 0, 0, 0, 0 }; + int terminal_width = TERMINAL_WIDTH; +#else +#define terminal_width TERMINAL_WIDTH +#endif + + + + dir = opendir("/proc"); + if (!dir) + error_msg_and_die("Can't open /proc"); + +#ifdef BB_FEATURE_AUTOWIDTH + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (win.ws_col > 0) + terminal_width = win.ws_col - 1; +#endif + + printf(" PID Uid Stat Command\n"); + while ((entry = readdir(dir)) != NULL) { + if (!isdigit(*entry->d_name)) + continue; + sprintf(path, "/proc/%s/status", entry->d_name); + if ((file2str(path, sbuf, sizeof sbuf)) != -1) { + parse_proc_status(sbuf, &p); + } + + /* Make some adjustments as needed */ + my_getpwuid(uidName, p.ruid); + if (*uidName == '\0') + sprintf(uidName, "%d", p.ruid); + + sprintf(path, "/proc/%s/cmdline", entry->d_name); + file = fopen(path, "r"); + if (file == NULL) + continue; + i = 0; + len = printf("%5d %-8s %c ", p.pid, uidName, p.state); + while (((c = getc(file)) != EOF) && (i < (terminal_width-len))) { + i++; + if (c == '\0') + c = ' '; + putc(c, stdout); + } + fclose(file); + if (i == 0) + printf("[%s]", p.cmd); + putchar('\n'); + } + closedir(dir); + return EXIT_SUCCESS; +} + + +#else /* BB_FEATURE_USE_DEVPS_PATCH */ + + +/* The following is the second ps implementation -- + * this one uses the nifty new devps kernel device. + */ + +#include /* For Erik's nifty devps device driver */ + + +extern int ps_main(int argc, char **argv) +{ + char device[] = "/dev/ps"; + int i, j, len, fd; + pid_t num_pids; + pid_t* pid_array = NULL; + struct pid_info info; + char uidName[9]; +#ifdef BB_FEATURE_AUTOWIDTH + struct winsize win = { 0, 0, 0, 0 }; + int terminal_width = TERMINAL_WIDTH; +#else +#define terminal_width TERMINAL_WIDTH +#endif + + if (argc > 1 && **(argv + 1) == '-') + show_usage(); + + /* open device */ + fd = open(device, O_RDONLY); + if (fd < 0) + perror_msg_and_die( "open failed for `%s'", device); + + /* Find out how many processes there are */ + if (ioctl (fd, DEVPS_GET_NUM_PIDS, &num_pids)<0) + perror_msg_and_die( "\nDEVPS_GET_PID_LIST"); + + /* Allocate some memory -- grab a few extras just in case + * some new processes start up while we wait. The kernel will + * just ignore any extras if we give it too many, and will trunc. + * the list if we give it too few. */ + pid_array = (pid_t*) xcalloc( num_pids+10, sizeof(pid_t)); + pid_array[0] = num_pids+10; + + /* Now grab the pid list */ + if (ioctl (fd, DEVPS_GET_PID_LIST, pid_array)<0) + perror_msg_and_die("\nDEVPS_GET_PID_LIST"); + +#ifdef BB_FEATURE_AUTOWIDTH + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (win.ws_col > 0) + terminal_width = win.ws_col - 1; +#endif + + /* Print up a ps listing */ + printf(" PID Uid Stat Command\n"); + + for (i=1; i 1) { + for( j=0; j<(sizeof(info.command_line)-1) && j < (terminal_width-len); j++) { + if (*(info.command_line+j) == '\0' && *(info.command_line+j+1) != '\0') { + *(info.command_line+j) = ' '; + } + } + *(info.command_line+j) = '\0'; + puts(info.command_line); + } else { + printf("[%s]\n", info.name); + } + } + + /* Free memory */ + free( pid_array); + + /* close device */ + if (close (fd) != 0) + perror_msg_and_die("close failed for `%s'", device); + + exit (0); +} + +#endif /* BB_FEATURE_USE_DEVPS_PATCH */ + diff --git a/busybox/procps/renice.c b/busybox/procps/renice.c new file mode 100644 index 000000000..ec35bdcde --- /dev/null +++ b/busybox/procps/renice.c @@ -0,0 +1,54 @@ +/* + * Mini renice implementation for busybox + * + * + * Copyright (C) 2000 Dave 'Kill a Cop' Cinege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "busybox.h" + + +extern int renice_main(int argc, char **argv) +{ + int prio, status = EXIT_SUCCESS; + + if (argc < 3) 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 { + perror_msg("%d: setpriority", ps); + status = EXIT_FAILURE; + } + } + + return status; +} diff --git a/busybox/procps/uptime.c b/busybox/procps/uptime.c new file mode 100644 index 000000000..6758d959e --- /dev/null +++ b/busybox/procps/uptime.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini uptime implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* 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 BB_FEATURE_UTMP_SUPPORT #define + */ + +/* getopt not needed */ + + +#include +#include +#include +#include +#include "busybox.h" + +static const int FSHIFT = 16; /* nr of bits of precision */ +#define FIXED_1 (1<> 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(¤t_secs); + current_time = localtime(¤t_secs); + + sysinfo(&info); + + printf(" %2d:%02d%s up ", + current_time->tm_hour%12 ? current_time->tm_hour%12 : 12, + current_time->tm_min, current_time->tm_hour > 11 ? "pm" : "am"); + 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/ps.c b/busybox/ps.c new file mode 100644 index 000000000..9e96a5402 --- /dev/null +++ b/busybox/ps.c @@ -0,0 +1,266 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ps implementation(s) for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * + * This contains _two_ implementations of ps for Linux. One uses the + * traditional /proc virtual filesystem, and the other use the devps kernel + * driver (written by Erik Andersen to avoid using /proc thereby saving 100k+). + * + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */ + + + +#if ! defined BB_FEATURE_USE_DEVPS_PATCH + +/* The following is the first ps implementation -- + * the one using the /proc virtual filesystem. + */ + +typedef struct proc_s { + char + cmd[16]; /* basename of executable file in call to exec(2) */ + int + ruid, /* real only (sorry) */ + pid, /* process id */ + ppid; /* pid of parent process */ + char + state; /* single-char code for process state (S=sleeping) */ +} proc_t; + + + +static int file2str(char *filename, char *ret, int cap) +{ + int fd, num_read; + + if ((fd = open(filename, O_RDONLY, 0)) == -1) + return -1; + if ((num_read = read(fd, ret, cap - 1)) <= 0) + return -1; + ret[num_read] = 0; + close(fd); + return num_read; +} + + +static void parse_proc_status(char *S, proc_t * P) +{ + char *tmp; + + memset(P->cmd, 0, sizeof P->cmd); + sscanf(S, "Name:\t%15c", P->cmd); + tmp = strchr(P->cmd, '\n'); + if (tmp) + *tmp = '\0'; + tmp = strstr(S, "State"); + sscanf(tmp, "State:\t%c", &P->state); + + tmp = strstr(S, "Pid:"); + if (tmp) + sscanf(tmp, "Pid:\t%d\n" "PPid:\t%d\n", &P->pid, &P->ppid); + else + error_msg("Internal error!"); + + /* For busybox, ignoring effective, saved, etc. */ + tmp = strstr(S, "Uid:"); + if (tmp) + sscanf(tmp, "Uid:\t%d", &P->ruid); + else + error_msg("Internal error!"); + + +} + +extern int ps_main(int argc, char **argv) +{ + proc_t p; + DIR *dir; + FILE *file; + struct dirent *entry; + char path[32], sbuf[512]; + char uidName[9]; + int len, i, c; +#ifdef BB_FEATURE_AUTOWIDTH + struct winsize win = { 0, 0, 0, 0 }; + int terminal_width = TERMINAL_WIDTH; +#else +#define terminal_width TERMINAL_WIDTH +#endif + + + + dir = opendir("/proc"); + if (!dir) + error_msg_and_die("Can't open /proc"); + +#ifdef BB_FEATURE_AUTOWIDTH + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (win.ws_col > 0) + terminal_width = win.ws_col - 1; +#endif + + printf(" PID Uid Stat Command\n"); + while ((entry = readdir(dir)) != NULL) { + if (!isdigit(*entry->d_name)) + continue; + sprintf(path, "/proc/%s/status", entry->d_name); + if ((file2str(path, sbuf, sizeof sbuf)) != -1) { + parse_proc_status(sbuf, &p); + } + + /* Make some adjustments as needed */ + my_getpwuid(uidName, p.ruid); + if (*uidName == '\0') + sprintf(uidName, "%d", p.ruid); + + sprintf(path, "/proc/%s/cmdline", entry->d_name); + file = fopen(path, "r"); + if (file == NULL) + continue; + i = 0; + len = printf("%5d %-8s %c ", p.pid, uidName, p.state); + while (((c = getc(file)) != EOF) && (i < (terminal_width-len))) { + i++; + if (c == '\0') + c = ' '; + putc(c, stdout); + } + fclose(file); + if (i == 0) + printf("[%s]", p.cmd); + putchar('\n'); + } + closedir(dir); + return EXIT_SUCCESS; +} + + +#else /* BB_FEATURE_USE_DEVPS_PATCH */ + + +/* The following is the second ps implementation -- + * this one uses the nifty new devps kernel device. + */ + +#include /* For Erik's nifty devps device driver */ + + +extern int ps_main(int argc, char **argv) +{ + char device[] = "/dev/ps"; + int i, j, len, fd; + pid_t num_pids; + pid_t* pid_array = NULL; + struct pid_info info; + char uidName[9]; +#ifdef BB_FEATURE_AUTOWIDTH + struct winsize win = { 0, 0, 0, 0 }; + int terminal_width = TERMINAL_WIDTH; +#else +#define terminal_width TERMINAL_WIDTH +#endif + + if (argc > 1 && **(argv + 1) == '-') + show_usage(); + + /* open device */ + fd = open(device, O_RDONLY); + if (fd < 0) + perror_msg_and_die( "open failed for `%s'", device); + + /* Find out how many processes there are */ + if (ioctl (fd, DEVPS_GET_NUM_PIDS, &num_pids)<0) + perror_msg_and_die( "\nDEVPS_GET_PID_LIST"); + + /* Allocate some memory -- grab a few extras just in case + * some new processes start up while we wait. The kernel will + * just ignore any extras if we give it too many, and will trunc. + * the list if we give it too few. */ + pid_array = (pid_t*) xcalloc( num_pids+10, sizeof(pid_t)); + pid_array[0] = num_pids+10; + + /* Now grab the pid list */ + if (ioctl (fd, DEVPS_GET_PID_LIST, pid_array)<0) + perror_msg_and_die("\nDEVPS_GET_PID_LIST"); + +#ifdef BB_FEATURE_AUTOWIDTH + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (win.ws_col > 0) + terminal_width = win.ws_col - 1; +#endif + + /* Print up a ps listing */ + printf(" PID Uid Stat Command\n"); + + for (i=1; i 1) { + for( j=0; j<(sizeof(info.command_line)-1) && j < (terminal_width-len); j++) { + if (*(info.command_line+j) == '\0' && *(info.command_line+j+1) != '\0') { + *(info.command_line+j) = ' '; + } + } + *(info.command_line+j) = '\0'; + puts(info.command_line); + } else { + printf("[%s]\n", info.name); + } + } + + /* Free memory */ + free( pid_array); + + /* close device */ + if (close (fd) != 0) + perror_msg_and_die("close failed for `%s'", device); + + exit (0); +} + +#endif /* BB_FEATURE_USE_DEVPS_PATCH */ + diff --git a/busybox/pwd.c b/busybox/pwd.c new file mode 100644 index 000000000..f6a00bf1e --- /dev/null +++ b/busybox/pwd.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini pwd implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "busybox.h" + +extern int pwd_main(int argc, char **argv) +{ + static char *buf; + + buf = xgetcwd(buf); + + if (buf != NULL) { + puts(buf); + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} diff --git a/busybox/rdate.c b/busybox/rdate.c new file mode 100644 index 000000000..50be4de8c --- /dev/null +++ b/busybox/rdate.c @@ -0,0 +1,116 @@ +/* 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +static const int RFC_868_BIAS = 2208988800UL; + +static time_t askremotedate(const char *host) +{ + struct hostent *h; + struct sockaddr_in s_in; + struct servent *tserv; + unsigned long int nett, localt; + int fd; + + h = xgethostbyname(host); /* get the IP addr */ + + if ((tserv = getservbyname("time", "tcp")) == NULL) /* find port # */ + perror_msg_and_die("time"); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) /* get net connection */ + perror_msg_and_die("socket"); + + memcpy(&s_in.sin_addr, h->h_addr, sizeof(s_in.sin_addr)); + s_in.sin_port= tserv->s_port; + s_in.sin_family = AF_INET; + + if (connect(fd, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) /* connect to time server */ + perror_msg_and_die("%s", host); + + if (read(fd, (void *)&nett, 4) != 4) /* read time from server */ + 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: + show_usage(); + } + } + + if (optind == argc) + show_usage(); + + remote_time = askremotedate(argv[optind]); + + if (setdate) { + if (stime(&remote_time) < 0) + perror_msg_and_die("Could not set time of day"); + } + + if (printdate) + printf("%s", ctime(&remote_time)); + + return EXIT_SUCCESS; +} diff --git a/busybox/readlink.c b/busybox/readlink.c new file mode 100644 index 000000000..c46ebd108 --- /dev/null +++ b/busybox/readlink.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini readlink implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include "busybox.h" + +int readlink_main(int argc, char **argv) +{ + char *buf = NULL; + + /* no options, no getopt */ + + if (argc != 2) + show_usage(); + + buf = xreadlink(argv[1]); + if (!buf) + return EXIT_FAILURE; + puts(buf); +#ifdef BB_FEATURE_CLEAN_UP + free(buf); +#endif + + return EXIT_SUCCESS; +} diff --git a/busybox/reboot.c b/busybox/reboot.c new file mode 100644 index 000000000..35c147b34 --- /dev/null +++ b/busybox/reboot.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini reboot implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +extern int reboot_main(int argc, char **argv) +{ +#ifdef BB_FEATURE_LINUXRC + /* don't assume init's pid == 1 */ + pid_t *pid = find_pid_by_name("init"); + if (!pid || *pid<=0) + error_msg_and_die("no process killed"); + return(kill(*pid, SIGTERM)); +#else + return(kill(1, SIGTERM)); +#endif +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/renice.c b/busybox/renice.c new file mode 100644 index 000000000..ec35bdcde --- /dev/null +++ b/busybox/renice.c @@ -0,0 +1,54 @@ +/* + * Mini renice implementation for busybox + * + * + * Copyright (C) 2000 Dave 'Kill a Cop' Cinege + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include "busybox.h" + + +extern int renice_main(int argc, char **argv) +{ + int prio, status = EXIT_SUCCESS; + + if (argc < 3) 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 { + perror_msg("%d: setpriority", ps); + status = EXIT_FAILURE; + } + } + + return status; +} diff --git a/busybox/reset.c b/busybox/reset.c new file mode 100644 index 000000000..755c4c335 --- /dev/null +++ b/busybox/reset.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini reset implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * and Kent Robotti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "busybox.h" + +extern int reset_main(int argc, char **argv) +{ + printf("\033c"); + return EXIT_SUCCESS; +} + diff --git a/busybox/rm.c b/busybox/rm.c new file mode 100644 index 000000000..51c9f4ceb --- /dev/null +++ b/busybox/rm.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rm implementation for busybox + * + * + * Copyright (C) 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int rm_main(int argc, char **argv) +{ + int status = 0; + int opt; + int flags = 0; + int i; + + while ((opt = getopt(argc, argv, "fiRr")) != -1) { + switch (opt) { + case 'f': + flags &= ~FILEUTILS_INTERACTIVE; + flags |= FILEUTILS_FORCE; + break; + case 'i': + flags &= ~FILEUTILS_FORCE; + flags |= FILEUTILS_INTERACTIVE; + break; + case 'R': + case 'r': + flags |= FILEUTILS_RECUR; + break; + } + } + + if (!(flags & FILEUTILS_FORCE) && optind == argc) + show_usage(); + + for (i = optind; i < argc; i++) { + char *base = get_last_path_component(argv[i]); + + if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0) { + error_msg("cannot remove `.' or `..'"); + status = 1; + continue; + } + + if (remove_file(argv[i], flags) < 0) + status = 1; + } + + return status; +} diff --git a/busybox/rmdir.c b/busybox/rmdir.c new file mode 100644 index 000000000..2c280376f --- /dev/null +++ b/busybox/rmdir.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rmdir implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include "busybox.h" + +extern int rmdir_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + + if (argc == 1 || **(argv + 1) == '-') + show_usage(); + + while (--argc > 0) { + if (rmdir(*(++argv)) == -1) { + perror_msg("%s", *argv); + status = EXIT_FAILURE; + } + } + return status; +} diff --git a/busybox/rmmod.c b/busybox/rmmod.c new file mode 100644 index 000000000..7596d0232 --- /dev/null +++ b/busybox/rmmod.c @@ -0,0 +1,62 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rmmod implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include "busybox.h" + +extern int delete_module(const char * name); + + +extern int rmmod_main(int argc, char **argv) +{ + int n, ret = EXIT_SUCCESS; + + /* Parse command line. */ + while ((n = getopt(argc, argv, "a")) != EOF) { + switch (n) { + case 'a': + /* Unload _all_ unused modules via NULL delete_module() call */ + if (delete_module(NULL)) + perror_msg_and_die("rmmod"); + return EXIT_SUCCESS; + default: + show_usage(); + } + } + + if (optind == argc) + show_usage(); + + for (n = optind; n < argc; n++) { + if (delete_module(argv[n]) < 0) { + perror_msg("%s", argv[n]); + ret = EXIT_FAILURE; + } + } + + return(ret); +} diff --git a/busybox/route.c b/busybox/route.c new file mode 100644 index 000000000..d571fc5a3 --- /dev/null +++ b/busybox/route.c @@ -0,0 +1,428 @@ +/* route + * + * Similar to the standard Unix route, but with only the necessary + * parts for AF_INET + * + * Bjorn Wesen, Axis Communications AB + * + * Author of the original route: + * Fred N. van Kempen, + * (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.10 2001/03/21 07:34:26 andersen Exp $ + * + * displayroute() code added by Vladimir N. Oleynik + * adjustments by Larry Doolittle + */ + +#include +#include +#include +#include +#include // HZ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define _(x) x + +#define RTACTION_ADD 1 +#define RTACTION_DEL 2 +#define RTACTION_HELP 3 +#define RTACTION_FLUSH 4 +#define RTACTION_SHOW 5 + +#define E_NOTFOUND 8 +#define E_SOCK 7 +#define E_LOOKUP 6 +#define E_VERSION 5 +#define E_USAGE 4 +#define E_OPTERR 3 +#define E_INTERN 2 +#define E_NOSUPP 1 + +/* resolve XXX.YYY.ZZZ.QQQ -> binary */ + +static int +INET_resolve(char *name, struct sockaddr *sa) +{ + struct sockaddr_in *s_in = (struct sockaddr_in *)sa; + + s_in->sin_family = AF_INET; + s_in->sin_port = 0; + + /* Default is special, meaning 0.0.0.0. */ + if (strcmp(name, "default")==0) { + s_in->sin_addr.s_addr = INADDR_ANY; + return 1; + } + /* Look to see if it's a dotted quad. */ + if (inet_aton(name, &s_in->sin_addr)) { + return 0; + } + /* guess not.. */ + return -1; +} + +#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */ +#define HAVE_NEW_ADDRT 1 +#endif +#ifdef RTF_IRTT /* route */ +#define HAVE_RTF_IRTT 1 +#endif +#ifdef RTF_REJECT /* route */ +#define HAVE_RTF_REJECT 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 + +/* add or delete a route depending on action */ + +static int +INET_setroute(int action, int options, char **args) +{ + struct rtentry rt; + char target[128], gateway[128] = "NONE", netmask[128] = "default"; + int xflag, isnet; + int skfd; + + xflag = 0; + + if (strcmp(*args, "-net")==0) { + xflag = 1; + args++; + } else if (strcmp(*args, "-host")==0) { + xflag = 2; + args++; + } + if (*args == NULL) + show_usage(); + + safe_strncpy(target, *args++, (sizeof target)); + + /* Clean out the RTREQ structure. */ + memset((char *) &rt, 0, sizeof(struct rtentry)); + + + if ((isnet = INET_resolve(target, &rt.rt_dst)) < 0) { + error_msg(_("can't resolve %s"), target); + return EXIT_FAILURE; /* XXX change to E_something */ + } + + switch (xflag) { + case 1: + isnet = 1; + break; + + case 2: + isnet = 0; + break; + + default: + break; + } + + /* Fill in the other fields. */ + rt.rt_flags = (RTF_UP | RTF_HOST); + if (isnet) + rt.rt_flags &= ~RTF_HOST; + + while (*args) { + if (strcmp(*args, "metric")==0) { + int metric; + + args++; + if (!*args || !isdigit(**args)) + show_usage(); + metric = atoi(*args); +#if HAVE_NEW_ADDRT + rt.rt_metric = metric + 1; +#else + ENOSUPP("inet_setroute", "NEW_ADDRT (metric)"); /* XXX Fixme */ +#endif + args++; + continue; + } + + if (strcmp(*args, "netmask")==0) { + struct sockaddr mask; + + args++; + if (!*args || mask_in_addr(rt)) + show_usage(); + safe_strncpy(netmask, *args, (sizeof netmask)); + if ((isnet = INET_resolve(netmask, &mask)) < 0) { + error_msg(_("can't resolve netmask %s"), netmask); + return E_LOOKUP; + } + rt.rt_genmask = full_mask(mask); + args++; + continue; + } + + if (strcmp(*args, "gw")==0 || strcmp(*args, "gateway")==0) { + args++; + if (!*args) + show_usage(); + if (rt.rt_flags & RTF_GATEWAY) + show_usage(); + safe_strncpy(gateway, *args, (sizeof gateway)); + if ((isnet = INET_resolve(gateway, &rt.rt_gateway)) < 0) { + error_msg(_("can't resolve gw %s"), gateway); + return E_LOOKUP; + } + if (isnet) { + error_msg( + _("%s: cannot use a NETWORK as gateway!"), + gateway); + return E_OPTERR; + } + rt.rt_flags |= RTF_GATEWAY; + args++; + continue; + } + + if (strcmp(*args, "mss")==0) { + args++; + rt.rt_flags |= RTF_MSS; + if (!*args) + show_usage(); + rt.rt_mss = atoi(*args); + args++; + if (rt.rt_mss < 64 || rt.rt_mss > 32768) { + error_msg(_("Invalid MSS.")); + return E_OPTERR; + } + continue; + } + + if (strcmp(*args, "window")==0) { + args++; + if (!*args) + show_usage(); + rt.rt_flags |= RTF_WINDOW; + rt.rt_window = atoi(*args); + args++; + if (rt.rt_window < 128) { + error_msg(_("Invalid window.")); + return E_OPTERR; + } + continue; + } + + if (strcmp(*args, "irtt")==0) { + args++; + if (!*args) + show_usage(); + args++; +#if HAVE_RTF_IRTT + rt.rt_flags |= RTF_IRTT; + rt.rt_irtt = atoi(*(args - 1)); + rt.rt_irtt *= (HZ / 100); /* FIXME */ +#if 0 /* FIXME: do we need to check anything of this? */ + if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) { + error_msg(_("Invalid initial rtt.")); + return E_OPTERR; + } +#endif +#else + ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */ +#endif + continue; + } + + if (strcmp(*args, "reject")==0) { + args++; +#if HAVE_RTF_REJECT + rt.rt_flags |= RTF_REJECT; +#else + ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */ +#endif + continue; + } + if (strcmp(*args, "mod")==0) { + args++; + rt.rt_flags |= RTF_MODIFIED; + continue; + } + if (strcmp(*args, "dyn")==0) { + args++; + rt.rt_flags |= RTF_DYNAMIC; + continue; + } + if (strcmp(*args, "reinstate")==0) { + args++; + rt.rt_flags |= RTF_REINSTATE; + continue; + } + if (strcmp(*args, "device")==0 || strcmp(*args, "dev")==0) { + args++; + if (rt.rt_dev || *args == NULL) + show_usage(); + rt.rt_dev = *args++; + continue; + } + /* nothing matches */ + if (!rt.rt_dev) { + rt.rt_dev = *args++; + if (*args) + show_usage(); /* must be last to catch typos */ + } else { + show_usage(); + } + } + +#if HAVE_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) { + error_msg( + _("netmask %.8x doesn't make sense with host route"), + (unsigned int)mask); + return E_OPTERR; + } + if (mask & (mask + 1)) { + error_msg(_("bogus netmask %s"), netmask); + return E_OPTERR; + } + mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr; + if (mask & ~mask_in_addr(rt)) { + error_msg(_("netmask doesn't match route address")); + return E_OPTERR; + } + } + /* 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) { + perror("socket"); + return E_SOCK; + } + /* Tell the kernel to accept this route. */ + if (action == RTACTION_DEL) { + if (ioctl(skfd, SIOCDELRT, &rt) < 0) { + perror("SIOCDELRT"); + close(skfd); + return E_SOCK; + } + } else { + if (ioctl(skfd, SIOCADDRT, &rt) < 0) { + perror("SIOCADDRT"); + close(skfd); + return E_SOCK; + } + } + + /* Close the socket. */ + (void) close(skfd); + return EXIT_SUCCESS; +} + +static void displayroutes(void) +{ + char buff[256]; + int nl = 0 ; + struct in_addr dest; + struct in_addr gw; + struct in_addr mask; + int flgs, ref, use, metric; + char flags[4]; + unsigned long int d,g,m; + + char sdest[16], sgw[16]; + + + FILE *fp = xfopen("/proc/net/route", "r"); + + while( fgets(buff, sizeof(buff), fp) != NULL ) { + if(nl) { + int ifl = 0; + while(buff[ifl]!=' ' && buff[ifl]!='\t' && buff[ifl]!='\0') + ifl++; + buff[ifl]=0; /* interface */ + if(sscanf(buff+ifl+1, "%lx%lx%d%d%d%d%lx", + &d, &g, &flgs, &ref, &use, &metric, &m)!=7) { + error_msg_and_die( "Unsuported kernel route format\n"); + } + if(nl==1) { + printf("Kernel IP routing table\n" +"Destination Gateway Genmask Flags Metric Ref Use Iface\n"); + } + + + ifl = 0; /* parse flags */ + if(flgs&1) + flags[ifl++]='U'; + if(flgs&2) + flags[ifl++]='G'; + if(flgs&4) + flags[ifl++]='H'; + flags[ifl]=0; + dest.s_addr = d; + gw.s_addr = g; + mask.s_addr = m; + strcpy(sdest, (dest.s_addr==0 ? "default" : + inet_ntoa(dest))); + strcpy(sgw, (gw.s_addr==0 ? "*" : + inet_ntoa(gw))); + printf("%-16s%-16s%-16s%-6s%-6d %-2d %7d %s\n", + sdest, sgw, + inet_ntoa(mask), + flags, metric, ref, use, buff); + } + nl++; + } +} + +int route_main(int argc, char **argv) +{ + int what = 0; + + argc--; + argv++; + + if (*argv == NULL) { + displayroutes(); + return EXIT_SUCCESS; + } else { + /* check verb */ + if (strcmp(*argv, "add")==0) + what = RTACTION_ADD; + else if (strcmp(*argv, "del")==0 || strcmp(*argv, "delete")==0) + what = RTACTION_DEL; + else if (strcmp(*argv, "flush")==0) + what = RTACTION_FLUSH; + else + show_usage(); + } + + return INET_setroute(what, 0, ++argv); +} diff --git a/busybox/rpm2cpio.c b/busybox/rpm2cpio.c new file mode 100644 index 000000000..45c3ffb17 --- /dev/null +++ b/busybox/rpm2cpio.c @@ -0,0 +1,95 @@ +/* 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 "busybox.h" +#include /* For ntohl & htonl function */ +#include + +#define RPM_MAGIC "\355\253\356\333" +#define RPM_HEADER_MAGIC "\216\255\350" + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +struct rpm_lead { + unsigned char magic[4]; + u8 major, minor; + u16 type; + u16 archnum; + char name[66]; + u16 osnum; + u16 signature_type; + char reserved[16]; +}; + +struct rpm_header { + char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */ + u8 version; /* 1 byte version number */ + u32 reserved; /* 4 bytes reserved */ + u32 entries; /* Number of entries in header (4 bytes) */ + u32 size; /* Size of store (4 bytes) */ +}; + +void skip_header(FILE *rpmfile) +{ + struct rpm_header header; + + fread(&header, sizeof(struct rpm_header), 1, rpmfile); + if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */ + if (header.version != 1) error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */ + header.entries = ntohl(header.entries); + header.size = ntohl(header.size); + fseek (rpmfile, 16 * header.entries, SEEK_CUR); /* Seek past index entries */ + fseek (rpmfile, header.size, SEEK_CUR); /* Seek past store */ +} + +/* No getopt required */ +extern int rpm2cpio_main(int argc, char **argv) +{ + struct rpm_lead lead; + int gunzip_pid; + FILE *rpmfile, *cpiofile; + + if (argc == 1) { + rpmfile = stdin; + } else { + rpmfile = fopen(argv[1], "r"); + if (!rpmfile) perror_msg_and_die("Can't open rpm file"); + /* set the buffer size */ + setvbuf(rpmfile, NULL, _IOFBF, 0x8000); + } + + fread (&lead, sizeof(struct rpm_lead), 1, rpmfile); + if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */ + /* Skip the signature header */ + skip_header(rpmfile); + fseek(rpmfile, (8 - (ftell(rpmfile) % 8)) % 8, SEEK_CUR); /* Pad to 8 byte boundary */ + /* Skip the main header */ + skip_header(rpmfile); + + cpiofile = gz_open(rpmfile, &gunzip_pid); + + copyfd(fileno(cpiofile), fileno(stdout)); + gz_close(gunzip_pid); + fclose(rpmfile); + return 0; +} diff --git a/busybox/scripts/depmod.pl b/busybox/scripts/depmod.pl new file mode 100755 index 000000000..e65f07b68 --- /dev/null +++ b/busybox/scripts/depmod.pl @@ -0,0 +1,227 @@ +#!/usr/bin/perl -w +# vi: set ts=4: +# Copyright (c) 2001 David Schleef +# Copyright (c) 2001 Erik Andersen +# Copyright (c) 2001 Stuart Hughes +# 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=1; +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", + "\t-h --help\t\tShow this help screen\n", + "\t-b --basedir\t\tModules base directory (defaults to /lib/modules)\n", + "\t-k --kernel\t\tKernel binary for the target\n", + "\t-F --kernelsyms\t\tKernel symbol file\n", + "\t-n --stdout\t\tWrite to stdout instead of modules.dep\n", + "\t-v --verbose\t\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($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 +foreach $module ( keys %$mod ) { + print "$module:\t"; + @sorted = sort bydep keys %{$mod->{$module}}; + print join(" \\\n\t",@sorted); +# foreach $m (@sorted ) { +# print "\t$m\n"; +# } + print "\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]... [FILE]... + +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 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 +Copyright (c) 2001 Erik Andersen +Copyright (c) 2001 Stuart Hughes +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + +=head1 AUTHOR + +David Schleef + +=cut + +# $Id: depmod.pl,v 1.1 2001/07/30 19:32:03 andersen Exp $ + diff --git a/busybox/scripts/inittab b/busybox/scripts/inittab new file mode 100644 index 000000000..8e7e945b3 --- /dev/null +++ b/busybox/scripts/inittab @@ -0,0 +1,86 @@ +# /etc/inittab init(8) configuration for BusyBox +# +# Copyright (C) 1999 by Lineo, inc. Written by Erik Andersen +# , +# +# +# 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: ::: +# +# : 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. +# +# : The runlevels field is completely ignored. +# +# : Valid actions include: sysinit, respawn, askfirst, wait, once, +# 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. +# +# : 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 +# 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 before rebooting +::ctrlaltdel:/sbin/reboot +::shutdown:/bin/umount -a -r +::shutdown:/sbin/swapoff -a + diff --git a/busybox/scripts/mk2knr.pl b/busybox/scripts/mk2knr.pl new file mode 100755 index 000000000..aaf4963b1 --- /dev/null +++ b/busybox/scripts/mk2knr.pl @@ -0,0 +1,84 @@ +#!/usr/bin/perl -w +# +# @(#) mk2knr.pl - generates a perl script that converts lexemes to K&R-style +# +# How to use this script: +# - In the busybox directory type 'scripts/mk2knr.pl files-you-want-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/scripts/undeb b/busybox/scripts/undeb new file mode 100644 index 000000000..a72e1e2ba --- /dev/null +++ b/busybox/scripts/undeb @@ -0,0 +1,53 @@ +#!/bin/sh +# +# This should work with the GNU version of tar and gzip! +# This should work with the bash or ash shell! +# Requires the programs (ar, tar, gzip, and the pager more or less). +# +usage() { +echo "Usage: undeb -c package.deb " +echo " undeb -l package.deb " +echo " undeb -x package.deb /foo/boo " +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/scripts/unrpm b/busybox/scripts/unrpm new file mode 100644 index 000000000..376286a6f --- /dev/null +++ b/busybox/scripts/unrpm @@ -0,0 +1,48 @@ +#!/bin/sh +# +# This should work with the GNU version of cpio and gzip! +# This should work with the bash or ash shell! +# Requires the programs (cpio, gzip, and the pager more or less). +# +usage() { +echo "Usage: unrpm -l package.rpm " +echo " unrpm -x package.rpm /foo/boo " +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/sed.c b/busybox/sed.c new file mode 100644 index 000000000..a18cfc7c3 --- /dev/null +++ b/busybox/sed.c @@ -0,0 +1,800 @@ +/* + * sed.c - very minimalist version of sed + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + 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) + + (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: + + - transliteration (y/source-chars/dest-chars/) (use 'tr') + - no pattern space hold space storing / swapping (x, etc.) + - no labels / branching (: label, b, t, and friends) + - and lots, lots more. +*/ + +#include +#include /* for getopt() */ +#include +#include /* for strdup() */ +#include +#include /* for isspace() */ +#include +#include "busybox.h" + +/* externs */ +extern void xregcomp(regex_t *preg, const char *regex, int cflags); +extern int optind; /* in unistd.h */ +extern char *optarg; /* ditto */ + +/* options */ +static int be_quiet = 0; + + +struct sed_cmd { + + + /* GENERAL FIELDS */ + char delimiter; /* The delimiter used to separate regexps */ + + /* address storage */ + int beg_line; /* 'sed 1p' 0 == no begining line, apply commands to all lines */ + int end_line; /* 'sed 1,3p' 0 == no end line, use only beginning. -1 == $ */ + regex_t *beg_match; /* sed -e '/match/cmd' */ + regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */ + + /* the command */ + char cmd; /* p,d,s (add more at your leisure :-) */ + + + /* SUBSTITUTION COMMAND SPECIFIC FIELDS */ + + /* sed -e 's/sub_match/replace/' */ + regex_t *sub_match; + char *replace; + unsigned int num_backrefs:4; /* how many back references (\1..\9) */ + /* Note: GNU/POSIX sed does not save more than nine backrefs, so + * we only use 4 bits to hold the number */ + unsigned int sub_g:1; /* sed -e 's/foo/bar/g' (global) */ + unsigned int sub_p:2; /* sed -e 's/foo/bar/p' (print substitution) */ + + + /* EDIT COMMAND (a,i,c) SPEICIFIC FIELDS */ + + char *editline; + + + /* FILE COMMAND (r) SPEICIFIC FIELDS */ + + char *filename; +}; + +/* globals */ +static struct sed_cmd *sed_cmds = NULL; /* growable arrary holding a sequence of sed cmds */ +static int ncmds = 0; /* number of sed commands */ + +/*static char *cur_file = NULL;*/ /* file currently being processed XXX: do I need this? */ + +#ifdef BB_FEATURE_CLEAN_UP +static void destroy_cmd_strs() +{ + if (sed_cmds == NULL) + return; + + /* destroy all the elements in the array */ + while (--ncmds >= 0) { + + if (sed_cmds[ncmds].beg_match) { + regfree(sed_cmds[ncmds].beg_match); + free(sed_cmds[ncmds].beg_match); + } + if (sed_cmds[ncmds].end_match) { + regfree(sed_cmds[ncmds].end_match); + free(sed_cmds[ncmds].end_match); + } + if (sed_cmds[ncmds].sub_match) { + regfree(sed_cmds[ncmds].sub_match); + free(sed_cmds[ncmds].sub_match); + } + if (sed_cmds[ncmds].replace) + free(sed_cmds[ncmds].replace); + } + + /* destroy the array */ + free(sed_cmds); + sed_cmds = NULL; +} +#endif + + +/* + * 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 preceeded by + * a backslash ('\'). + */ +static int index_of_next_unescaped_regexp_delim(struct sed_cmd *sed_cmd, const char *str, int idx) +{ + for ( ; str[idx]; idx++) { + if (str[idx] == sed_cmd->delimiter && str[idx-1] != '\\') + return idx; + } + + /* if we make it to here, we've hit the end of the string */ + return -1; +} + +/* + * returns the index in the string just past where the address ends. + */ +static int get_address(struct sed_cmd *sed_cmd, const char *str, int *linenum, regex_t **regex) +{ + char *my_str = strdup(str); + int idx = 0; + char olddelimiter; + olddelimiter = sed_cmd->delimiter; + sed_cmd->delimiter = '/'; + + if (isdigit(my_str[idx])) { + do { + idx++; + } while (isdigit(my_str[idx])); + my_str[idx] = 0; + *linenum = atoi(my_str); + } + else if (my_str[idx] == '$') { + *linenum = -1; + idx++; + } + else if (my_str[idx] == '/') { + idx = index_of_next_unescaped_regexp_delim(sed_cmd, my_str, ++idx); + if (idx == -1) + error_msg_and_die("unterminated match expression"); + my_str[idx] = '\0'; + *regex = (regex_t *)xmalloc(sizeof(regex_t)); + xregcomp(*regex, my_str+1, REG_NEWLINE); + idx++; /* so it points to the next character after the last '/' */ + } + else { + error_msg("get_address: no address found in string\n" + "\t(you probably didn't check the string you passed me)"); + idx = -1; + } + + free(my_str); + sed_cmd->delimiter = olddelimiter; + return idx; +} + +static int parse_subst_cmd(struct sed_cmd *sed_cmd, const char *substr) +{ + int oldidx, cflags = REG_NEWLINE; + char *match; + int idx = 0; + int j; + + /* + * the string that gets passed to this function should look like this: + * s/match/replace/gIp + * || | ||| + * mandatory optional + * + * (all three of the '/' slashes are mandatory) + */ + + /* verify that the 's' is followed by something. That something + * (typically a 'slash') is now our regexp delimiter... */ + if (!substr[++idx]) + error_msg_and_die("bad format in substitution expression"); + else + sed_cmd->delimiter=substr[idx]; + + /* save the match string */ + oldidx = idx+1; + idx = index_of_next_unescaped_regexp_delim(sed_cmd, substr, ++idx); + if (idx == -1) + error_msg_and_die("bad format in substitution expression"); + match = xstrndup(substr + oldidx, idx - oldidx); + + /* 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 */ + + /* sed_cmd->num_backrefs = 0; */ /* XXX: not needed? --apparently not */ + for (j = 0; match[j]; j++) { + /* GNU/POSIX sed does not save more than nine backrefs */ + if (match[j] == '\\' && match[j+1] == '(' && sed_cmd->num_backrefs <= 9) + sed_cmd->num_backrefs++; + } + + /* save the replacement string */ + oldidx = idx+1; + idx = index_of_next_unescaped_regexp_delim(sed_cmd, substr, ++idx); + if (idx == -1) + error_msg_and_die("bad format in substitution expression"); + sed_cmd->replace = xstrndup(substr + oldidx, idx - oldidx); + + /* process the flags */ + while (substr[++idx]) { + switch (substr[idx]) { + case 'g': + sed_cmd->sub_g = 1; + break; + case 'I': + cflags |= REG_ICASE; + break; + case 'p': + sed_cmd->sub_p = 1; + break; + default: + /* any whitespace or semicolon trailing after a s/// is ok */ + if (strchr("; \t\v\n\r", substr[idx])) + goto out; + /* else */ + error_msg_and_die("bad option in substitution expression"); + } + } + +out: + /* compile the match string into a regex */ + sed_cmd->sub_match = (regex_t *)xmalloc(sizeof(regex_t)); + xregcomp(sed_cmd->sub_match, match, cflags); + free(match); + + return idx; +} + +static int parse_edit_cmd(struct sed_cmd *sed_cmd, const char *editstr) +{ + int idx = 0; + int slashes_eaten = 0; + char *ptr; /* shorthand */ + + /* + * the string that gets passed to this function should look like this: + * + * need one of these + * | + * | this backslash (immediately following the edit command) is mandatory + * | | + * [aic]\ + * TEXT1\ + * TEXT2\ + * TEXTN + * + * as soon as we hit a TEXT line that has no trailing '\', we're done. + * this means a command like: + * + * i\ + * INSERTME + * + * is a-ok. + * + */ + + if (editstr[1] != '\\' && (editstr[2] != '\n' || editstr[2] != '\r')) + error_msg_and_die("bad format in edit expression"); + + /* store the edit line text */ + /* make editline big enough to accomodate the extra '\n' we will tack on + * to the end */ + sed_cmd->editline = xmalloc(strlen(&editstr[3]) + 2); + strcpy(sed_cmd->editline, &editstr[3]); + ptr = sed_cmd->editline; + + /* now we need to go through * and: s/\\[\r\n]$/\n/g on the edit line */ + while (ptr[idx]) { + while (ptr[idx] != '\\' || (ptr[idx+1] != '\n' && ptr[idx+1] != '\r')) { + idx++; + if (!ptr[idx]) { + goto out; + } + } + /* move the newline over the '\' before it (effectively eats the '\') */ + memmove(&ptr[idx], &ptr[idx+1], strlen(&ptr[idx+1])); + ptr[strlen(ptr)-1] = 0; + slashes_eaten++; + /* substitue \r for \n if needed */ + if (ptr[idx] == '\r') + ptr[idx] = '\n'; + } + +out: + /* this accounts for discrepancies between the modified string and the + * original string passed in to this function */ + idx += slashes_eaten; + + /* figure out if we need to add a newline */ + if (ptr[idx-1] != '\n') { + ptr[idx] = '\n'; + idx++; + } + + /* terminate string */ + ptr[idx]= 0; + /* adjust for opening 2 chars [aic]\ */ + idx += 2; + + return idx; +} + + +static int parse_file_cmd(struct sed_cmd *sed_cmd, const char *filecmdstr) +{ + int idx = 0; + int filenamelen = 0; + + /* + * the string that gets passed to this function should look like this: + * '[ ]filename' + * | | + * | a filename + * | + * optional whitespace + + * re: the file to be read, the GNU manual says the following: "Note that + * if filename cannot be read, it is treated as if it were an empty file, + * without any error indication." Thus, all of the following commands are + * perfectly leagal: + * + * sed -e '1r noexist' + * sed -e '1r ;' + * sed -e '1r' + */ + + /* the file command may be followed by whitespace; move past it. */ + while (isspace(filecmdstr[++idx])) + { ; } + + /* the first non-whitespace we get is a filename. the filename ends when we + * hit a normal sed command terminator or end of string */ + filenamelen = strcspn(&filecmdstr[idx], "; \n\r\t\v\0"); + sed_cmd->filename = xmalloc(filenamelen + 1); + safe_strncpy(sed_cmd->filename, &filecmdstr[idx], filenamelen + 1); + + return idx + filenamelen; +} + + +static char *parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) +{ + int idx = 0; + + /* parse the command + * format is: [addr][,addr]cmd + * |----||-----||-| + * part1 part2 part3 + */ + + /* first part (if present) is an address: either a number or a /regex/ */ + if (isdigit(cmdstr[idx]) || cmdstr[idx] == '/') + idx = get_address(sed_cmd, cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); + + /* second part (if present) will begin with a comma */ + if (cmdstr[idx] == ',') + idx += get_address(sed_cmd, &cmdstr[++idx], &sed_cmd->end_line, &sed_cmd->end_match); + + /* last part (mandatory) will be a command */ + if (cmdstr[idx] == '\0') + error_msg_and_die("missing command"); + sed_cmd->cmd = cmdstr[idx]; + + /* if it was a single-letter command that takes no arguments (such as 'p' + * or 'd') all we need to do is increment the index past that command */ + if (strchr("pd", cmdstr[idx])) { + idx++; + } + /* handle (s)ubstitution command */ + else if (sed_cmd->cmd == 's') { + idx += parse_subst_cmd(sed_cmd, &cmdstr[idx]); + } + /* 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') + error_msg_and_die("only a beginning address can be specified for edit commands"); + idx += parse_edit_cmd(sed_cmd, &cmdstr[idx]); + } + /* handle file cmds: (r)ead */ + else if (sed_cmd->cmd == 'r') { + if (sed_cmd->end_line || sed_cmd->end_match) + error_msg_and_die("Command only uses one address"); + idx += parse_file_cmd(sed_cmd, &cmdstr[idx]); + } + else { + error_msg_and_die("invalid command"); + } + + /* give back whatever's left over */ + return (char *)&cmdstr[idx]; +} + +static void add_cmd_str(const char *cmdstr) +{ + char *mystr = (char *)cmdstr; + + do { + + /* trim leading whitespace and semicolons */ + memmove(mystr, &mystr[strspn(mystr, "; \n\r\t\v")], strlen(mystr)); + /* if we ate the whole thing, that means there was just trailing + * whitespace or a final / no-op semicolon. either way, get out */ + if (strlen(mystr) == 0) + return; + /* if this is a comment, jump past it and keep going */ + if (mystr[0] == '#') { + mystr = strpbrk(mystr, ";\n\r"); + continue; + } + /* grow the array */ + sed_cmds = xrealloc(sed_cmds, sizeof(struct sed_cmd) * (++ncmds)); + /* zero new element */ + memset(&sed_cmds[ncmds-1], 0, sizeof(struct sed_cmd)); + /* load command string into new array element, get remainder */ + mystr = parse_cmd_str(&sed_cmds[ncmds-1], mystr); + + } while (mystr && strlen(mystr)); +} + + +static void load_cmd_file(char *filename) +{ + FILE *cmdfile; + char *line; + char *nextline; + + cmdfile = xfopen(filename, "r"); + + while ((line = get_line_from_file(cmdfile)) != NULL) { + /* if a line ends with '\' it needs the next line appended to it */ + while (line[strlen(line)-2] == '\\' && + (nextline = get_line_from_file(cmdfile)) != NULL) { + line = xrealloc(line, strlen(line) + strlen(nextline) + 1); + strcat(line, nextline); + free(nextline); + } + /* eat trailing newline (if any) --if I don't do this, edit commands + * (aic) will print an extra newline */ + chomp(line); + add_cmd_str(line); + free(line); + } +} + +static void print_subst_w_backrefs(const char *line, const char *replace, regmatch_t *regmatch) +{ + int i; + + /* 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] == '\\' && isdigit(replace[i+1])) { + int j; + char tmpstr[2]; + int backref; + ++i; /* i now indexes the backref number, instead of the leading slash */ + tmpstr[0] = replace[i]; + tmpstr[1] = 0; + backref = atoi(tmpstr); + /* print out the text held in regmatch[backref] */ + for (j = regmatch[backref].rm_so; j < regmatch[backref].rm_eo; j++) + fputc(line[j], stdout); + } + + /* if we find a backslash escaped character, print the character */ + else if (replace[i] == '\\') { + ++i; + fputc(replace[i], stdout); + } + + /* if we find an unescaped '&' print out the whole matched text. + * fortunately, regmatch[0] contains the indicies to the whole matched + * expression (kinda seems like it was designed for just such a + * purpose...) */ + else if (replace[i] == '&' && replace[i-1] != '\\') { + int j; + for (j = regmatch[0].rm_so; j < regmatch[0].rm_eo; j++) + fputc(line[j], stdout); + } + /* nothing special, just print this char of the replacement string to stdout */ + else + fputc(replace[i], stdout); + } +} + +static int do_subst_command(const struct sed_cmd *sed_cmd, const char *line) +{ + char *hackline = (char *)line; + int altered = 0; + regmatch_t *regmatch = NULL; + + /* we only proceed if the substitution 'search' expression matches */ + if (regexec(sed_cmd->sub_match, line, 0, NULL, 0) == REG_NOMATCH) + return 0; + + /* whaddaya know, it matched. get the number of back references */ + regmatch = xmalloc(sizeof(regmatch_t) * (sed_cmd->num_backrefs+1)); + + /* and now, as long as we've got a line to try matching and if we can match + * the search string, we make substitutions */ + while (*hackline && (regexec(sed_cmd->sub_match, hackline, + sed_cmd->num_backrefs+1, regmatch, 0) == 0) ) { + int i; + + /* print everything before the match */ + for (i = 0; i < regmatch[0].rm_so; i++) + fputc(hackline[i], stdout); + + /* then print the substitution string */ + print_subst_w_backrefs(hackline, sed_cmd->replace, regmatch); + + /* advance past the match */ + hackline += regmatch[0].rm_eo; + /* flag that something has changed */ + altered++; + + /* if we're not doing this globally, get out now */ + if (!sed_cmd->sub_g) + break; + } + + puts(hackline); + + /* cleanup */ + free(regmatch); + + return altered; +} + + +static void process_file(FILE *file) +{ + char *line = NULL; + static int linenum = 0; /* GNU sed does not restart counting lines at EOF */ + unsigned int still_in_range = 0; + int altered; + int i; + + /* go through every line in the file */ + while ((line = get_line_from_file(file)) != NULL) { + + chomp(line); + linenum++; + altered = 0; + + /* for every line, go through all the commands */ + for (i = 0; i < ncmds; i++) { + + + /* + * entry point into sedding... + */ + if ( + /* no range necessary */ + (sed_cmds[i].beg_line == 0 && sed_cmds[i].end_line == 0 && + sed_cmds[i].beg_match == NULL && + sed_cmds[i].end_match == NULL) || + /* this line number is the first address we're looking for */ + (sed_cmds[i].beg_line && (sed_cmds[i].beg_line == linenum)) || + /* this line matches our first address regex */ + (sed_cmds[i].beg_match && (regexec(sed_cmds[i].beg_match, line, 0, NULL, 0) == 0)) || + /* we are currently within the beginning & ending address range */ + still_in_range + ) { + + /* + * actual sedding + */ + switch (sed_cmds[i].cmd) { + + case 'p': + puts(line); + break; + + case 'd': + altered++; + break; + + case 's': + + /* + * Some special cases for 's' printing to make it compliant with + * GNU sed printing behavior (aka "The -n | s///p Matrix"): + * + * -n ONLY = never print anything regardless of any successful + * substitution + * + * s///p ONLY = always print successful substitutions, even if + * the line is going to be printed anyway (line will be printed + * twice). + * + * -n AND s///p = print ONLY a successful substitution ONE TIME; + * no other lines are printed - this is the reason why the 'p' + * flag exists in the first place. + */ + + /* if the user specified that they didn't want anything printed (i.e., a -n + * flag and no 'p' flag after the s///), then there's really no point doing + * anything here. */ + if (be_quiet && !sed_cmds[i].sub_p) + break; + + /* we print the line once, unless we were told to be quiet */ + if (!be_quiet) + altered |= do_subst_command(&sed_cmds[i], line); + + /* we also print the line if we were given the 'p' flag + * (this is quite possibly the second printing) */ + if (sed_cmds[i].sub_p) + altered |= do_subst_command(&sed_cmds[i], line); + + break; + + case 'a': + puts(line); + fputs(sed_cmds[i].editline, stdout); + altered++; + break; + + case 'i': + fputs(sed_cmds[i].editline, stdout); + break; + + case 'c': + /* single-address case */ + if (sed_cmds[i].end_match == NULL && sed_cmds[i].end_line == 0) { + fputs(sed_cmds[i].editline, stdout); + } + /* multi-address case */ + else { + /* matching text */ + if (sed_cmds[i].end_match && (regexec(sed_cmds[i].end_match, line, 0, NULL, 0) == 0)) + fputs(sed_cmds[i].editline, stdout); + /* matching line numbers */ + if (sed_cmds[i].end_line > 0 && sed_cmds[i].end_line == linenum) + fputs(sed_cmds[i].editline, stdout); + } + altered++; + + break; + + case 'r': { + FILE *outfile; + puts(line); + outfile = fopen(sed_cmds[i].filename, "r"); + if (outfile) + print_file(outfile); + /* else if we couldn't open the output file, + * no biggie, just don't print anything */ + altered++; + } + break; + } + + /* + * exit point from sedding... + */ + if ( + /* this is a single-address command or... */ + (sed_cmds[i].end_line == 0 && sed_cmds[i].end_match == NULL) || ( + /* we were in the middle of our address range (this + * isn't the first time through) and.. */ + (still_in_range == 1) && ( + /* this line number is the last address we're looking for or... */ + (sed_cmds[i].end_line && (sed_cmds[i].end_line == linenum)) || + /* this line matches our last address regex */ + (sed_cmds[i].end_match && (regexec(sed_cmds[i].end_match, line, 0, NULL, 0) == 0)) + ) + ) + ) { + /* we're out of our address range */ + still_in_range = 0; + } + + /* didn't hit the exit? then we're still in the middle of an address range */ + else { + still_in_range = 1; + } + } + } + + /* we will print the line unless we were told to be quiet or if the + * line was altered (via a 'd'elete or 's'ubstitution), in which case + * the altered line was already printed */ + if (!be_quiet && !altered) + puts(line); + + free(line); + } +} + +extern int sed_main(int argc, char **argv) +{ + int opt; + +#ifdef BB_FEATURE_CLEAN_UP + /* destroy command strings on exit */ + if (atexit(destroy_cmd_strs) == -1) + perror_msg_and_die("atexit"); +#endif + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "ne:f:")) > 0) { + switch (opt) { + case 'n': + be_quiet++; + break; + case 'e': + add_cmd_str(optarg); + break; + case 'f': + load_cmd_file(optarg); + break; + default: + 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 (ncmds == 0) { + if (argv[optind] == NULL) + show_usage(); + else { + add_cmd_str(argv[optind]); + optind++; + } + } + + + /* 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)) { + process_file(stdin); + } + else { + int i; + FILE *file; + for (i = optind; i < argc; i++) { + file = fopen(argv[i], "r"); + if (file == NULL) { + perror_msg("%s", argv[i]); + } else { + process_file(file); + fclose(file); + } + } + } + + return 0; +} diff --git a/busybox/setkeycodes.c b/busybox/setkeycodes.c new file mode 100644 index 000000000..c3c7e09aa --- /dev/null +++ b/busybox/setkeycodes.c @@ -0,0 +1,72 @@ +/* vi: set sw=4 ts=4: */ +/* + * setkeycodes + * + * Copyright (C) 1994-1998 Andries E. Brouwer + * + * Adjusted for BusyBox by 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 +#include +#include +#include +#include "busybox.h" + + +/* From */ +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) { + show_usage(); + } + + fd = get_console_fd("/dev/console"); + + while (argc > 2) { + a.keycode = atoi(argv[2]); + a.scancode = sc = strtol(argv[1], &ep, 16); + if (*ep) { + 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) { + error_msg_and_die("SCANCODE or KEYCODE outside bounds"); + } + if (ioctl(fd,KDSETKEYCODE,&a)) { + perror("KDSETKEYCODE"); + 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/shell/ash.c b/busybox/shell/ash.c new file mode 100644 index 000000000..bcd12f106 --- /dev/null +++ b/busybox/shell/ash.c @@ -0,0 +1,12880 @@ +/* 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. + * + * 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 + * + * This version of ash is adapted from the source in Debian's ash 0.3.8-5 + * package. + * + * Modified by Erik Andersen and + * Vladimir Oleynik to be used in busybox + * + * + * Original copyright notice is retained at the end of this file. + */ + + +/* These defines allow you to adjust the feature set to be compiled + * into the ash shell. As a rule, enabling these options will make + * ash get bigger... With all of these options off, ash adds about + * 60k to busybox on an x86 system.*/ + + +/* Enable job control. This allows you to run jobs in the background, + * which is great when ash is being used as an interactive shell, but + * it completely useless for is all you are doing is running scripts. + * This adds about 2.5k on an x86 system. */ +#undef JOBS + +/* This enables alias support in ash. If you want to support things + * like "alias ls='ls -l'" with ash, enable this. This is only useful + * when ash is used as an intractive shell. This adds about 1.5k */ +#define ASH_ALIAS + +/* If you need ash to act as a full Posix shell, with full math + * support, enable this. This adds a bit over 2k an x86 system. */ +//#undef ASH_MATH_SUPPORT +#define ASH_MATH_SUPPORT + +/* Getopts is used by shell procedures to parse positional parameters. + * You probably want to leave this disabled, and use the busybox getopt + * applet if you want to do this sort of thing. There are some scripts + * out there that use it, so it you need it, enable. Most people will + * leave this disabled. This adds 1k on an x86 system. */ +#undef ASH_GETOPTS + +/* This allows you to override shell builtins and use whatever is on + * the filesystem. This is most useful when ash is acting as a + * standalone shell. Adds about 272 bytes. */ +#undef ASH_CMDCMD + + +/* Optimize size vs speed as size */ +#define ASH_OPTIMIZE_FOR_SIZE + +/* Enable this to compile in extra debugging noise. When debugging is + * on, debugging info will be written to $HOME/trace and a quit signal + * will generate a core dump. */ +#undef DEBUG + +/* These are here to work with glibc -- Don't change these... */ +#undef FNMATCH_BROKEN +#undef GLOB_BROKEN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if !defined(FNMATCH_BROKEN) +#include +#endif +#if !defined(GLOB_BROKEN) +#include +#endif + +#ifdef JOBS +#include +#endif + +#include "busybox.h" +#include "cmdedit.h" + +/* + * 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 */ + +/* Syntax classes for is_ functions */ +#define ISDIGIT 01 /* a digit */ +#define ISUPPER 02 /* an upper case letter */ +#define ISLOWER 04 /* a lower case letter */ +#define ISUNDER 010 /* an underscore */ +#define ISSPECL 020 /* the name of a special parameter */ + +#define SYNBASE 130 +#define PEOF -130 + +#define PEOA -129 + +#define TEOF 0 +#define TNL 1 +#define TSEMI 2 +#define TBACKGND 3 +#define TAND 4 +#define TOR 5 +#define TPIPE 6 +#define TLP 7 +#define TRP 8 +#define TENDCASE 9 +#define TENDBQUOTE 10 +#define TREDIR 11 +#define TWORD 12 +#define TASSIGN 13 +#define TNOT 14 +#define TCASE 15 +#define TDO 16 +#define TDONE 17 +#define TELIF 18 +#define TELSE 19 +#define TESAC 20 +#define TFI 21 +#define TFOR 22 +#define TIF 23 +#define TIN 24 +#define TTHEN 25 +#define TUNTIL 26 +#define TWHILE 27 +#define TBEGIN 28 +#define TEND 29 + + +#define BASESYNTAX (basesyntax + SYNBASE) +#define DQSYNTAX (dqsyntax + SYNBASE) +#define SQSYNTAX (sqsyntax + SYNBASE) +#define ARISYNTAX (arisyntax + SYNBASE) + +/* control characters in argument strings */ +#define CTLESC '\201' +#define CTLVAR '\202' +#define CTLENDVAR '\203' +#define CTLBACKQ '\204' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ +/* CTLBACKQ | CTLQUOTE == '\205' */ +#define CTLARI '\206' +#define CTLENDARI '\207' +#define CTLQUOTEMARK '\210' + +#define is_digit(c) ((c)>='0' && (c)<='9') +#define is_alpha(c) (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c))) +#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c)))) +#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c)))) +#define is_special(c) ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT)) +#define digit_val(c) ((c) - '0') + + +#define _DIAGASSERT(x) + + + +#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 */ + + +/* 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 VSTRIMLEFT 0x6 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */ +#define VSTRIMRIGHT 0x8 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ +#define VSLENGTH 0xa /* ${#var} */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output to pipe */ + +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ + +#if defined(BSD) +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) +#endif + +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ + +#ifndef ALIGN +union align { + int i; + char *cp; +}; + +#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1)) +#endif + +#ifdef BB_LOCALE_SUPPORT +#include +static void change_lc_all(const char *value); +static void change_lc_ctype(const char *value); +#endif + +/* + * 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. :-)) + */ + +static void onint (void); +static volatile int suppressint; +static volatile int intpending; + +#define INTOFF suppressint++ +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define INTON { if (--suppressint == 0 && intpending) onint(); } +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#else +static void __inton (void); +static void forceinton (void); +#define INTON __inton() +#define FORCEINTON forceinton() +#endif + +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + + +typedef void *pointer; +#ifndef NULL +#define NULL (void *)0 +#endif + +static inline pointer ckmalloc (int sz) { return xmalloc(sz); } +static inline pointer ckrealloc(void *p, int sz) { return xrealloc(p, sz); } +static inline char * savestr (const char *s) { return xstrdup(s); } + +static pointer stalloc (int); +static void stunalloc (pointer); +static void ungrabstackstr (char *, char *); +static char * growstackstr(void); +static char * makestrspace(size_t newlen); +static char *sstrdup (const char *); + +/* + * 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. + */ + +#define MINSIZE 504 /* minimum size of a block */ + + +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 int stacknleft = MINSIZE; + + +#define equal(s1, s2) (strcmp(s1, s2) == 0) + +#define stackblock() stacknxt +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() + +#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) +#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(n); } +#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0')) + + +#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) +#define STUNPUTC(p) (++sstrnleft, --p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) +#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) + +#define ckfree(p) free((pointer)(p)) + + +#ifdef DEBUG +#define TRACE(param) trace param +static void trace (const char *, ...); +static void trargs (char **); +static void showtree (union node *); +static void trputc (int); +static void trputs (const char *); +static void opentrace (void); +#else +#define TRACE(param) +#endif + +#define NSEMI 0 +#define NCMD 1 +#define NPIPE 2 +#define NREDIR 3 +#define NBACKGND 4 +#define NSUBSHELL 5 +#define NAND 6 +#define NOR 7 +#define NIF 8 +#define NWHILE 9 +#define NUNTIL 10 +#define NFOR 11 +#define NCASE 12 +#define NCLIST 13 +#define NDEFUN 14 +#define NARG 15 +#define NTO 16 +#define NFROM 17 +#define NFROMTO 18 +#define NAPPEND 19 +#define NTOOV 20 +#define NTOFD 21 +#define NFROMFD 22 +#define NHERE 23 +#define NXHERE 24 +#define NNOT 25 + +/* + * 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 NOPTS 16 + +static char optet_vals[NOPTS]; + +static const char * const optlist[NOPTS] = { + "e" "errexit", + "f" "noglob", + "I" "ignoreeof", + "i" "interactive", + "m" "monitor", + "n" "noexec", + "s" "stdin", + "x" "xtrace", + "v" "verbose", + "V" "vi", + "E" "emacs", + "C" "noclobber", + "a" "allexport", + "b" "notify", + "u" "nounset", + "q" "quietprofile" +}; + +#define optent_name(optent) (optent+1) +#define optent_letter(optent) optent[0] +#define optent_val(optent) optet_vals[optent] + +#define eflag optent_val(0) +#define fflag optent_val(1) +#define Iflag optent_val(2) +#define iflag optent_val(3) +#define mflag optent_val(4) +#define nflag optent_val(5) +#define sflag optent_val(6) +#define xflag optent_val(7) +#define vflag optent_val(8) +#define Vflag optent_val(9) +#define Eflag optent_val(10) +#define Cflag optent_val(11) +#define aflag optent_val(12) +#define bflag optent_val(13) +#define uflag optent_val(14) +#define qflag optent_val(15) + + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + + +struct nbinary { + int type; + union node *ch1; + union node *ch2; +}; + + +struct ncmd { + int type; + int backgnd; + 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 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 nbinary nbinary; + struct ncmd ncmd; + struct npipe npipe; + struct nredir nredir; + struct nif nif; + struct nfor nfor; + struct ncase ncase; + struct nclist nclist; + struct narg narg; + struct nfile nfile; + struct ndup ndup; + struct nhere nhere; + struct nnot nnot; +}; + + +struct nodelist { + struct nodelist *next; + union node *n; +}; + +struct 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 */ +}; + +struct cmdentry { + int cmdtype; + union param { + int index; + union node *func; + const struct builtincmd *cmd; + } u; +}; + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; +#ifdef 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 */ +}; + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; + struct stackmark *marknext; +}; + +struct shparam { + int nparam; /* # of positional parameters (without $0) */ + unsigned char malloc; /* if parameter list dynamically allocated */ + char **p; /* parameter list */ + int optind; /* next parameter to be processed by getopts */ + int optoff; /* used by getopts */ +}; + +/* + * 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 int exerrno = 0; /* Last exec error */ + + +static void tryexec (char *, char **, char **); +static void printentry (struct tblentry *, int); +static void clearcmdentry (int); +static struct tblentry *cmdlookup (const char *, int); +static void delete_cmd_entry (void); +static int path_change (const char *, int *); + + +static void flushall (void); +static void out2fmt (const char *, ...) + __attribute__((__format__(__printf__,1,2))); +static int xwrite (int, const char *, int); + +static void outstr (const char *p, FILE *file) { fputs(p, file); } +static void out1str(const char *p) { outstr(p, stdout); } +static void out2str(const char *p) { outstr(p, stderr); } + +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define out2c(c) putc((c), stderr) +#else +static void out2c(int c) { putc(c, stderr); } +#endif + +/* syntax table used when not in quotes */ +static const char basesyntax[257] = { + CENDFILE, CSPCL, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CSPCL, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CSPCL, CWORD, + CDQUOTE, CWORD, CVAR, CWORD, + CSPCL, CSQUOTE, CSPCL, CSPCL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CSPCL, CSPCL, CWORD, + CSPCL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CBACK, CWORD, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CSPCL, CENDVAR, + CWORD +}; + +/* syntax table used when in double quotes */ +static const char dqsyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CCTL, + CENDQUOTE,CWORD, CVAR, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CCTL, CBACK, CCTL, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CENDVAR, + CCTL +}; + +/* syntax table used when in single quotes */ +static const char sqsyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CCTL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CENDQUOTE,CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CCTL, CCTL, CCTL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL +}; + +/* syntax table used when in arithmetic */ +static const char arisyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CDQUOTE, CWORD, CVAR, CWORD, + CWORD, CSQUOTE, CLP, CRP, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CBACK, CWORD, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CENDVAR, + CWORD +}; + +/* character classification table */ +static const char is_type[257] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, ISSPECL, + 0, ISSPECL, ISSPECL, 0, + 0, 0, 0, 0, + ISSPECL, 0, 0, ISSPECL, + 0, 0, ISDIGIT, ISDIGIT, + ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, + ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, + 0, 0, 0, 0, + 0, ISSPECL, ISSPECL, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, 0, 0, 0, + 0, ISUNDER, 0, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, 0, 0, 0, + 0 +}; + +/* Array indicating which tokens mark the end of a list */ +static const char tokendlist[] = { + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, +}; + +static const char *const tokname[] = { + "end of file", + "newline", + "\";\"", + "\"&\"", + "\"&&\"", + "\"||\"", + "\"|\"", + "\"(\"", + "\")\"", + "\";;\"", + "\"`\"", + "redirection", + "word", + "assignment", + "\"!\"", + "\"case\"", + "\"do\"", + "\"done\"", + "\"elif\"", + "\"else\"", + "\"esac\"", + "\"fi\"", + "\"for\"", + "\"if\"", + "\"in\"", + "\"then\"", + "\"until\"", + "\"while\"", + "\"{\"", + "\"}\"", +}; + +#define KWDOFFSET 14 + +static const char *const parsekwd[] = { + "!", + "case", + "do", + "done", + "elif", + "else", + "esac", + "fi", + "for", + "if", + "in", + "then", + "until", + "while", + "{", + "}" +}; + + +static int plinno = 1; /* input line number */ + +static int parselleft; /* copy of parsefile->lleft */ + +static struct parsefile basepf; /* top level input file */ +static char basebuf[BUFSIZ]; /* buffer for top level input file */ +static struct parsefile *parsefile = &basepf; /* current input file */ + +/* + * 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 tokpushback; /* last token pushed back */ +#define NEOF ((union node *)&tokpushback) +static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ + + +static void error (const char *, ...) __attribute__((__noreturn__)); +static void exerror (int, const char *, ...) __attribute__((__noreturn__)); +static void shellexec (char **, char **, const char *, int) + __attribute__((noreturn)); +static void exitshell (int) __attribute__((noreturn)); + +static int goodname(const char *); +static void ignoresig (int); +static void onsig (int); +static void dotrap (void); +static int decode_signal (const char *, int); + +static void shprocvar(void); +static void deletefuncs(void); +static void setparam (char **); +static void freeparam (volatile struct shparam *); + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 +#define SKIPFILE 4 + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDBUILTIN 1 /* command is a shell builtin */ +#define CMDFUNCTION 2 /* command is a shell function */ + +#define DO_ERR 1 /* find_command prints errors */ +#define DO_ABS 2 /* find_command checks absolute paths */ +#define DO_NOFUN 4 /* find_command ignores functions */ +#define DO_BRUTE 8 /* find_command ignores hash table */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is staticly allocated */ +#define VTEXTFIXED 0x08 /* text is staticly 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 */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + 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 */ + char *text; /* saved text */ +}; + + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +#define rmescapes(p) _rmescapes((p), 0) +static char *_rmescapes (char *, int); +#else +static void rmescapes (char *); +#endif + +static int casematch (union node *, const char *); +static void clearredir(void); +static void popstring(void); +static void readcmdfile (const char *); + +static int number (const char *); +static int is_number (const char *, int *num); +static char *single_quote (const char *); +static int nextopt (const char *); + +static void redirect (union node *, int); +static void popredir (void); +static int dup_as_newfd (int, int); + +static void changepath(const char *newval); +static void getoptsreset(const char *value); + + +static int parsenleft; /* copy of parsefile->nleft */ +static char *parsenextc; /* copy of parsefile->nextc */ +static int rootpid; /* pid of main shell */ +static int rootshell; /* true if we aren't a child of the main shell */ + +static const char spcstr[] = " "; +static const char snlfmt[] = "%s\n"; + +static int sstrnleft; +static int herefd = -1; + +static struct localvar *localvars; + +static struct var vifs; +static struct var vmail; +static struct var vmpath; +static struct var vpath; +static struct var vps1; +static struct var vps2; +static struct var voptind; +#ifdef BB_LOCALE_SUPPORT +static struct var vlc_all; +static struct var vlc_ctype; +#endif + +struct varinit { + struct var *var; + int flags; + const char *text; + void (*func) (const char *); +}; + +static const char defpathvar[] = + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; +#define defpath (defpathvar + 5) + +#ifdef IFS_BROKEN +static const char defifsvar[] = "IFS= \t\n"; +#define defifs (defifsvar + 4) +#else +static const char defifs[] = " \t\n"; +#endif + +static const struct varinit varinit[] = { +#ifdef IFS_BROKEN + { &vifs, VSTRFIXED|VTEXTFIXED, defifsvar, +#else + { &vifs, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=", +#endif + NULL }, + { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", + NULL }, + { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", + NULL }, + { &vpath, VSTRFIXED|VTEXTFIXED, defpathvar, + changepath }, + /* + * vps1 depends on uid + */ + { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", + NULL }, + { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", + getoptsreset }, +#ifdef BB_LOCALE_SUPPORT + { &vlc_all, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL=", + change_lc_all }, + { &vlc_ctype, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE=", + change_lc_ctype }, +#endif + { NULL, 0, NULL, + NULL } +}; + +#define VTABSIZE 39 + +static struct var *vartab[VTABSIZE]; + +/* + * 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 optindval() (voptind.text + 7) + +#define mpathset() ((vmpath.flags & VUNSET) == 0) + +static void initvar (void); +static void setvar (const char *, const char *, int); +static void setvareq (char *, int); +static void listsetvar (struct strlist *); +static const char *lookupvar (const char *); +static const char *bltinlookup (const char *); +static char **environment (void); +static int showvarscmd (int, char **); +static void mklocal (char *); +static void poplocalvars (void); +static int unsetvar (const char *); +static int varequal (const char *, const char *); + + +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 */ + + +#ifdef ASH_ALIAS + +#define ALIASINUSE 1 +#define ALIASDEAD 2 + +#define ATABSIZE 39 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +static struct alias *atab[ATABSIZE]; + +static void setalias (char *, char *); +static struct alias **hashalias (const char *); +static struct alias *freealias (struct alias *); +static struct alias **__lookupalias (const char *); + +static void +setalias(name, val) + char *name, *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(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); +} + +static void +printalias(const struct alias *ap) { + char *p; + + p = single_quote(ap->val); + printf("alias %s=%s\n", ap->name, p); + stunalloc(p); +} + + +/* + * 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) { + out2fmt("%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)) { + out2fmt("%s: %s not found\n", "unalias", *argptr); + i = 1; + } + } + + return (i); +} + +static struct alias ** +hashalias(p) + const char *p; + { + unsigned int hashval; + + hashval = *p << 4; + while (*p) + hashval+= *p++; + return &atab[hashval % ATABSIZE]; +} + +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 struct alias ** +__lookupalias(const char *name) { + struct alias **app = hashalias(name); + + for (; *app; app = &(*app)->next) { + if (equal(name, (*app)->name)) { + break; + } + } + + return app; +} +#endif + +#ifdef ASH_MATH_SUPPORT +/* The generated file arith.c has been replaced with a custom hand + * written implementation written by Aaron Lehmann . + * This is now part of libbb, so that it can be used by all the shells + * in busybox. */ +#define ARITH_NUM 257 +#define ARITH_LPAREN 258 +#define ARITH_RPAREN 259 +#define ARITH_OR 260 +#define ARITH_AND 261 +#define ARITH_BOR 262 +#define ARITH_BXOR 263 +#define ARITH_BAND 264 +#define ARITH_EQ 265 +#define ARITH_NE 266 +#define ARITH_LT 267 +#define ARITH_GT 268 +#define ARITH_GE 269 +#define ARITH_LE 270 +#define ARITH_LSHIFT 271 +#define ARITH_RSHIFT 272 +#define ARITH_ADD 273 +#define ARITH_SUB 274 +#define ARITH_MUL 275 +#define ARITH_DIV 276 +#define ARITH_REM 277 +#define ARITH_UNARYMINUS 278 +#define ARITH_UNARYPLUS 279 +#define ARITH_NOT 280 +#define ARITH_BNOT 281 + +static void expari (int); +#endif + +static char *trap[NSIG]; /* trap handler commands */ +static char sigmode[NSIG - 1]; /* current value of signal */ +static char gotsig[NSIG - 1]; /* indicates specified signal received */ +static int pendingsigs; /* indicates some signal received */ + +/* + * This file was generated by the mkbuiltins program. + */ + +#ifdef JOBS +static int bgcmd (int, char **); +static int fgcmd (int, char **); +static int killcmd (int, char **); +#endif +static int bltincmd (int, char **); +static int cdcmd (int, char **); +static int breakcmd (int, char **); +#ifdef 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 histcmd (int, char **); +static int hashcmd (int, char **); +static int helpcmd (int, char **); +static int jobscmd (int, char **); +static int localcmd (int, char **); +#ifndef BB_PWD +static int pwdcmd (int, char **); +#endif +static int readcmd (int, char **); +static int returncmd (int, char **); +static int setcmd (int, char **); +static int setvarcmd (int, char **); +static int shiftcmd (int, char **); +static int trapcmd (int, char **); +static int umaskcmd (int, char **); +#ifdef ASH_ALIAS +static int aliascmd (int, char **); +static int unaliascmd (int, char **); +#endif +static int unsetcmd (int, char **); +static int waitcmd (int, char **); +static int ulimitcmd (int, char **); +static int timescmd (int, char **); +#ifdef ASH_MATH_SUPPORT +static int letcmd (int, char **); +#endif +static int typecmd (int, char **); +#ifdef ASH_GETOPTS +static int getoptscmd (int, char **); +#endif + +#ifndef BB_TRUE_FALSE +static int true_main (int, char **); +static int false_main (int, char **); +#endif + +static void setpwd (const char *, int); + + +#define BUILTIN_NOSPEC "0" +#define BUILTIN_SPECIAL "1" +#define BUILTIN_REGULAR "2" +#define BUILTIN_ASSIGN "4" +#define BUILTIN_SPEC_ASSG "5" +#define BUILTIN_REG_ASSG "6" + +#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1) +#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2) +#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4) + +struct builtincmd { + const char *name; + int (*const builtinfunc) (int, char **); + //unsigned flags; +}; + + +/* It is CRUCIAL that this listing be kept in ascii order, otherwise + * the binary search in find_builtin() will stop working. If you value + * your kneecaps, you'll be sure to *make sure* that any changes made + * to this array result in the listing remaining in ascii order. You + * have been warned. + */ +static const struct builtincmd builtincmds[] = { + { BUILTIN_SPECIAL ".", dotcmd }, /* first, see declare DOTCMD */ + { BUILTIN_SPECIAL ":", true_main }, +#ifdef ASH_ALIAS + { BUILTIN_REG_ASSG "alias", aliascmd }, +#endif +#ifdef JOBS + { BUILTIN_REGULAR "bg", bgcmd }, +#endif + { BUILTIN_SPECIAL "break", breakcmd }, + { BUILTIN_SPECIAL "builtin", bltincmd }, + { BUILTIN_REGULAR "cd", cdcmd }, + { BUILTIN_NOSPEC "chdir", cdcmd }, +#ifdef ASH_CMDCMD + { BUILTIN_REGULAR "command", commandcmd }, +#endif + { BUILTIN_SPECIAL "continue", breakcmd }, + { BUILTIN_SPECIAL "eval", evalcmd }, + { BUILTIN_SPECIAL "exec", execcmd }, + { BUILTIN_SPECIAL "exit", exitcmd }, + { BUILTIN_SPEC_ASSG "export", exportcmd }, + { BUILTIN_REGULAR "false", false_main }, + { BUILTIN_REGULAR "fc", histcmd }, +#ifdef JOBS + { BUILTIN_REGULAR "fg", fgcmd }, +#endif +#ifdef ASH_GETOPTS + { BUILTIN_REGULAR "getopts", getoptscmd }, +#endif + { BUILTIN_NOSPEC "hash", hashcmd }, + { BUILTIN_NOSPEC "help", helpcmd }, + { BUILTIN_REGULAR "jobs", jobscmd }, +#ifdef JOBS + { BUILTIN_REGULAR "kill", killcmd }, +#endif +#ifdef ASH_MATH_SUPPORT + { BUILTIN_REGULAR "let", letcmd }, +#endif + { BUILTIN_ASSIGN "local", localcmd }, +#ifndef BB_PWD + { BUILTIN_NOSPEC "pwd", pwdcmd }, +#endif + { BUILTIN_REGULAR "read", readcmd }, + { BUILTIN_SPEC_ASSG "readonly", exportcmd }, + { BUILTIN_SPECIAL "return", returncmd }, + { BUILTIN_SPECIAL "set", setcmd }, + { BUILTIN_NOSPEC "setvar", setvarcmd }, + { BUILTIN_SPECIAL "shift", shiftcmd }, + { BUILTIN_SPECIAL "times", timescmd }, + { BUILTIN_SPECIAL "trap", trapcmd }, + { BUILTIN_REGULAR "true", true_main }, + { BUILTIN_NOSPEC "type", typecmd }, + { BUILTIN_NOSPEC "ulimit", ulimitcmd }, + { BUILTIN_REGULAR "umask", umaskcmd }, +#ifdef ASH_ALIAS + { BUILTIN_REGULAR "unalias", unaliascmd }, +#endif + { BUILTIN_SPECIAL "unset", unsetcmd }, + { BUILTIN_REGULAR "wait", waitcmd }, +}; +#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) ) + +static const struct builtincmd *DOTCMD = &builtincmds[0]; +static struct builtincmd *BLTINCMD; +static struct builtincmd *EXECCMD; +static struct builtincmd *EVALCMD; + +/* states */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ + +/* + * 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; /* status flags (defined above) */ + char *cmd; /* text of command being run */ +}; + + +static int job_warning; /* user was warned about stopped jobs */ + +#ifdef JOBS +static void setjobctl(int enable); +#else +#define setjobctl(on) /* do nothing */ +#endif + + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ + short nprocs; /* number of processes */ + short pgrp; /* process group of this job */ + char state; /* true if job is finished */ + char used; /* true if this entry is in used */ + char changed; /* true if status has changed */ +#ifdef JOBS + char jobctl; /* job running under job control */ +#endif +}; + +static struct job *jobtab; /* array of jobs */ +static int njobs; /* size of array */ +static int backgndpid = -1; /* pid of last background process */ +#ifdef JOBS +static int initialpgrp; /* pgrp of shell on invocation */ +static int curjob; /* current job */ +static int jobctl; +#endif +static int intreceived; + +static struct job *makejob (const union node *, int); +static int forkshell (struct job *, const union node *, int); +static int waitforjob (struct job *); + +static int docd (char *, int); +static char *getcomponent (void); +static void updatepwd (const char *); +static void getpwd (void); + +static char *padvance (const char **, const char *); + +static char nullstr[1]; /* zero length string */ +static char *curdir = nullstr; /* current working directory */ +static char *cdcomppath; + +static int +cdcmd(argc, argv) + int argc; + char **argv; +{ + const char *dest; + const char *path; + char *p; + struct stat statb; + int print = 0; + + nextopt(nullstr); + if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL) + error("HOME not set"); + if (*dest == '\0') + dest = "."; + if (dest[0] == '-' && dest[1] == '\0') { + dest = bltinlookup("OLDPWD"); + if (!dest || !*dest) { + dest = curdir; + } + print = 1; + if (dest) + print = 1; + else + dest = "."; + } + if (*dest == '/' || (path = bltinlookup("CDPATH")) == NULL) + path = nullstr; + while ((p = padvance(&path, dest)) != NULL) { + if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { + if (!print) { + /* + * XXX - rethink + */ + if (p[0] == '.' && p[1] == '/' && p[2] != '\0') + p += 2; + print = strcmp(p, dest); + } + if (docd(p, print) >= 0) + return 0; + + } + } + error("can't cd to %s", dest); + /* NOTREACHED */ +} + + +/* + * Actually do the chdir. In an interactive shell, print the + * directory name if "print" is nonzero. + */ + +static int +docd(dest, print) + char *dest; + int print; +{ + char *p; + char *q; + char *component; + struct stat statb; + int first; + int badstat; + + TRACE(("docd(\"%s\", %d) called\n", dest, print)); + + /* + * Check each component of the path. If we find a symlink or + * something we can't stat, clear curdir to force a getcwd() + * next time we get the value of the current directory. + */ + badstat = 0; + cdcomppath = sstrdup(dest); + STARTSTACKSTR(p); + if (*dest == '/') { + STPUTC('/', p); + cdcomppath++; + } + first = 1; + while ((q = getcomponent()) != NULL) { + if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) + continue; + if (! first) + STPUTC('/', p); + first = 0; + component = q; + while (*q) + STPUTC(*q++, p); + if (equal(component, "..")) + continue; + STACKSTRNUL(p); + if ((lstat(stackblock(), &statb) < 0) + || (S_ISLNK(statb.st_mode))) { + /* print = 1; */ + badstat = 1; + break; + } + } + + INTOFF; + if (chdir(dest) < 0) { + INTON; + return -1; + } + updatepwd(badstat ? NULL : dest); + INTON; + if (print && iflag) + printf(snlfmt, curdir); + return 0; +} + + +/* + * Get the next component of the path name pointed to by cdcomppath. + * This routine overwrites the string pointed to by cdcomppath. + */ + +static char * +getcomponent() { + char *p; + char *start; + + if ((p = cdcomppath) == NULL) + return NULL; + start = cdcomppath; + while (*p != '/' && *p != '\0') + p++; + if (*p == '\0') { + cdcomppath = NULL; + } else { + *p++ = '\0'; + cdcomppath = p; + } + return start; +} + + + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. We also call hashcd to let the routines in exec.c know + * that the current directory has changed. + */ + +static void hashcd (void); + +static void +updatepwd(const char *dir) +{ + char *new; + char *p; + size_t len; + + hashcd(); /* update command hash table */ + + /* + * If our argument is NULL, we don't know the current directory + * any more because we traversed a symbolic link or something + * we couldn't stat(). + */ + if (dir == NULL || curdir == nullstr) { + setpwd(0, 1); + return; + } + len = strlen(dir); + cdcomppath = sstrdup(dir); + STARTSTACKSTR(new); + if (*dir != '/') { + p = curdir; + while (*p) + STPUTC(*p++, new); + if (p[-1] == '/') + STUNPUTC(new); + } + while ((p = getcomponent()) != NULL) { + if (equal(p, "..")) { + while (new > stackblock() && (STUNPUTC(new), *new) != '/'); + } else if (*p != '\0' && ! equal(p, ".")) { + STPUTC('/', new); + while (*p) + STPUTC(*p++, new); + } + } + if (new == stackblock()) + STPUTC('/', new); + STACKSTRNUL(new); + setpwd(stackblock(), 1); +} + + +#ifndef BB_PWD +static int +pwdcmd(argc, argv) + int argc; + char **argv; +{ + printf(snlfmt, curdir); + return 0; +} +#endif + +/* + * Find out what the current directory is. If we already know the current + * directory, this routine returns immediately. + */ +static void +getpwd(void) +{ + curdir = xgetcwd(0); + if(curdir==0) + curdir = nullstr; +} + +static void +setpwd(const char *val, int setold) +{ + if (setold) { + setvar("OLDPWD", curdir, VEXPORT); + } + INTOFF; + if (curdir != nullstr) { + free(curdir); + curdir = nullstr; + } + if (!val) { + getpwd(); + } else { + curdir = savestr(val); + } + INTON; + setvar("PWD", curdir, VEXPORT); +} + +/* + * Errors and exceptions. + */ + +/* + * Code to handle exceptions in C. + */ + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exeception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +struct jmploc { + jmp_buf loc; +}; + +/* 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 */ + +static struct jmploc *handler; +static int exception; + +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) __attribute__((__noreturn__)); + +static void +exraise(int e) +{ +#ifdef DEBUG + if (handler == NULL) + abort(); +#endif + flushall(); + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. The call to _exit is necessary because + * there is a short period after a fork before the signal handlers are + * set to the appropriate value for the child. (The test for iflag is + * just defensive programming.) + */ + +static void +onint(void) { + sigset_t mysigset; + + if (suppressint) { + intpending++; + return; + } + intpending = 0; + sigemptyset(&mysigset); + sigprocmask(SIG_SETMASK, &mysigset, NULL); + if (rootshell && iflag) + exraise(EXINT); + else { + signal(SIGINT, SIG_DFL); + raise(SIGINT); + } + /* NOTREACHED */ +} + + +static char *commandname; /* currently executing command */ + +/* + * Exverror is called to raise the error exception. If the first argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ +static void +exverror(int cond, const char *msg, va_list ap) +{ + CLEAR_PENDING_INT; + INTOFF; + +#ifdef DEBUG + if (msg) + TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid())); + else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); +#endif + if (msg) { + if (commandname) + out2fmt("%s: ", commandname); + vfprintf(stderr, msg, ap); + out2c('\n'); + } + 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); +} + + + +/* + * Table of error messages. + */ + +struct errname { + short errcode; /* error number */ + char action; /* operation which encountered the error */ +}; + +/* + * Types of operations (passed to the errmsg routine). + */ + +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + +static const struct errname errormsg[] = { + { EINTR, ALL }, + { EACCES, ALL }, + { EIO, ALL }, + { ENOENT, E_OPEN }, + { ENOENT, E_CREAT }, + { ENOENT, E_EXEC }, + { ENOTDIR, E_OPEN }, + { ENOTDIR, E_CREAT }, + { ENOTDIR, E_EXEC }, + { EISDIR, ALL }, + { EEXIST, E_CREAT }, +#ifdef EMFILE + { EMFILE, ALL }, +#endif + { ENFILE, ALL }, + { ENOSPC, ALL }, +#ifdef EDQUOT + { EDQUOT, ALL }, +#endif +#ifdef ENOSR + { ENOSR, ALL }, +#endif + { ENXIO, ALL }, + { EROFS, ALL }, + { ETXTBSY, ALL }, +#ifdef EAGAIN + { EAGAIN, E_EXEC }, +#endif + { ENOMEM, ALL }, +#ifdef ENOLINK + { ENOLINK, ALL }, +#endif +#ifdef EMULTIHOP + { EMULTIHOP, ALL }, +#endif +#ifdef ECOMM + { ECOMM, ALL }, +#endif +#ifdef ESTALE + { ESTALE, ALL }, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, ALL }, +#endif +#ifdef ELOOP + { ELOOP, ALL }, +#endif + { E2BIG, E_EXEC }, +#ifdef ELIBACC + { ELIBACC, E_EXEC }, +#endif +}; + +#define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname)) + +/* + * 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, int action) +{ + struct errname const *ep; + static char buf[12]; + + for (ep = errormsg ; ep < errormsg+ERRNAME_SIZE; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return strerror(e); + } + + snprintf(buf, sizeof buf, "error %d", e); + return buf; +} + + +#ifdef ASH_OPTIMIZE_FOR_SIZE +static void +__inton() { + if (--suppressint == 0 && intpending) { + onint(); + } +} +static void forceinton (void) { + suppressint = 0; + if (intpending) + onint(); +} +#endif + +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_BACKCMD 04 /* command executing within back quotes */ + +static int evalskip; /* set if we are skipping commands */ +static int skipcount; /* number of levels to skip */ +static int loopnest; /* current loop nesting level */ +static int funcnest; /* depth of function calls */ + + +static struct strlist *cmdenviron; /* environment for builtin command */ +static int exitstatus; /* exit status of last command */ +static int oexitstatus; /* saved exit status */ + +static void evalsubshell (const union node *, int); +static void expredir (union node *); +static void prehash (union node *); +static void eprintlist (struct strlist *); + +static union node *parsecmd(int); +/* + * Called to reset things after an exception. + */ + +/* + * The eval commmand. + */ +static void evalstring (char *, int); + +static int +evalcmd(argc, argv) + int argc; + char **argv; +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + evalstring(p, EV_TESTED); + } + return exitstatus; +} + +/* + * Execute a command or commands contained in a string. + */ + +static void evaltree (union node *, int); +static void setinputstring (char *); +static void popfile (void); +static void setstackmark(struct stackmark *mark); +static void popstackmark(struct stackmark *mark); + + +static void +evalstring(char *s, int flag) +{ + union node *n; + struct stackmark smark; + + setstackmark(&smark); + setinputstring(s); + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, flag); + popstackmark(&smark); + } + popfile(); + popstackmark(&smark); +} + +static struct builtincmd *find_builtin (const char *); +static void expandarg (union node *, struct arglist *, int); +static void calcsize (const union node *); +static union node *copynode (const union node *); + +/* + * Make a copy of a parse tree. + */ + +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 inline union node * +copyfunc(union node *n) +{ + if (n == NULL) + return NULL; + funcblocksize = 0; + funcstringsize = 0; + calcsize(n); + funcblock = ckmalloc(funcblocksize + funcstringsize); + funcstring = (char *) funcblock + funcblocksize; + return copynode(n); +} + +/* + * Free a parse tree. + */ + +static void +freefunc(union node *n) +{ + if (n) + ckfree(n); +} + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name. + */ + +static inline void +addcmdentry(char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp; + + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + INTON; +} + +static inline void +evalloop(const union node *n, int flags) +{ + int status; + + loopnest++; + status = 0; + for (;;) { + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { +skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + if (n->type == NWHILE) { + if (exitstatus != 0) + break; + } else { + if (exitstatus == 0) + break; + } + evaltree(n->nbinary.ch2, flags & EV_TESTED); + status = exitstatus; + if (evalskip) + goto skipping; + } + loopnest--; + exitstatus = status; +} + +static void +evalfor(const 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) { + oexitstatus = exitstatus; + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); + if (evalskip) + goto out; + } + *arglist.lastp = NULL; + + exitstatus = 0; + loopnest++; + for (sp = arglist.list ; sp ; sp = sp->next) { + setvar(n->nfor.var, sp->text, 0); + evaltree(n->nfor.body, flags & EV_TESTED); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; +out: + popstackmark(&smark); +} + +static inline void +evalcase(const union node *n, int flags) +{ + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + oexitstatus = exitstatus; + expandarg(n->ncase.expr, &arglist, EXP_TILDE); + for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.list->text)) { + if (evalskip == 0) { + evaltree(cp->nclist.body, flags); + } + goto out; + } + } + } +out: + popstackmark(&smark); +} + +/* + * 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 inline void +evalpipe(n) + union node *n; +{ + 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++; + 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 (prevfd > 0) { + close(0); + dup_as_newfd(prevfd, 0); + close(prevfd); + if (pip[0] == 0) { + pip[0] = -1; + } + } + if (pip[1] >= 0) { + if (pip[0] >= 0) { + close(pip[0]); + } + if (pip[1] != 1) { + close(1); + dup_as_newfd(pip[1], 1); + close(pip[1]); + } + } + evaltree(lp->n, EV_EXIT); + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + INTON; + if (n->npipe.backgnd == 0) { + INTOFF; + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + INTON; + } +} + +static void find_command (const char *, struct cmdentry *, int, const char *); + +static int +isassignment(const char *word) { + if (!is_name(*word)) { + return 0; + } + do { + word++; + } while (is_in_name(*word)); + return *word == '='; +} + + +static void +evalcommand(union node *cmd, int flags) +{ + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + char **envp; + struct strlist *sp; + int mode; + struct cmdentry cmdentry; + struct job *jp; + char *volatile savecmdname; + volatile struct shparam saveparam; + struct localvar *volatile savelocalvars; + volatile int e; + char *lastarg; + const char *path; + const struct builtincmd *firstbltin; + struct jmploc *volatile savehandler; + struct jmploc jmploc; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &argv; + (void) &argc; + (void) &lastarg; + (void) &flags; +#endif + + /* First expand the arguments. */ + TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); + setstackmark(&smark); + arglist.lastp = &arglist.list; + varlist.lastp = &varlist.list; + arglist.list = 0; + oexitstatus = exitstatus; + exitstatus = 0; + path = pathval(); + for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { + expandarg(argp, &varlist, EXP_VARTILDE); + } + for ( + argp = cmd->ncmd.args; argp && !arglist.list; + argp = argp->narg.next + ) { + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + } + if (argp) { + struct builtincmd *bcmd; + int pseudovarflag; + bcmd = find_builtin(arglist.list->text); + pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd); + for (; argp; argp = argp->narg.next) { + if (pseudovarflag && isassignment(argp->narg.text)) { + expandarg(argp, &arglist, EXP_VARTILDE); + continue; + } + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + } + } + *arglist.lastp = NULL; + *varlist.lastp = NULL; + expredir(cmd->ncmd.redirect); + argc = 0; + for (sp = arglist.list ; sp ; sp = sp->next) + argc++; + argv = stalloc(sizeof (char *) * (argc + 1)); + + for (sp = arglist.list ; sp ; sp = sp->next) { + TRACE(("evalcommand arg: %s\n", sp->text)); + *argv++ = sp->text; + } + *argv = NULL; + lastarg = NULL; + if (iflag && funcnest == 0 && argc > 0) + lastarg = argv[-1]; + argv -= argc; + + /* Print the command if xflag is set. */ + if (xflag) { + out2c('+'); + eprintlist(varlist.list); + eprintlist(arglist.list); + out2c('\n'); + } + + /* Now locate the command. */ + if (argc == 0) { + cmdentry.cmdtype = CMDBUILTIN; + firstbltin = cmdentry.u.cmd = BLTINCMD; + } else { + const char *oldpath; + int findflag = DO_ERR; + int oldfindflag; + + /* + * Modify the command lookup path, if a PATH= assignment + * is present + */ + for (sp = varlist.list ; sp ; sp = sp->next) + if (varequal(sp->text, defpathvar)) { + path = sp->text + 5; + findflag |= DO_BRUTE; + } + oldpath = path; + oldfindflag = findflag; + firstbltin = 0; + for(;;) { + find_command(argv[0], &cmdentry, findflag, path); + if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ + exitstatus = 127; + goto out; + } + /* implement bltin and command here */ + if (cmdentry.cmdtype != CMDBUILTIN) { + break; + } + if (!firstbltin) { + firstbltin = cmdentry.u.cmd; + } + if (cmdentry.u.cmd == BLTINCMD) { + for(;;) { + struct builtincmd *bcmd; + + argv++; + if (--argc == 0) + goto found; + if (!(bcmd = find_builtin(*argv))) { + out2fmt("%s: not found\n", *argv); + exitstatus = 127; + goto out; + } + cmdentry.u.cmd = bcmd; + if (bcmd != BLTINCMD) + break; + } + } + if (cmdentry.u.cmd == find_builtin("command")) { + argv++; + if (--argc == 0) { + goto found; + } + if (*argv[0] == '-') { + if (!equal(argv[0], "-p")) { + argv--; + argc++; + break; + } + argv++; + if (--argc == 0) { + goto found; + } + path = defpath; + findflag |= DO_BRUTE; + } else { + path = oldpath; + findflag = oldfindflag; + } + findflag |= DO_NOFUN; + continue; + } +found: + break; + } + } + + /* Fork off a child process if necessary. */ + if (cmd->ncmd.backgnd + || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) + ) { + jp = makejob(cmd, 1); + mode = cmd->ncmd.backgnd; + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ + flags |= EV_EXIT; + } + + /* This is the child process if a fork occurred. */ + /* Execute the command. */ + if (cmdentry.cmdtype == CMDFUNCTION) { +#ifdef DEBUG + trputs("Shell function: "); trargs(argv); +#endif + exitstatus = oexitstatus; + redirect(cmd->ncmd.redirect, REDIR_PUSH); + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + INTOFF; + savelocalvars = localvars; + localvars = NULL; + INTON; + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) { + freeparam((volatile struct shparam *) + &saveparam); + } else { + saveparam.optind = shellparam.optind; + saveparam.optoff = shellparam.optoff; + freeparam(&shellparam); + shellparam = saveparam; + } + poplocalvars(); + localvars = savelocalvars; + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + for (sp = varlist.list ; sp ; sp = sp->next) + mklocal(sp->text); + funcnest++; + evaltree(cmdentry.u.func, flags & EV_TESTED); + funcnest--; + INTOFF; + poplocalvars(); + localvars = savelocalvars; + saveparam.optind = shellparam.optind; + saveparam.optoff = shellparam.optoff; + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + popredir(); + INTON; + if (evalskip == SKIPFUNC) { + evalskip = 0; + skipcount = 0; + } + if (flags & EV_EXIT) + exitshell(exitstatus); + } else if (cmdentry.cmdtype == CMDBUILTIN) { +#ifdef DEBUG + trputs("builtin command: "); trargs(argv); +#endif + mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH; + redirect(cmd->ncmd.redirect, mode); + savecmdname = commandname; + if (IS_BUILTIN_SPECIAL(firstbltin)) { + listsetvar(varlist.list); + } else { + cmdenviron = varlist.list; + } + e = -1; + if (setjmp(jmploc.loc)) { + e = exception; + exitstatus = (e == EXINT)? SIGINT+128 : 2; + goto cmddone; + } + savehandler = handler; + handler = &jmploc; + commandname = argv[0]; + argptr = argv + 1; + optptr = NULL; /* initialize nextopt */ + exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv); + flushall(); +cmddone: + cmdenviron = NULL; + if (e != EXSHELLPROC) { + commandname = savecmdname; + if (flags & EV_EXIT) + exitshell(exitstatus); + } + handler = savehandler; + if (e != -1) { + if ((e != EXERROR && e != EXEXEC) + || cmdentry.u.cmd == BLTINCMD + || cmdentry.u.cmd == DOTCMD + || cmdentry.u.cmd == EVALCMD + || cmdentry.u.cmd == EXECCMD) + exraise(e); + FORCEINTON; + } + if (cmdentry.u.cmd != EXECCMD) + popredir(); + } else { +#ifdef DEBUG + trputs("normal command: "); trargs(argv); +#endif + redirect(cmd->ncmd.redirect, 0); + clearredir(); + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, path, cmdentry.u.index); + } + goto out; + +parent: /* parent process gets here (if we forked) */ + if (mode == 0) { /* argument to fork */ + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } + +out: + if (lastarg) + setvar("_", lastarg, 0); + popstackmark(&smark); +} + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ +static void +evaltree(n, flags) + union node *n; + int flags; +{ + int checkexit = 0; + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + goto out; + } + TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, flags & EV_TESTED); + if (evalskip) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NAND: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus != 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NOR: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus == 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NREDIR: + expredir(n->nredir.redirect); + redirect(n->nredir.redirect, REDIR_PUSH); + evaltree(n->nredir.n, flags); + popredir(); + break; + case NSUBSHELL: + evalsubshell(n, flags); + break; + case NBACKGND: + evalsubshell(n, flags); + break; + case NIF: { + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) + evaltree(n->nif.ifpart, flags); + else if (n->nif.elsepart) + evaltree(n->nif.elsepart, flags); + else + exitstatus = 0; + break; + } + case NWHILE: + case NUNTIL: + evalloop(n, flags); + break; + case NFOR: + evalfor(n, flags); + break; + case NCASE: + evalcase(n, flags); + break; + case NDEFUN: { + struct builtincmd *bcmd; + struct cmdentry entry; + if ( + (bcmd = find_builtin(n->narg.text)) && + IS_BUILTIN_SPECIAL(bcmd) + ) { + out2fmt("%s is a special built-in\n", n->narg.text); + exitstatus = 1; + break; + } + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(n->narg.next); + addcmdentry(n->narg.text, &entry); + exitstatus = 0; + break; + } + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + exitstatus = !exitstatus; + break; + + case NPIPE: + evalpipe(n); + checkexit = 1; + break; + case NCMD: + evalcommand(n, flags); + checkexit = 1; + break; +#ifdef DEBUG + default: + printf("Node type = %d\n", n->type); + break; +#endif + } +out: + if (pendingsigs) + dotrap(); + if ( + flags & EV_EXIT || + (checkexit && eflag && exitstatus && !(flags & EV_TESTED)) + ) + exitshell(exitstatus); +} + +/* + * Kick off a subshell to evaluate a tree. + */ + +static void +evalsubshell(const union node *n, int flags) +{ + struct job *jp; + int backgnd = (n->type == NBACKGND); + + expredir(n->nredir.redirect); + jp = makejob(n, 1); + if (forkshell(jp, n, backgnd) == 0) { + if (backgnd) + flags &=~ EV_TESTED; + redirect(n->nredir.redirect, 0); + evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ + } + if (! backgnd) { + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } +} + +/* + * Compute the names of the files in a redirection list. + */ + +static void fixredir(union node *n, const char *text, int err); + +static void +expredir(union node *n) +{ + union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + struct arglist fn; + fn.lastp = &fn.list; + oexitstatus = exitstatus; + switch (redir->type) { + case NFROMTO: + case NFROM: + case NTO: + case NAPPEND: + case NTOOV: + 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; + } + } +} + + +/* + * 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 pip[2]; + struct job *jp; + struct stackmark smark; /* unnecessary */ + + setstackmark(&smark); + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + exitstatus = 0; + goto out; + } + exitstatus = 0; + 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); + dup_as_newfd(pip[1], 1); + close(pip[1]); + } + eflag = 0; + evaltree(n, EV_EXIT); + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; +out: + popstackmark(&smark); + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", + result->fd, result->buf, result->nleft, result->jp)); +} + + +/* + * Execute a simple command. + */ + +/* + * 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(n) + 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, or a bltin command with no arguments. Set the + * specified variables. + */ + +int +bltincmd(argc, argv) + int argc; + char **argv; +{ + /* + * Preserve exitstatus of a previous possible redirection + * as POSIX mandates + */ + return exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +static int +breakcmd(argc, argv) + int argc; + char **argv; +{ + int n = argc > 1 ? number(argv[1]) : 1; + + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +static int +returncmd(argc, argv) + int argc; + char **argv; +{ + int ret = argc > 1 ? number(argv[1]) : oexitstatus; + + 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; + } +} + + +#ifndef BB_TRUE_FALSE +static int +false_main(argc, argv) + int argc; + char **argv; +{ + return 1; +} + + +static int +true_main(argc, argv) + int argc; + char **argv; +{ + return 0; +} +#endif + +/* + * Controls whether the shell is interactive or not. + */ + +static void setsignal(int signo); +static void chkmail(int silent); + + +static void +setinteractive(int on) +{ + static int is_interactive; + static int do_banner=0; + + if (on == is_interactive) + return; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + chkmail(1); + is_interactive = on; + if (do_banner==0 && is_interactive) { + /* Looks like they want an interactive shell */ + printf( "\n\n" BB_BANNER " Built-in shell (ash)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); + do_banner=1; + } +} + +static void +optschanged(void) +{ + setinteractive(iflag); + setjobctl(mflag); +} + + +static int +execcmd(argc, argv) + int argc; + char **argv; +{ + if (argc > 1) { + struct strlist *sp; + + iflag = 0; /* exit on error */ + mflag = 0; + optschanged(); + for (sp = cmdenviron; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + shellexec(argv + 1, environment(), pathval(), 0); + } + return 0; +} + +static void +eprintlist(struct strlist *sp) +{ + for (; sp; sp = sp->next) { + out2fmt(" %s",sp->text); + } +} + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + */ + +static const char *pathopt; /* set by padvance */ + +static void +shellexec(argv, envp, path, idx) + char **argv, **envp; + const char *path; + int idx; +{ + char *cmdname; + int e; + + if (strchr(argv[0], '/') != NULL) { + tryexec(argv[0], argv, envp); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--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; + } + exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); + /* NOTREACHED */ +} + +/* + * Clear traps on a fork. + */ +static void +clear_traps(void) { + char **tp; + + for (tp = trap ; tp < &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + INTON; + } + } +} + + +static void +initshellproc(void) { + +#ifdef ASH_ALIAS + /* from alias.c: */ + { + rmaliases(); + } +#endif + /* from eval.c: */ + { + exitstatus = 0; + } + + /* from exec.c: */ + { + deletefuncs(); + } + + /* from jobs.c: */ + { + backgndpid = -1; +#ifdef JOBS + jobctl = 0; +#endif + } + + /* from options.c: */ + { + int i; + + for (i = 0; i < NOPTS; i++) + optent_val(i) = 0; + optschanged(); + + } + + /* from redir.c: */ + { + clearredir(); + } + + /* from trap.c: */ + { + char *sm; + + clear_traps(); + for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } + } + + /* from var.c: */ + { + shprocvar(); + } +} + +static int preadbuffer(void); +static void pushfile (void); + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) +static int +pgetc(void) +{ + return pgetc_macro(); +} +#else +static int +pgetc_macro(void) +{ + return --parsenleft >= 0? *parsenextc++ : preadbuffer(); +} + +static inline int +pgetc(void) +{ + return pgetc_macro(); +} +#endif + + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +static void +pungetc() { + parsenleft++; + parsenextc--; +} + + +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() { + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} + + +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ + +static void +setinputfd(fd, push) + int fd, push; +{ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); + if (push) { + pushfile(); + parsefile->buf = 0; + } else { + closescript(); + while (parsefile->strpush) + popstring(); + } + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(BUFSIZ); + parselleft = parsenleft = 0; + plinno = 1; +} + + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +static void +setinputfile(const char *fname, int push) +{ + int fd; + int myfileno2; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s", fname); + if (fd < 10) { + myfileno2 = dup_as_newfd(fd, 10); + close(fd); + if (myfileno2 < 0) + error("Out of file descriptors"); + fd = myfileno2; + } + setinputfd(fd, push); + INTON; +} + + +static void +tryexec(char *cmd, char **argv, char **envp) +{ + int e; + +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + char *name = cmd; + char** argv_l=argv; + int argc_l; +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + name = get_last_path_component(name); +#endif + argv_l=envp; + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++) + putenv(*argv_l); + argv_l=argv; + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++) + optind = 1; + run_applet_by_name(name, argc_l, argv); +#endif + execve(cmd, argv, envp); + e = errno; + if (e == ENOEXEC) { + INTOFF; + initshellproc(); + setinputfile(cmd, 0); + commandname = arg0 = savestr(argv[0]); + setparam(argv + 1); + exraise(EXSHELLPROC); + } + errno = e; +} + +static char *commandtext (const union node *); + +/* + * 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 const char *pathopt; + +static void growstackblock(void); + + +static char * +padvance(const char **path, const char *name) +{ + const char *p; + char *q; + const char *start; + int len; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start ; *p && *p != ':' && *p != '%' ; p++); + len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ + while (stackblocksize() < len) + growstackblock(); + q = stackblock(); + if (p != start) { + 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); +} + +/* + * Wrapper around strcmp for qsort/bsearch/... + */ +static int +pstrcmp(const void *a, const void *b) +{ + return strcmp((const char *) a, *(const char *const *) b); +} + +/* + * Find a keyword is in a sorted array. + */ + +static const char *const * +findkwd(const char *s) +{ + return bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *), + sizeof(const char *), pstrcmp); +} + + +/*** Command hashing code ***/ + + +static int +hashcmd(argc, argv) + int argc; + char **argv; +{ + struct tblentry **pp; + struct tblentry *cmdp; + int c; + int verbose; + struct cmdentry entry; + char *name; +#ifdef ASH_ALIAS + const struct alias *ap; +#endif + + verbose = 0; + while ((c = nextopt("rvV")) != '\0') { + if (c == 'r') { + clearcmdentry(0); + return 0; + } else if (c == 'v' || c == 'V') { + verbose = c; + } + } + if (*argptr == NULL) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype != CMDBUILTIN) { + printentry(cmdp, verbose); + } + } + } + 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(); +#ifdef ASH_ALIAS + /* Then look at the aliases */ + if ((ap = lookupalias(name, 0)) != NULL) { + if (verbose=='v') + printf("%s is an alias for %s\n", name, ap->val); + else + printalias(ap); + continue; + } +#endif + /* First look at the keywords */ + if (findkwd(name)!=0) { + if (verbose=='v') + printf("%s is a shell keyword\n", name); + else + printf(snlfmt, name); + continue; + } + + find_command(name, &entry, DO_ERR, pathval()); + if (entry.cmdtype == CMDUNKNOWN) c = 1; + else if (verbose) { + cmdp = cmdlookup(name, 0); + if (cmdp) printentry(cmdp, verbose=='v'); + flushall(); + } + } + return c; +} + +static void +printentry(cmdp, verbose) + struct tblentry *cmdp; + int verbose; + { + int idx; + const char *path; + char *name; + + printf("%s%s", cmdp->cmdname, (verbose ? " is " : "")); + if (cmdp->cmdtype == CMDNORMAL) { + idx = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--idx >= 0); + if(verbose) + out1str(name); + } else if (cmdp->cmdtype == CMDBUILTIN) { + if(verbose) + out1str("a shell builtin"); + } else if (cmdp->cmdtype == CMDFUNCTION) { + if (verbose) { + INTOFF; + out1str("a function\n"); + name = commandtext(cmdp->param.func); + printf("%s() {\n %s\n}", cmdp->cmdname, name); + ckfree(name); + INTON; + } +#ifdef DEBUG + } else { + error("internal error: cmdtype %d", cmdp->cmdtype); +#endif + } + printf(snlfmt, cmdp->rehash ? "*" : nullstr); +} + + + +/*** List the available builtins ***/ + + +static int helpcmd(int argc, char** argv) +{ + int col, i; + + printf("\nBuilt-in commands:\n-------------------\n"); + for (col=0, i=0; i < NUMBUILTINS; i++) { + col += printf("%c%s", ((col == 0) ? '\t' : ' '), + builtincmds[i].name+1); + if (col > 60) { + printf("\n"); + col = 0; + } + } +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + { + extern const struct BB_applet applets[]; + extern const size_t NUM_APPLETS; + + for (i=0; i < NUM_APPLETS; i++) { + + col += printf("%c%s", ((col == 0) ? '\t' : ' '), + applets[i].name); + if (col > 60) { + printf("\n"); + col = 0; + } + } + } +#endif + printf("\n\n"); + return EXIT_SUCCESS; +} + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +static int prefix (const char *, const char *); + +static void +find_command(const 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 bltin; + int firstchange; + int updatetbl; + int regular; + struct builtincmd *bcmd; + + /* If name contains a slash, don't use the hash table */ + if (strchr(name, '/') != NULL) { + if (act & DO_ABS) { + while (stat(name, &statb) < 0) { + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + entry->cmdtype = CMDUNKNOWN; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = 0; + return; + } + + updatetbl = 1; + if (act & DO_BRUTE) { + firstchange = path_change(path, &bltin); + } else { + bltin = builtinloc; + firstchange = 9999; + } + + /* If name is in the table, and not invalidated by cd, we're done */ + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) { + if (cmdp->cmdtype == CMDFUNCTION) { + if (act & DO_NOFUN) { + updatetbl = 0; + } else { + goto success; + } + } else if (act & DO_BRUTE) { + if ((cmdp->cmdtype == CMDNORMAL && + cmdp->param.index >= firstchange) || + (cmdp->cmdtype == CMDBUILTIN && + ((builtinloc < 0 && bltin >= 0) ? + bltin : builtinloc) >= firstchange)) { + /* need to recompute the entry */ + } else { + goto success; + } + } else { + goto success; + } + } + + bcmd = find_builtin(name); + regular = bcmd && IS_BUILTIN_REGULAR(bcmd); + + if (regular) { + if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) { + goto success; + } + } else if (act & DO_BRUTE) { + if (firstchange == 0) { + updatetbl = 0; + } + } + + /* If %builtin not in path, check for builtin next */ + if (regular || (bltin < 0 && bcmd)) { +builtin: + if (!updatetbl) { + entry->cmdtype = CMDBUILTIN; + entry->u.cmd = bcmd; + return; + } + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.cmd = bcmd; + INTON; + goto 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 (idx >= firstchange) { + updatetbl = 0; + } + if (pathopt) { + if (prefix("builtin", pathopt)) { + if ((bcmd = find_builtin(name))) { + goto builtin; + } + continue; + } else if (!(act & DO_NOFUN) && + prefix("func", pathopt)) { + /* handled below */ + } else { + continue; /* ignore unimplemented options */ + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && idx <= prev && + idx < firstchange) { + if (idx < prev) + continue; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat(fullname, &statb) < 0) { + 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 we aren't called with DO_BRUTE and cmdp is set, it must + be a function and we're being called with DO_NOFUN */ + 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) + out2fmt("%s: %s\n", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; +} + + + +/* + * Search the table of builtin commands. + */ + +static int +bstrcmp(const void *name, const void *b) +{ + return strcmp((const char *)name, (*(const char *const *) b)+1); +} + +static struct builtincmd * +find_builtin(const char *name) +{ + struct builtincmd *bp; + + bp = bsearch(name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd), + bstrcmp + ); + 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 && builtinloc >= 0)) + cmdp->rehash = 1; + } + } +} + + + +/* + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. Called with + * interrupts off. + */ + +static void +changepath(const char *newval) +{ + int firstchange; + int bltin; + + firstchange = path_change(newval, &bltin); + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + clearcmdentry(firstchange); + builtinloc = bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +static void +clearcmdentry(firstchange) + int firstchange; +{ + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if ((cmdp->cmdtype == CMDNORMAL && + cmdp->param.index >= firstchange) + || (cmdp->cmdtype == CMDBUILTIN && + builtinloc >= firstchange)) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + +/* + * Delete all functions. + */ + +static void +deletefuncs(void) { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDFUNCTION) { + *pp = cmdp->next; + freefunc(cmdp->param.func); + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + */ + +static struct tblentry **lastcmdentry; + +static struct tblentry * +cmdlookup(const char *name, int add) +{ + int hashval; + const char *p; + struct tblentry *cmdp; + struct tblentry **pp; + + p = name; + hashval = *p << 4; + while (*p) + hashval += *p++; + hashval &= 0x7FFF; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + INTOFF; + cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB + + strlen(name) + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + cmdp->rehash = 0; + strcpy(cmdp->cmdname, name); + INTON; + } + lastcmdentry = pp; + return cmdp; +} + +/* + * Delete the command entry returned on the last lookup. + */ + +static void +delete_cmd_entry() { + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + ckfree(cmdp); + INTON; +} + + + + + +static const short nodesize[26] = { + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct ncmd)), + ALIGN(sizeof (struct npipe)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nif)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nfor)), + ALIGN(sizeof (struct ncase)), + ALIGN(sizeof (struct nclist)), + ALIGN(sizeof (struct narg)), + ALIGN(sizeof (struct narg)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct ndup)), + ALIGN(sizeof (struct ndup)), + ALIGN(sizeof (struct nhere)), + ALIGN(sizeof (struct nhere)), + ALIGN(sizeof (struct nnot)), +}; + + + +/* + * Delete a function if it exists. + */ + +static void +unsetfunc(char *name) +{ + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + delete_cmd_entry(); + } +} + + +/* + * Locate and print what a word is... + */ + +static int +typecmd(int argc, char **argv) +{ + int i; + int err = 0; + char *argv_a[2]; + + argv_a[1] = 0; + + for (i = 1; i < argc; i++) { + argv_a[0] = argv[i]; + argptr = argv_a; + optptr = "v"; + err |= hashcmd(argc, argv); + } + return err; +} + +#ifdef ASH_CMDCMD +static int +commandcmd(argc, argv) + 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) { + 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) { + out2str( + "command [-p] command [arg ...]\n" + "command {-v|-V} command\n"); + return EX_USAGE; + } + + if (verify_only || verbose_verify_only) { + char *argv_a[2]; + + argv_a[1] = 0; + argv_a[0] = *argptr; + argptr = argv_a; + optptr = verbose_verify_only ? "v" : "V"; /* reverse special */ + return hashcmd(argc, argv); + } + + return 0; +} +#endif + +static int +path_change(newval, bltin) + const char *newval; + int *bltin; +{ + const char *old, *new; + int idx; + int firstchange; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + idx = 0; + *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 == '%' && *bltin < 0 && prefix("builtin", new + 1)) + *bltin = idx; + if (*new == ':') { + idx++; + } + new++, old++; + } + if (builtinloc >= 0 && *bltin < 0) + firstchange = 0; + return firstchange; +} +/* + * 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 */ + +/* + * 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 */ +}; + + +static char *expdest; /* output of current string */ +static struct nodelist *argbackq; /* list of back quote expressions */ +static struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +static struct ifsregion *ifslastp; /* last struct in list */ +static struct arglist exparg; /* holds expanded arg list */ + +static void argstr (char *, int); +static char *exptilde (char *, int); +static void expbackq (union node *, int, int); +static int subevalvar (char *, char *, int, int, int, int, int); +static int varisset (char *, int); +static void strtodest (const char *, const char *, int); +static void 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); +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB) +#if !defined(GLOB_BROKEN) +static void addglob (const glob_t *); +#endif +#endif +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +static void expmeta (char *, char *); +#endif +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +static struct strlist *expsort (struct strlist *); +static struct strlist *msort (struct strlist *, int); +#endif +static int patmatch (char *, char *, int); +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +static int patmatch2 (char *, char *, int); +#else +static int pmatch (char *, char *, int); +#define patmatch2 patmatch +#endif +static char *cvtnum (int, char *); + +/* + * Expand shell variables and backquotes inside a here document. + */ + +/* arg: the document, fd: where to write the expanded version */ +static inline void +expandhere(union node *arg, int fd) +{ + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + xwrite(fd, stackblock(), expdest - 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. + */ + +static void +expandarg(arg, arglist, flag) + 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; + } + ifsfree(); + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } +} + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +static inline char * +evalvar(p, flag) + char *p; + int flag; +{ + int subtype; + int varflags; + char *var; + const char *val; + int patloc; + int c; + int set; + int special; + int startloc; + int varlen; + int easy; + int quotes = flag & (EXP_FULL | EXP_CASE); + + varflags = *p++; + subtype = varflags & VSTYPE; + var = p; + special = 0; + if (! is_name(*p)) + special = 1; + p = strchr(p, '=') + 1; +again: /* jump here after setting a variable with ${var=text} */ + if (special) { + set = varisset(var, varflags & VSNUL); + val = NULL; + } else { + val = lookupvar(var); + if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { + val = NULL; + set = 0; + } else + set = 1; + } + varlen = 0; + startloc = expdest - stackblock(); + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + varvalue(var, varflags & VSQUOTE, flag); + if (subtype == VSLENGTH) { + varlen = expdest - stackblock() - startloc; + STADJUST(-varlen, expdest); + } + } else { + if (subtype == VSLENGTH) { + varlen = strlen(val); + } else { + strtodest( + val, + varflags & VSQUOTE ? + DQSYNTAX : BASESYNTAX, + quotes + ); + } + } + } + + if (subtype == VSPLUS) + set = ! set; + + easy = ((varflags & VSQUOTE) == 0 || + (*var == '@' && shellparam.nparam != 1)); + + + switch (subtype) { + case VSLENGTH: + expdest = cvtnum(varlen, expdest); + goto record; + + case VSNORMAL: + if (!easy) + break; +record: + recordregion(startloc, expdest - stackblock(), + varflags & VSQUOTE); + break; + + case VSPLUS: + case VSMINUS: + if (!set) { + argstr(p, flag); + break; + } + if (easy) + goto record; + break; + + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + if (!set) + break; + /* + * Terminate the string and start recording the pattern + * right after it + */ + STPUTC('\0', expdest); + patloc = expdest - stackblock(); + if (subevalvar(p, NULL, patloc, subtype, + startloc, varflags, quotes) == 0) { + int amount = (expdest - stackblock() - patloc) + 1; + STADJUST(-amount, expdest); + } + /* Remove any recorded regions beyond start of variable */ + removerecordregions(startloc); + goto record; + + case VSASSIGN: + case VSQUESTION: + if (!set) { + if (subevalvar(p, var, 0, subtype, startloc, + varflags, quotes)) { + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; + } + break; + } + if (easy) + goto record; + break; + +#ifdef DEBUG + default: + abort(); +#endif + } + + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (set) + argbackq = argbackq->next; + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + +/* + * 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(p, flag) + char *p; + int flag; +{ + char c; + int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ + int firsteq = 1; + + if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) + p = exptilde(p, flag); + for (;;) { + switch (c = *p++) { + case '\0': + case CTLENDVAR: /* ??? */ + goto breakloop; + case CTLQUOTEMARK: + /* "$@" syntax adherence hack */ + if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') + break; + if ((flag & EXP_FULL) != 0) + STPUTC(c, expdest); + break; + case CTLESC: + if (quotes) + STPUTC(c, expdest); + c = *p++; + STPUTC(c, expdest); + break; + case CTLVAR: + p = evalvar(p, flag); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c & CTLQUOTE, flag); + argbackq = argbackq->next; + break; +#ifdef ASH_MATH_SUPPORT + case CTLENDARI: + expari(flag); + break; +#endif + case ':': + case '=': + /* + * sort of a hack - expand tildes in variable + * assignments (after the first '=' and after ':'s). + */ + STPUTC(c, expdest); + if (flag & EXP_VARTILDE && *p == '~') { + if (c == '=') { + if (firsteq) + firsteq = 0; + else + break; + } + p = exptilde(p, flag); + } + break; + default: + STPUTC(c, expdest); + } + } +breakloop:; + return; +} + +static char * +exptilde(p, flag) + char *p; + int flag; +{ + char c, *startp = p; + struct passwd *pw; + const char *home; + int quotes = flag & (EXP_FULL | EXP_CASE); + + while ((c = *p) != '\0') { + switch(c) { + case CTLESC: + return (startp); + case CTLQUOTEMARK: + return (startp); + case ':': + if (flag & EXP_VARTILDE) + goto done; + break; + case '/': + goto done; + } + p++; + } +done: + *p = '\0'; + if (*(startp+1) == '\0') { + if ((home = lookupvar("HOME")) == NULL) + goto lose; + } else { + if ((pw = getpwnam(startp+1)) == NULL) + goto lose; + home = pw->pw_dir; + } + if (*home == '\0') + goto lose; + *p = c; + strtodest(home, SQSYNTAX, quotes); + 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 ASH_MATH_SUPPORT +/* + * Expand arithmetic expression. Backup to start of expression, + * evaluate, place result in (backed up) result, adjust string position. + */ +static void +expari(int flag) +{ + char *p, *start; + int errcode; + int result; + int begoff; + int quotes = flag & (EXP_FULL | EXP_CASE); + int quoted; + + /* ifsfree(); */ + + /* + * This routine is slightly over-complicated for + * efficiency. First we make sure there is + * enough space for the result, which may be bigger + * than the expression if we add exponentation. Next we + * scan backwards looking for the start of arithmetic. If the + * next previous character is a CTLESC character, then we + * have to rescan starting from the beginning since CTLESC + * characters have to be processed left to right. + */ + CHECKSTRSPACE(10, expdest); + USTPUTC('\0', expdest); + start = stackblock(); + p = expdest - 1; + while (*p != CTLARI && p >= start) + --p; + if (*p != CTLARI) + error("missing CTLARI (shouldn't happen)"); + if (p > start && *(p-1) == CTLESC) + for (p = start; *p != CTLARI; p++) + if (*p == CTLESC) + p++; + + if (p[1] == '"') + quoted=1; + else + quoted=0; + begoff = p - start; + removerecordregions(begoff); + if (quotes) + rmescapes(p+2); + result = arith(p+2, &errcode); + if (errcode < 0) { + if(errcode == -2) + error("divide by zero"); + else + error("syntax error: \"%s\"\n", p+2); + } + snprintf(p, 12, "%d", result); + + while (*p++) + ; + + if (quoted == 0) + recordregion(begoff, p - 1 - start, 0); + result = expdest - p + 1; + STADJUST(-result, expdest); +} +#endif + +/* + * Expand stuff in backwards quotes. + */ + +static void +expbackq(cmd, quoted, flag) + union node *cmd; + int quoted; + int flag; +{ + volatile struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + volatile struct ifsregion saveifs; + struct ifsregion *volatile savelastp; + struct nodelist *volatile saveargbackq; + char lastc; + int startloc = dest - stackblock(); + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + volatile int saveherefd; + int quotes = flag & (EXP_FULL | EXP_CASE); + struct jmploc jmploc; + struct jmploc *volatile savehandler; + int ex; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &dest; + (void) &syntax; +#endif + + in.fd = -1; + in.buf = 0; + in.jp = 0; + + INTOFF; + saveifs = ifsfirst; + savelastp = ifslastp; + saveargbackq = argbackq; + saveherefd = herefd; + herefd = -1; + if ((ex = setjmp(jmploc.loc))) { + goto err1; + } + savehandler = handler; + handler = &jmploc; + INTON; + p = grabstackstr(dest); + evalbackcmd(cmd, (struct backcmd *) &in); + ungrabstackstr(p, dest); +err1: + INTOFF; + ifsfirst = saveifs; + ifslastp = savelastp; + argbackq = saveargbackq; + herefd = saveherefd; + if (ex) { + goto err2; + } + + p = in.buf; + lastc = '\0'; + for (;;) { + if (--in.nleft < 0) { + 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; + in.nleft = i - 1; + } + lastc = *p++; + if (lastc != '\0') { + if (quotes && syntax[(int)lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } + + /* Eat all trailing newlines */ + for (; dest > stackblock() && dest[-1] == '\n';) + STUNPUTC(dest); + +err2: + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + exitstatus = waitforjob(in.jp); + handler = savehandler; + if (ex) { + longjmp(handler->loc, 1); + } + if (quoted == 0) + recordregion(startloc, dest - stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - stackblock()) - startloc, + (dest - stackblock()) - startloc, + stackblock() + startloc)); + expdest = dest; + INTON; +} + +static int +subevalvar(p, str, strloc, subtype, startloc, varflags, quotes) + char *p; + char *str; + int strloc; + int subtype; + int startloc; + int varflags; + int quotes; +{ + char *startp; + char *loc = NULL; + char *q; + int c = 0; + int saveherefd = herefd; + struct nodelist *saveargbackq = argbackq; + int amount; + + herefd = -1; + argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + argbackq = saveargbackq; + startp = stackblock() + startloc; + if (str == NULL) + str = stackblock() + strloc; + + switch (subtype) { + case VSASSIGN: + setvar(str, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + varflags &= ~VSNUL; + if (c != 0) + *loc = c; + return 1; + + case VSQUESTION: + if (*p != CTLENDVAR) { + out2fmt(snlfmt, startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - str - 1, + str, (varflags & VSNUL) ? "null or " + : nullstr); + /* NOTREACHED */ + + case VSTRIMLEFT: + for (loc = startp; loc < str; loc++) { + c = *loc; + *loc = '\0'; + if (patmatch2(str, startp, quotes)) + goto recordleft; + *loc = c; + if (quotes && *loc == CTLESC) + loc++; + } + return 0; + + case VSTRIMLEFTMAX: + for (loc = str - 1; loc >= startp;) { + c = *loc; + *loc = '\0'; + if (patmatch2(str, startp, quotes)) + goto recordleft; + *loc = c; + loc--; + if (quotes && loc > startp && *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; + + case VSTRIMRIGHT: + for (loc = str - 1; loc >= startp;) { + if (patmatch2(str, loc, quotes)) + goto recordright; + loc--; + if (quotes && loc > startp && *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; + + case VSTRIMRIGHTMAX: + for (loc = startp; loc < str - 1; loc++) { + if (patmatch2(str, loc, quotes)) + goto recordright; + if (quotes && *loc == CTLESC) + loc++; + } + return 0; + +#ifdef DEBUG + default: + abort(); +#endif + } + +recordleft: + *loc = c; + amount = ((str - 1) - (loc - startp)) - expdest; + STADJUST(amount, expdest); + while (loc != str - 1) + *startp++ = *loc++; + return 1; + +recordright: + amount = loc - expdest; + STADJUST(amount, expdest); + STPUTC('\0', expdest); + STADJUST(-1, expdest); + return 1; +} + + +/* + * Test whether a specialized variable is set. + */ + +static int +varisset(name, nulok) + char *name; + int nulok; +{ + if (*name == '!') + return backgndpid != -1; + else if (*name == '@' || *name == '*') { + if (*shellparam.p == NULL) + return 0; + + if (nulok) { + char **av; + + for (av = shellparam.p; *av; av++) + if (**av != '\0') + return 1; + return 0; + } + } else if (is_digit(*name)) { + char *ap; + int num = atoi(name); + + if (num > shellparam.nparam) + return 0; + + if (num == 0) + ap = arg0; + else + ap = shellparam.p[num - 1]; + + if (nulok && (ap == NULL || *ap == '\0')) + return 0; + } + return 1; +} + +/* + * Put a string on the stack. + */ + +static void +strtodest(p, syntax, quotes) + const char *p; + const char *syntax; + int quotes; +{ + while (*p) { + if (quotes && syntax[(int) *p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); + } +} + +/* + * Add the value of a specialized variable to the stack string. + */ + +static void +varvalue(name, quoted, flags) + char *name; + int quoted; + int flags; +{ + int num; + char *p; + int i; + int sep; + int sepq = 0; + char **ap; + char const *syntax; + int allow_split = flags & EXP_FULL; + int quotes = flags & (EXP_FULL | EXP_CASE); + + syntax = quoted ? DQSYNTAX : BASESYNTAX; + switch (*name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = oexitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; +numvar: + expdest = cvtnum(num, expdest); + break; + case '-': + for (i = 0 ; i < NOPTS ; i++) { + if (optent_val(i)) + STPUTC(optent_letter(optlist[i]), expdest); + } + break; + case '@': + if (allow_split && quoted) { + sep = 1 << CHAR_BIT; + goto param; + } + /* fall through */ + case '*': + sep = ifsset() ? ifsval()[0] : ' '; + if (quotes) { + sepq = syntax[(int) sep] == CCTL; + } +param: + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + strtodest(p, syntax, quotes); + if (*ap && sep) { + if (sepq) + STPUTC(CTLESC, expdest); + STPUTC(sep, expdest); + } + } + break; + case '0': + strtodest(arg0, syntax, quotes); + break; + default: + num = atoi(name); + if (num > 0 && num <= shellparam.nparam) { + strtodest(shellparam.p[num - 1], syntax, quotes); + } + break; + } +} + + +/* + * Record the fact that we have to scan this region of the + * string for IFS characters. + */ + +static void +recordregion(start, end, nulonly) + 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(string, arglist) + 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; + ifsspc = 0; + nulonly = 0; + realifs = ifsset() ? ifsval() : defifs; + if (ifslastp != NULL) { + 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 (!(*start || (!ifsspc && start > string && nulonly))) { + return; + } + } + + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; +} + +static void +ifsfree() +{ + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + ifslastp = NULL; + ifsfirst.next = NULL; +} + +/* + * Add a file name to the list. + */ + +static void +addfname(const char *name) +{ + char *p; + struct strlist *sp; + + p = sstrdup(name); + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} + +/* + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. + */ + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) +static void +expandmeta(str, flag) + struct strlist *str; + int flag; +{ + const char *p; + glob_t pglob; + /* TODO - EXP_REDIR */ + + while (str) { + if (fflag) + goto nometa; + p = preglob(str->text); + INTOFF; + switch (glob(p, 0, 0, &pglob)) { + case 0: + if(pglob.gl_pathv[1]==0 && !strcmp(p, pglob.gl_pathv[0])) + goto nometa2; + addglob(&pglob); + globfree(&pglob); + INTON; + break; + case GLOB_NOMATCH: +nometa2: + globfree(&pglob); + INTON; +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + break; + default: /* GLOB_NOSPACE */ + error("Out of space"); + } + str = str->next; + } +} + + +/* + * Add the result of glob(3) to the list. + */ + +static void +addglob(pglob) + const glob_t *pglob; +{ + char **p = pglob->gl_pathv; + + do { + addfname(*p); + } while (*++p); +} + + +#else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ +static char *expdir; + + +static void +expandmeta(str, flag) + struct strlist *str; + int flag; +{ + char *p; + struct strlist **savelastp; + struct strlist *sp; + char c; + /* TODO - EXP_REDIR */ + + while (str) { + if (fflag) + goto nometa; + p = str->text; + for (;;) { /* fast check for meta chars */ + if ((c = *p++) == '\0') + goto nometa; + if (c == '*' || c == '?' || c == '[' || c == '!') + break; + } + savelastp = exparg.lastp; + INTOFF; + if (expdir == NULL) { + int i = strlen(str->text); + expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ + } + + expmeta(expdir, str->text); + ckfree(expdir); + expdir = NULL; + 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; + } +} + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +static void +expmeta(enddir, name) + char *enddir; + char *name; + { + char *p; + const char *cp; + char *q; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + + metaflag = 0; + start = name; + for (p = name ; ; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!') + q++; + for (;;) { + while (*q == CTLQUOTEMARK) + q++; + if (*q == CTLESC) + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { + metaflag = 1; + } else if (*p == '\0') + break; + else if (*p == CTLQUOTEMARK) + continue; + else if (*p == CTLESC) + p++; + if (*p == '/') { + if (metaflag) + break; + start = p + 1; + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLQUOTEMARK) + continue; + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + } + if (metaflag == 0 || lstat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (start != name) { + p = name; + while (p < start) { + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + *enddir++ = *p++; + } + } + 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; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if (*p == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name, 0)) { + if (atend) { + strcpy(enddir, dp->d_name); + 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] = '/'; +} +#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ + + + +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +/* + * 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(str) + 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(list, len) + struct strlist *list; + int len; +{ + struct strlist *p, *q = NULL; + struct strlist **lpp; + int half; + int n; + + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { + if (strcmp(p->text, q->text) < 0) { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} +#endif + + + +/* + * Returns true if the pattern matches the string. + */ + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +/* squoted: string might have quote chars */ +static int +patmatch(char *pattern, char *string, int squoted) +{ + const char *p; + char *q; + + p = preglob(pattern); + q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string; + + return !fnmatch(p, q, 0); +} + + +static int +patmatch2(char *pattern, char *string, int squoted) +{ + char *p; + int res; + + sstrnleft--; + p = grabstackstr(expdest); + res = patmatch(pattern, string, squoted); + ungrabstackstr(p, expdest); + return res; +} +#else +static int +patmatch(char *pattern, char *string, int squoted) { + return pmatch(pattern, string, squoted); +} + + +static int +pmatch(char *pattern, char *string, int squoted) +{ + char *p, *q; + char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (squoted && *q == CTLESC) + q++; + if (*q++ != *p++) + return 0; + break; + case CTLQUOTEMARK: + continue; + case '?': + if (squoted && *q == CTLESC) + q++; + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + while (c == CTLQUOTEMARK || c == '*') + c = *++p; + if (c != CTLESC && c != CTLQUOTEMARK && + c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (squoted && *q == CTLESC && + q[1] == c) + break; + if (*q == '\0') + return 0; + if (squoted && *q == CTLESC) + q++; + q++; + } + } + do { + if (pmatch(p, q, squoted)) + return 1; + if (squoted && *q == CTLESC) + q++; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; + + endp = p; + if (*endp == '!') + endp++; + for (;;) { + while (*endp == CTLQUOTEMARK) + endp++; + if (*endp == '\0') + goto dft; /* no matching ] */ + if (*endp == CTLESC) + endp++; + if (*++endp == ']') + break; + } + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q++; + if (squoted && chr == CTLESC) + chr = *q++; + if (chr == '\0') + return 0; + c = *p++; + do { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + break; + } +dft: default: + if (squoted && *q == CTLESC) + q++; + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; +} +#endif + + + +/* + * Remove any CTLESC characters from a string. + */ + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +static char * +_rmescapes(char *str, int flag) +{ + char *p, *q, *r; + static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; + + p = strpbrk(str, qchars); + if (!p) { + return str; + } + q = p; + r = str; + if (flag & RMESCAPE_ALLOC) { + size_t len = p - str; + q = r = stalloc(strlen(p) + len + 1); + if (len > 0) { + memcpy(q, str, len); + q += len; + } + } + while (*p) { + if (*p == CTLQUOTEMARK) { + p++; + continue; + } + if (*p == CTLESC) { + p++; + if (flag & RMESCAPE_GLOB && *p != '/') { + *q++ = '\\'; + } + } + *q++ = *p++; + } + *q = '\0'; + return r; +} +#else +static void +rmescapes(str) + char *str; +{ + char *p, *q; + + p = str; + while (*p != CTLESC && *p != CTLQUOTEMARK) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLQUOTEMARK) { + p++; + continue; + } + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; +} +#endif + + + +/* + * See if a pattern matches in a case statement. + */ + +static int +casematch(union node *pattern, const char *val) +{ + struct stackmark smark; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + ifslastp = NULL; + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + STPUTC('\0', expdest); + p = grabstackstr(expdest); + result = patmatch(p, (char *)val, 0); + popstackmark(&smark); + return result; +} + +/* + * Our own itoa(). + */ + +static char * +cvtnum(num, buf) + int num; + char *buf; + { + int len; + + CHECKSTRSPACE(32, buf); + len = sprintf(buf, "%d", num); + STADJUST(len, buf); + return buf; +} +/* + * Editline and history functions (and glue). + */ +static int histcmd(argc, argv) + int argc; + char **argv; +{ + error("not compiled with history support"); + /* NOTREACHED */ +} + + +struct redirtab { + struct redirtab *next; + short renamed[10]; /* Current ash support only 0-9 descriptors */ + /* char on arm (and others) can't be negative */ +}; + +static struct redirtab *redirlist; + +extern char **environ; + + + +/* + * Initialization code. + */ + +static void +init(void) { + + /* from cd.c: */ + { + setpwd(0, 0); + } + + /* from input.c: */ + { + basepf.nextc = basepf.buf = basebuf; + } + + /* 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); + } +} + + + +/* + * This routine is called when an error or an interrupt occurs in an + * interactive shell and control is returned to the main command loop. + */ + +/* 1 == check for aliases, 2 == also check for assignments */ +static int checkalias; /* also used in no alias mode for check assignments */ + +static void +reset(void) { + + /* from eval.c: */ + { + evalskip = 0; + loopnest = 0; + funcnest = 0; + } + + /* from input.c: */ + { + if (exception != EXSHELLPROC) + parselleft = parsenleft = 0; /* clear input buffer */ + popallfiles(); + } + + /* from parser.c: */ + { + tokpushback = 0; + checkkwd = 0; + checkalias = 0; + } + + /* from redir.c: */ + { + while (redirlist) + popredir(); + } + +} + + + +/* + * This file implements the input routines used by the parser. + */ + +#ifdef BB_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 + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ + + + +/* + * Same as pgetc(), but ignores PEOA. + */ + +#ifdef ASH_ALIAS +static int +pgetc2() +{ + int c; + do { + c = pgetc_macro(); + } while (c == PEOA); + return c; +} +#else +static inline int pgetc2() { 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; +} + +static inline int +preadfd(void) +{ + int nr; + char *buf = parsefile->buf; + parsenextc = buf; + +retry: +#ifdef BB_FEATURE_COMMAND_EDITING + { + if (!iflag || parsefile->fd) + nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); + else { + nr = cmdedit_read_input((char*)cmdedit_prompt, buf); + } + } +#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; +} + +static void +popstring(void) +{ + struct strpush *sp = parsefile->strpush; + + INTOFF; +#ifdef ASH_ALIAS + if (sp->ap) { + if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { + if (!checkalias) { + checkalias = 1; + } + } + 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; +} + + +/* + * 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. + */ + +static int +preadbuffer(void) +{ + char *p, *q; + int more; + char savec; + + while (parsefile->strpush) { +#ifdef 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++; +} + + +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +static void +pushstring(char *s, int len, void *ap) +{ + struct strpush *sp; + + 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 ASH_ALIAS + sp->ap = (struct alias *)ap; + if (ap) { + ((struct alias *)ap)->flag |= ALIASINUSE; + sp->string = s; + } +#endif + parsenextc = s; + parsenleft = len; + INTON; +} + + +/* + * 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; +} + +#ifdef JOBS +static void restartjob (struct job *); +#endif +static void freejob (struct job *); +static struct job *getjob (const char *); +static int dowait (int, struct job *); +static void waitonint(int); + + +/* + * We keep track of whether or not fd0 has been redirected. This is for + * background commands, where we want to redirect fd0 to /dev/null only + * if it hasn't already been redirected. +*/ +static int fd0_redirected = 0; + +/* Return true if fd 0 has already been redirected at least once. */ +static inline int +fd0_redirected_p () { + return fd0_redirected != 0; +} + +static void dupredirect (const union node *, int, int fd1dup); + +#ifdef 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. + */ + + + +static void setjobctl(int enable) +{ +#ifdef OLD_TTY_DRIVER + int ldisc; +#endif + + if (enable == jobctl || rootshell == 0) + return; + if (enable) { + do { /* while we are in the background */ +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { +#else + initialpgrp = tcgetpgrp(2); + if (initialpgrp < 0) { +#endif + out2str("sh: can't access tty; job control turned off\n"); + mflag = 0; + return; + } + if (initialpgrp == -1) + initialpgrp = getpgrp(); + else if (initialpgrp != getpgrp()) { + killpg(initialpgrp, SIGTTIN); + continue; + } + } while (0); +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { + out2str("sh: need new tty driver to run job control; job control turned off\n"); + mflag = 0; + return; + } +#endif + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + setpgid(0, rootpid); +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&rootpid); +#else + tcsetpgrp(2, rootpid); +#endif + } else { /* turning job control off */ + setpgid(0, initialpgrp); +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&initialpgrp); +#else + tcsetpgrp(2, initialpgrp); +#endif + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + } + jobctl = enable; +} +#endif + + +#ifdef JOBS +static int +killcmd(argc, argv) + 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[1] == '-') { + signo = decode_signal(argv[1] + 1, 1); + if (signo < 0) { + int c; + + while ((c = nextopt("ls:")) != '\0') + switch (c) { + case 'l': + list = 1; + break; + case 's': + signo = decode_signal(optionarg, 1); + if (signo < 0) { + error( + "invalid signal number or name: %s", + optionarg + ); + } + break; +#ifdef DEBUG + default: + error( + "nextopt returned character code 0%o", c); +#endif + } + } else + argptr++; + } + + if (!list && signo < 0) + signo = SIGTERM; + + if ((signo < 0 || !*argptr) ^ list) { + goto usage; + } + + if (list) { + const char *name; + + if (!*argptr) { + out1str("0\n"); + for (i = 1; i < NSIG; i++) { + name = u_signal_names(0, &i, 1); + if(name) + printf(snlfmt, name); + } + return 0; + } + name = u_signal_names(*argptr, &signo, -1); + if (name) + printf(snlfmt, name); + else + error("invalid signal number or exit status: %s", + *argptr); + return 0; + } + + do { + if (**argptr == '%') { + jp = getjob(*argptr); + if (jp->jobctl == 0) + error("job %s not created under job control", + *argptr); + pid = -jp->ps[0].pid; + } else + pid = atoi(*argptr); + if (kill(pid, signo) != 0) + error("%s: %m", *argptr); + } while (*++argptr); + + return 0; +} + +static int +fgcmd(argc, argv) + int argc; + char **argv; +{ + struct job *jp; + int pgrp; + int status; + + jp = getjob(argv[1]); + if (jp->jobctl == 0) + error("job not created under job control"); + pgrp = jp->ps[0].pid; +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&pgrp); +#else + tcsetpgrp(2, pgrp); +#endif + restartjob(jp); + INTOFF; + status = waitforjob(jp); + INTON; + return status; +} + + +static int +bgcmd(argc, argv) + int argc; + char **argv; +{ + struct job *jp; + + do { + jp = getjob(*++argv); + if (jp->jobctl == 0) + error("job not created under job control"); + restartjob(jp); + } while (--argc > 1); + return 0; +} + + +static void +restartjob(jp) + struct job *jp; +{ + struct procstat *ps; + int i; + + if (jp->state == JOBDONE) + return; + INTOFF; + killpg(jp->ps[0].pid, SIGCONT); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if (WIFSTOPPED(ps->status)) { + ps->status = -1; + jp->state = 0; + } + } + INTON; +} +#endif + +static void showjobs(int change); + + +static int +jobscmd(argc, argv) + int argc; + char **argv; +{ + showjobs(0); + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + * + * If the shell is interrupted in the process of creating a job, the + * result may be a job structure containing zero processes. Such structures + * will be freed here. + */ + +static void +showjobs(change) + int change; +{ + int jobno; + int procno; + int i; + struct job *jp; + struct procstat *ps; + int col; + char s[64]; + + TRACE(("showjobs(%d) called\n", change)); + while (dowait(0, (struct job *)NULL) > 0); + for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { + if (! jp->used) + continue; + if (jp->nprocs == 0) { + freejob(jp); + continue; + } + if (change && ! jp->changed) + continue; + procno = jp->nprocs; + for (ps = jp->ps ; ; ps++) { /* for each process */ + if (ps == jp->ps) + snprintf(s, 64, "[%d] %ld ", jobno, + (long)ps->pid); + else + snprintf(s, 64, " %ld ", + (long)ps->pid); + out1str(s); + col = strlen(s); + s[0] = '\0'; + if (ps->status == -1) { + /* don't print anything */ + } else if (WIFEXITED(ps->status)) { + snprintf(s, 64, "Exit %d", + WEXITSTATUS(ps->status)); + } else { +#ifdef JOBS + if (WIFSTOPPED(ps->status)) + i = WSTOPSIG(ps->status); + else /* WIFSIGNALED(ps->status) */ +#endif + i = WTERMSIG(ps->status); + if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) + strcpy(s, sys_siglist[i & 0x7F]); + else + snprintf(s, 64, "Signal %d", i & 0x7F); + if (WCOREDUMP(ps->status)) + strcat(s, " (core dumped)"); + } + out1str(s); + col += strlen(s); + printf( + "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ', + ps->cmd + ); + if (--procno <= 0) + break; + } + jp->changed = 0; + if (jp->state == JOBDONE) { + freejob(jp); + } + } +} + + +/* + * Mark a job structure as unused. + */ + +static void +freejob(struct job *jp) +{ + const 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; +#ifdef JOBS + if (curjob == jp - jobtab + 1) + curjob = 0; +#endif + INTON; +} + + + +static int +waitcmd(argc, argv) + int argc; + char **argv; +{ + struct job *job; + int status, retval; + struct job *jp; + + if (--argc > 0) { +start: + job = getjob(*++argv); + } else { + job = NULL; + } + for (;;) { /* loop until process terminated or stopped */ + if (job != NULL) { + if (job->state) { + status = job->ps[job->nprocs - 1].status; + if (! iflag) + freejob(job); + if (--argc) { + goto start; + } + if (WIFEXITED(status)) + retval = WEXITSTATUS(status); +#ifdef JOBS + else if (WIFSTOPPED(status)) + retval = WSTOPSIG(status) + 128; +#endif + else { + /* XXX: limits number of signals */ + retval = WTERMSIG(status) + 128; + } + return retval; + } + } else { + for (jp = jobtab ; ; jp++) { + if (jp >= jobtab + njobs) { /* no running procs */ + return 0; + } + if (jp->used && jp->state == 0) + break; + } + } + if (dowait(2, 0) < 0 && errno == EINTR) { + return 129; + } + } +} + + + +/* + * Convert a job name to a job structure. + */ + +static struct job * +getjob(const char *name) +{ + int jobno; + struct job *jp; + int pid; + int i; + + if (name == NULL) { +#ifdef JOBS +currentjob: + if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0) + error("No current job"); + return &jobtab[jobno - 1]; +#else + error("No current job"); +#endif + } else if (name[0] == '%') { + if (is_digit(name[1])) { + jobno = number(name + 1); + if (jobno > 0 && jobno <= njobs + && jobtab[jobno - 1].used != 0) + return &jobtab[jobno - 1]; +#ifdef JOBS + } else if (name[1] == '%' && name[2] == '\0') { + goto currentjob; +#endif + } else { + struct job *found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && prefix(name + 1, jp->ps[0].cmd)) { + if (found) + error("%s: ambiguous", name); + found = jp; + } + } + if (found) + return found; + } + } else if (is_number(name, &pid)) { + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } + } + error("No such job: %s", name); + /* NOTREACHED */ +} + + + +/* + * Return a new job structure, + */ + +static struct job * +makejob(const union node *node, int nprocs) +{ + int i; + struct job *jp; + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + INTOFF; + if (njobs == 0) { + jobtab = ckmalloc(4 * sizeof jobtab[0]); + } else { + jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); + memcpy(jp, jobtab, njobs * sizeof jp[0]); + /* Relocate `ps' pointers */ + for (i = 0; i < njobs; i++) + if (jp[i].ps == &jobtab[i].ps0) + jp[i].ps = &jp[i].ps0; + ckfree(jobtab); + jobtab = jp; + } + jp = jobtab + njobs; + for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); + INTON; + break; + } + if (jp->used == 0) + break; + } + INTOFF; + jp->state = 0; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; +#ifdef JOBS + jp->jobctl = jobctl; +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; + } + INTON; + TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, + jp - jobtab + 1)); + return jp; +} + + +/* + * Fork of a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + */ + + + +static int +forkshell(struct job *jp, const union node *n, int mode) +{ + int pid; +#ifdef JOBS + int pgrp; +#endif + const char *devnull = _PATH_DEVNULL; + const char *nullerr = "Can't open %s"; + + TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n, + mode)); + INTOFF; + pid = fork(); + if (pid == -1) { + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork"); + } + if (pid == 0) { + struct job *p; + int wasroot; + int i; + + TRACE(("Child shell %d\n", getpid())); + wasroot = rootshell; + rootshell = 0; + closescript(); + INTON; + clear_traps(); +#ifdef JOBS + jobctl = 0; /* do job control only in root shell */ + if (wasroot && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + setpgid(0, pgrp); + if (mode == FORK_FG) { + /*** this causes superfluous TIOCSPGRPS ***/ +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) + error("TIOCSPGRP failed, errno=%d", errno); +#else + if (tcsetpgrp(2, pgrp) < 0) + error("tcsetpgrp failed, errno=%d", errno); +#endif + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } else if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#endif + for (i = njobs, p = jobtab ; --i >= 0 ; p++) + if (p->used) + freejob(p); + if (wasroot && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + return pid; + } +#ifdef JOBS + if (rootshell && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; + setpgid(pid, pgrp); + } +#endif + if (mode == FORK_BG) + backgndpid = pid; /* set $! */ + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; + if (iflag && rootshell && n) + ps->cmd = commandtext(n); + } + INTON; + TRACE(("In parent shell: child = %d\n", pid)); + return pid; +} + + + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * forground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + */ + +static int +waitforjob(struct job *jp) +{ +#ifdef JOBS + int mypgrp = getpgrp(); +#endif + int status; + int st; + struct sigaction act, oact; + + INTOFF; + intreceived = 0; +#ifdef JOBS + if (!jobctl) { +#else + if (!iflag) { +#endif + sigaction(SIGINT, 0, &act); + act.sa_handler = waitonint; + sigaction(SIGINT, &act, &oact); + } + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == 0) { + dowait(1, jp); + } +#ifdef JOBS + if (!jobctl) { +#else + if (!iflag) { +#endif + sigaction(SIGINT, &oact, 0); + if (intreceived && trap[SIGINT]) kill(getpid(), SIGINT); + } +#ifdef JOBS + if (jp->jobctl) { +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); +#else + if (tcsetpgrp(2, mypgrp) < 0) + error("tcsetpgrp failed, errno=%d\n", errno); +#endif + } + if (jp->state == JOBSTOPPED) + curjob = jp - jobtab + 1; +#endif + status = jp->ps[jp->nprocs - 1].status; + /* convert to 8 bits */ + if (WIFEXITED(status)) + st = WEXITSTATUS(status); +#ifdef JOBS + else if (WIFSTOPPED(status)) + st = WSTOPSIG(status) + 128; +#endif + else + st = WTERMSIG(status) + 128; +#ifdef JOBS + if (jp->jobctl) { + /* + * 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 + * occured, and if so interrupt ourselves. Yuck. - mycroft + */ + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) + raise(SIGINT); + } + if (jp->state == JOBDONE) + +#endif + freejob(jp); + INTON; + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +/* + * 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. + * + */ + +static inline int +waitproc(int block, int *status) +{ + int flags; + + flags = 0; +#ifdef JOBS + if (jobctl) + flags |= WUNTRACED; +#endif + if (block == 0) + flags |= WNOHANG; + return wait3(status, flags, (struct rusage *)NULL); +} + +static int +dowait(int block, struct job *job) +{ + int pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + int core; + int sig; + + TRACE(("dowait(%d) called\n", block)); + do { + pid = waitproc(block, &status); + TRACE(("wait returns %d, status=%d\n", pid, status)); + } while (!(block & 2) && pid == -1 && errno == EINTR); + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = jobtab ; jp < jobtab + njobs ; jp++) { + if (jp->used) { + done = 1; + stopped = 1; + for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { + if (sp->pid == -1) + continue; + if (sp->pid == pid) { + TRACE(("Changing status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + stopped = 0; + else if (WIFSTOPPED(sp->status)) + done = 0; + } + if (stopped) { /* stopped or done */ + int state = done? JOBDONE : JOBSTOPPED; + if (jp->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); + jp->state = state; +#ifdef JOBS + if (done && curjob == jp - jobtab + 1) + curjob = 0; /* no current job */ +#endif + } + } + } + } + INTON; + if (! rootshell || ! iflag || (job && thisjob == job)) { + core = WCOREDUMP(status); +#ifdef JOBS + if (WIFSTOPPED(status)) sig = WSTOPSIG(status); + else +#endif + if (WIFEXITED(status)) sig = 0; + else sig = WTERMSIG(status); + + if (sig != 0 && sig != SIGINT && sig != SIGPIPE) { + if (thisjob != job) + out2fmt("%d: ", pid); +#ifdef JOBS + if (sig == SIGTSTP && rootshell && iflag) + out2fmt("%%%ld ", + (long)(job - jobtab + 1)); +#endif + if (sig < NSIG && sys_siglist[sig]) + out2str(sys_siglist[sig]); + else + out2fmt("Signal %d", sig); + if (core) + out2str(" - core dumped"); + out2c('\n'); + } else { + TRACE(("Not printing status: status=%d, sig=%d\n", + status, sig)); + } + } else { + TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); + if (thisjob) + thisjob->changed = 1; + } + return pid; +} + + + + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ +static int +stoppedjobs(void) +{ + int jobno; + struct job *jp; + + if (job_warning) + return (0); + for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { + if (jp->used == 0) + continue; + if (jp->state == JOBSTOPPED) { + out2str("You have stopped jobs.\n"); + job_warning = 2; + return (1); + } + } + + return (0); +} + +/* + * Return a string identifying a command (to be printed by the + * jobs command. + */ + +static char *cmdnextc; +static int cmdnleft; +#define MAXCMDTEXT 200 + +static void +cmdputs(const char *s) +{ + const char *p; + char *q; + char c; + int subtype = 0; + + if (cmdnleft <= 0) + return; + p = s; + q = cmdnextc; + while ((c = *p++) != '\0') { + if (c == CTLESC) + *q++ = *p++; + else if (c == CTLVAR) { + *q++ = '$'; + if (--cmdnleft > 0) + *q++ = '{'; + subtype = *p++; + } else if (c == '=' && subtype != 0) { + *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; + subtype = 0; + } else if (c == CTLENDVAR) { + *q++ = '}'; + } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE) + cmdnleft++; /* ignore it */ + else + *q++ = c; + if (--cmdnleft <= 0) { + *q++ = '.'; + *q++ = '.'; + *q++ = '.'; + break; + } + } + cmdnextc = q; +} + + +static void +cmdtxt(const union node *n) +{ + union node *np; + struct nodelist *lp; + const char *p; + int i; + char s[2]; + + if (n == NULL) + return; + switch (n->type) { + case NSEMI: + cmdtxt(n->nbinary.ch1); + cmdputs("; "); + cmdtxt(n->nbinary.ch2); + break; + case NAND: + cmdtxt(n->nbinary.ch1); + cmdputs(" && "); + cmdtxt(n->nbinary.ch2); + break; + case NOR: + cmdtxt(n->nbinary.ch1); + cmdputs(" || "); + cmdtxt(n->nbinary.ch2); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + cmdtxt(lp->n); + if (lp->next) + cmdputs(" | "); + } + break; + case NSUBSHELL: + cmdputs("("); + cmdtxt(n->nredir.n); + cmdputs(")"); + break; + case NREDIR: + case NBACKGND: + cmdtxt(n->nredir.n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + cmdtxt(n->nif.ifpart); + cmdputs("..."); + break; + case NWHILE: + cmdputs("while "); + goto until; + case NUNTIL: + cmdputs("until "); +until: + cmdtxt(n->nbinary.ch1); + cmdputs("; do "); + cmdtxt(n->nbinary.ch2); + cmdputs("; done"); + break; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in ..."); + break; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in ..."); + break; + case NDEFUN: + cmdputs(n->narg.text); + cmdputs("() ..."); + break; + case NCMD: + for (np = n->ncmd.args ; np ; np = np->narg.next) { + cmdtxt(np); + if (np->narg.next) + cmdputs(spcstr); + } + for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { + cmdputs(spcstr); + cmdtxt(np); + } + break; + case NARG: + cmdputs(n->narg.text); + break; + case NTO: + p = ">"; i = 1; goto redir; + case NAPPEND: + p = ">>"; i = 1; goto redir; + case NTOFD: + p = ">&"; i = 1; goto redir; + case NTOOV: + p = ">|"; i = 1; goto redir; + case NFROM: + p = "<"; i = 0; goto redir; + case NFROMFD: + p = "<&"; i = 0; goto redir; + case NFROMTO: + p = "<>"; i = 0; goto redir; +redir: + if (n->nfile.fd != i) { + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + } + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + s[1] = '\0'; + cmdputs(s); + } else { + cmdtxt(n->nfile.fname); + } + break; + case NHERE: + case NXHERE: + cmdputs("<<..."); + break; + default: + cmdputs("???"); + break; + } +} + + +static char * +commandtext(const union node *n) +{ + char *name; + + cmdnextc = name = ckmalloc(MAXCMDTEXT); + cmdnleft = MAXCMDTEXT - 4; + cmdtxt(n); + *cmdnextc = '\0'; + return name; +} + + +static void waitonint(int sig) { + intreceived = 1; + return; +} +/* + * Routines to check for mail. (Perhaps make part of main.c?) + */ + + +#define MAXMBOXES 10 + + +static int nmboxes; /* number of mailboxes */ +static time_t mailtime[MAXMBOXES]; /* times of mailboxes */ + + + +/* + * Print appropriate message(s) if mail has arrived. If the argument is + * nozero, then the value of MAIL has changed, so we just update the + * values. + */ + +static void +chkmail(int silent) +{ + int i; + const char *mpath; + char *p; + char *q; + struct stackmark smark; + struct stat statb; + + if (silent) + nmboxes = 10; + if (nmboxes == 0) + return; + setstackmark(&smark); + mpath = mpathset()? mpathval() : mailval(); + for (i = 0 ; i < nmboxes ; i++) { + p = padvance(&mpath, nullstr); + if (p == NULL) + break; + if (*p == '\0') + continue; + for (q = p ; *q ; q++); +#ifdef DEBUG + if (q[-1] != '/') + abort(); +#endif + q[-1] = '\0'; /* delete trailing '/' */ + if (stat(p, &statb) < 0) + statb.st_size = 0; + if (statb.st_size > mailtime[i] && ! silent) { + out2fmt(snlfmt, + pathopt? pathopt : "you have mail"); + } + mailtime[i] = statb.st_size; + } + nmboxes = i; + popstackmark(&smark); +} + +#define PROFILE 0 + +#if PROFILE +static short profile_buf[16384]; +extern int etext(); +#endif + +static void read_profile (const char *); +static void cmdloop (int); +static void options (int); +static void setoption (int, int); +static void procargs (int, 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(argc, argv) + int argc; + char **argv; +{ + struct jmploc jmploc; + struct stackmark smark; + volatile int state; + const char *shinit; + + BLTINCMD = find_builtin("builtin"); + EXECCMD = find_builtin("exec"); + EVALCMD = find_builtin("eval"); + +#ifndef BB_FEATURE_SH_FANCY_PROMPT + unsetenv("PS1"); + unsetenv("PS2"); +#endif + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif +#if defined(linux) || defined(__GNU__) + signal(SIGCHLD, SIG_DFL); +#endif + state = 0; + if (setjmp(jmploc.loc)) { + INTOFF; + /* + * When a shell procedure is executed, we raise the + * exception EXSHELLPROC to clean up before executing + * the shell procedure. + */ + switch (exception) { + case EXSHELLPROC: + rootpid = getpid(); + rootshell = 1; + minusc = NULL; + state = 3; + break; + + case EXEXEC: + exitstatus = exerrno; + break; + + case EXERROR: + exitstatus = 2; + break; + + default: + break; + } + + if (exception != EXSHELLPROC) { + if (state == 0 || iflag == 0 || ! rootshell) + exitshell(exitstatus); + } + reset(); + if (exception == EXINT) { + out2c('\n'); + } + 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(); + rootshell = 1; + init(); + setstackmark(&smark); + procargs(argc, argv); + if (argv[0] && argv[0][0] == '-') { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + read_profile(".profile"); + } +state2: + state = 3; +#ifndef linux + if (getuid() == geteuid() && getgid() == getegid()) { +#endif + if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { + state = 3; + read_profile(shinit); + } +#ifndef linux + } +#endif +state3: + state = 4; + if (sflag == 0 || minusc) { + static int sigs[] = { + SIGINT, SIGQUIT, SIGHUP, +#ifdef SIGTSTP + SIGTSTP, +#endif + SIGPIPE + }; +#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) + int i; + + for (i = 0; i < SIGSSIZE; i++) + setsignal(sigs[i]); + } + + if (minusc) + evalstring(minusc, 0); + + if (sflag || minusc == NULL) { +state4: /* XXX ??? - why isn't this before the "if" statement */ + cmdloop(1); + } +#if PROFILE + monitor(0); +#endif + exitshell(exitstatus); + /* 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)); + setstackmark(&smark); + for (;;) { + if (pendingsigs) + dotrap(); + inter = 0; + if (iflag && top) { + inter++; + showjobs(1); + chkmail(0); + flushall(); + } + 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); + setstackmark(&smark); + if (evalskip == SKIPFILE) { + evalskip = 0; + break; + } + } + popstackmark(&smark); +} + + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +static void +read_profile(name) + 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(const 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 compatable we should do a path + * search for the file, which is necessary to find sub-commands. + */ + + +static inline char * +find_dot_file(mybasename) + char *mybasename; +{ + char *fullname; + const char *path = pathval(); + struct stat statb; + + /* don't try this for absolute or relative paths */ + if (strchr(mybasename, '/')) + return mybasename; + + while ((fullname = padvance(&path, mybasename)) != 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("%s: not found", mybasename); + /* NOTREACHED */ +} + +static int +dotcmd(argc, argv) + int argc; + char **argv; +{ + struct strlist *sp; + exitstatus = 0; + + for (sp = cmdenviron; sp ; sp = sp->next) + setvareq(savestr(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]); + setinputfile(fullname, 1); + commandname = fullname; + cmdloop(0); + popfile(); + popstackmark(&smark); + } + return exitstatus; +} + + +static int +exitcmd(argc, argv) + int argc; + char **argv; +{ + if (stoppedjobs()) + return 0; + if (argc > 1) + exitstatus = number(argv[1]); + else + exitstatus = oexitstatus; + exitshell(exitstatus); + /* NOTREACHED */ +} + +static pointer +stalloc(int nbytes) +{ + char *p; + + nbytes = ALIGN(nbytes); + if (nbytes > stacknleft) { + int blocksize; + struct stack_block *sp; + + blocksize = nbytes; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + INTOFF; + sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += nbytes; + stacknleft -= nbytes; + return p; +} + + +static void +stunalloc(pointer p) +{ +#ifdef DEBUG + if (p == NULL) { /*DEBUG */ + write(2, "stunalloc\n", 10); + abort(); + } +#endif + if (!(stacknxt >= (char *)p && (char *)p >= stackp->space)) { + p = stackp->space; + } + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + +static void +setstackmark(struct stackmark *mark) +{ + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; + mark->marknext = markp; + markp = mark; +} + + +static 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; + 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. + */ + +static void +growstackblock(void) { + char *p; + int newlen = ALIGN(stacknleft * 2 + 100); + char *oldspace = stacknxt; + int oldlen = stacknleft; + struct stack_block *sp; + struct stack_block *oldstackp; + + if (stacknxt == stackp->space && stackp != &stackbase) { + INTOFF; + oldstackp = stackp; + sp = stackp; + stackp = sp->prev; + sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen); + sp->prev = stackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + { + /* Stack marks pointing to the start of the old block + * must be relocated to point to the new block + */ + struct stackmark *xmark; + xmark = markp; + while (xmark != NULL && xmark->stackp == oldstackp) { + xmark->stackp = stackp; + xmark->stacknxt = stacknxt; + xmark->stacknleft = stacknleft; + xmark = xmark->marknext; + } + } + INTON; + } else { + p = stalloc(newlen); + memcpy(p, oldspace, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ + } +} + + + +static inline void +grabstackblock(int len) +{ + len = ALIGN(len); + stacknxt += len; + stacknleft -= len; +} + + + +/* + * The following routines are somewhat easier to use that the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + + +static char * +growstackstr(void) { + int len = stackblocksize(); + if (herefd >= 0 && len >= 1024) { + xwrite(herefd, stackblock(), len); + sstrnleft = len - 1; + return stackblock(); + } + growstackblock(); + sstrnleft = stackblocksize() - len - 1; + return stackblock() + len; +} + + +/* + * Called from CHECKSTRSPACE. + */ + +static char * +makestrspace(size_t newlen) { + int len = stackblocksize() - sstrnleft; + do { + growstackblock(); + sstrnleft = stackblocksize() - len; + } while (sstrnleft < newlen); + return stackblock() + len; +} + + + +static void +ungrabstackstr(char *s, char *p) +{ + stacknleft += stacknxt - s; + stacknxt = s; + sstrnleft = stacknleft - (p - s); +} +/* + * Miscelaneous builtins. + */ + + +#undef rflag + +//#ifdef __GLIBC__ +static mode_t getmode(const void *, mode_t); +static void *setmode(const char *); +//#endif + +#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 +typedef long rlim_t; +#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(argc, argv) + 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)) { + putprompt(prompt); + flushall(); + } + 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') + STPUTC(c, p); + continue; + } + if (!rflag && c == '\\') { + backslash++; + continue; + } + if (c == '\n') + break; + if (startword && *ifs == ' ' && strchr(ifs, c)) { + continue; + } + startword = 0; + if (backslash && c == '\\') { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + STPUTC(c, p); + } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + startword = 1; + STARTSTACKSTR(p); + } else { + STPUTC(c, p); + } + } + STACKSTRNUL(p); + /* Remove trailing blanks */ + while (stackblock() <= --p && strchr(ifs, *p) != NULL) + *p = '\0'; + setvar(*ap, stackblock(), 0); + while (*++ap != NULL) + setvar(*ap, nullstr, 0); + return status; +} + + + +static int +umaskcmd(argc, argv) + int argc; + char **argv; +{ + char *ap; + int 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 u[4], g[4], o[4]; + + i = 0; + if ((mask & S_IRUSR) == 0) + u[i++] = 'r'; + if ((mask & S_IWUSR) == 0) + u[i++] = 'w'; + if ((mask & S_IXUSR) == 0) + u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if ((mask & S_IRGRP) == 0) + g[i++] = 'r'; + if ((mask & S_IWGRP) == 0) + g[i++] = 'w'; + if ((mask & S_IXGRP) == 0) + g[i++] = 'x'; + g[i] = '\0'; + + i = 0; + if ((mask & S_IROTH) == 0) + o[i++] = 'r'; + if ((mask & S_IWOTH) == 0) + o[i++] = 'w'; + if ((mask & S_IXOTH) == 0) + o[i++] = 'x'; + o[i] = '\0'; + + printf("u=%s,g=%s,o=%s\n", u, g, o); + } else { + printf("%.4o\n", mask); + } + } else { + if (is_digit((unsigned char)*ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else { + void *set; + + INTOFF; + if ((set = setmode(ap)) != 0) { + mask = getmode(set, ~mask & 0777); + ckfree(set); + } + INTON; + if (!set) + 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(processes)", RLIMIT_NPROC, 1, 'p' }, +#endif +#ifdef RLIMIT_NOFILE + { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_VMEM + { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, +#endif +#ifdef RLIMIT_SWAP + { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' }, +#endif + { (char *) 0, 0, 0, '\0' } +}; + +static int +ulimitcmd(argc, argv) + int argc; + char **argv; +{ + int c; + rlim_t val = 0; + enum { SOFT = 0x1, HARD = 0x2 } + how = SOFT | HARD; + const struct limits *l; + int set, all = 0; + int optc, what; + struct rlimit limit; + + what = 'f'; + while ((optc = nextopt("HSatfdsmcnpl")) != '\0') + switch (optc) { + case 'H': + how = HARD; + break; + case 'S': + how = SOFT; + break; + case 'a': + all = 1; + break; + default: + what = optc; + } + + for (l = limits; l->name && l->option != what; l++) + ; + if (!l->name) + error("internal error (%c)", what); + + set = *argptr ? 1 : 0; + if (set) { + char *p = *argptr; + + if (all || argptr[1]) + error("too many arguments"); + if (strcmp(p, "unlimited") == 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); + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + + printf("%-20s ", l->name); + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else + { + val /= l->factor; + printf("%lld\n", (long long) val); + } + } + 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 { + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else + { + val /= l->factor; + printf("%lld\n", (long long) val); + } + } + return 0; +} +/* + * prefix -- see if pfx is a prefix of string. + */ + +static int +prefix(char const *pfx, char const *string) +{ + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return 1; +} + +/* + * Return true if s is a string of digits, and save munber in intptr + * nagative is bad + */ + +static int +is_number(const char *p, int *intptr) +{ + int ret = 0; + + do { + if (! is_digit(*p)) + return 0; + ret *= 10; + ret += digit_val(*p); + p++; + } while (*p != '\0'); + + *intptr = ret; + return 1; +} + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +static int +number(const char *s) +{ + int i; + if (! is_number(s, &i)) + error("Illegal number: %s", s); + return i; +} + +/* + * Produce a possibly single quoted string suitable as input to the shell. + * The return string is allocated on the stack. + */ + +static char * +single_quote(const char *s) { + char *p; + + STARTSTACKSTR(p); + + do { + char *q = p; + size_t len1, len1p, len2, len2p; + + len1 = strcspn(s, "'"); + len2 = strspn(s + len1, "'"); + + len1p = len1 ? len1 + 2 : len1; + switch (len2) { + case 0: + len2p = 0; + break; + case 1: + len2p = 2; + break; + default: + len2p = len2 + 2; + } + + CHECKSTRSPACE(len1p + len2p + 1, p); + + if (len1) { + *p = '\''; + q = p + 1 + len1; + memcpy(p + 1, s, len1); + *q++ = '\''; + s += len1; + } + + switch (len2) { + case 0: + break; + case 1: + *q++ = '\\'; + *q = '\''; + s++; + break; + default: + *q = '"'; + q += 1 + len2; + memcpy(q + 1, s, len2); + *q = '"'; + s += len2; + } + + STADJUST(len1p + len2p, p); + } while (*s); + + USTPUTC(0, p); + + return grabstackstr(p); +} + +/* + * Like strdup but works with the ash stack. + */ + +static char * +sstrdup(const char *p) +{ + size_t len = strlen(p) + 1; + return memcpy(stalloc(len), p, len); +} + + +/* + * Routine for dealing with parsed shell commands. + */ + + +static void sizenodelist (const struct nodelist *); +static struct nodelist *copynodelist (const struct nodelist *); +static char *nodesavestr (const char *); + +static void +calcsize(const union node *n) +{ + if (n == NULL) + return; + funcblocksize += nodesize[n->type]; + switch (n->type) { + case NSEMI: + case NAND: + case NOR: + case NWHILE: + case NUNTIL: + calcsize(n->nbinary.ch2); + calcsize(n->nbinary.ch1); + break; + case NCMD: + calcsize(n->ncmd.redirect); + calcsize(n->ncmd.args); + 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 NIF: + calcsize(n->nif.elsepart); + calcsize(n->nif.ifpart); + calcsize(n->nif.test); + break; + case NFOR: + funcstringsize += strlen(n->nfor.var) + 1; + calcsize(n->nfor.body); + calcsize(n->nfor.args); + break; + case NCASE: + calcsize(n->ncase.cases); + calcsize(n->ncase.expr); + break; + case NCLIST: + calcsize(n->nclist.body); + calcsize(n->nclist.pattern); + calcsize(n->nclist.next); + break; + case NDEFUN: + case NARG: + sizenodelist(n->narg.backquote); + funcstringsize += strlen(n->narg.text) + 1; + calcsize(n->narg.next); + break; + case NTO: + case NFROM: + case NFROMTO: + case NAPPEND: + case NTOOV: + 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(const struct nodelist *lp) +{ + while (lp) { + funcblocksize += ALIGN(sizeof(struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + +static union node * +copynode(const union node *n) +{ + union node *new; + + if (n == NULL) + return NULL; + new = funcblock; + funcblock = (char *) funcblock + nodesize[n->type]; + switch (n->type) { + case NSEMI: + case NAND: + case NOR: + case NWHILE: + case NUNTIL: + new->nbinary.ch2 = copynode(n->nbinary.ch2); + new->nbinary.ch1 = copynode(n->nbinary.ch1); + break; + case NCMD: + new->ncmd.redirect = copynode(n->ncmd.redirect); + new->ncmd.args = copynode(n->ncmd.args); + new->ncmd.assign = copynode(n->ncmd.assign); + new->ncmd.backgnd = n->ncmd.backgnd; + break; + case NPIPE: + new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); + new->npipe.backgnd = n->npipe.backgnd; + break; + case NREDIR: + case NBACKGND: + case NSUBSHELL: + new->nredir.redirect = copynode(n->nredir.redirect); + new->nredir.n = copynode(n->nredir.n); + break; + case NIF: + new->nif.elsepart = copynode(n->nif.elsepart); + new->nif.ifpart = copynode(n->nif.ifpart); + new->nif.test = copynode(n->nif.test); + break; + case NFOR: + new->nfor.var = nodesavestr(n->nfor.var); + new->nfor.body = copynode(n->nfor.body); + new->nfor.args = copynode(n->nfor.args); + break; + case NCASE: + new->ncase.cases = copynode(n->ncase.cases); + new->ncase.expr = copynode(n->ncase.expr); + break; + case NCLIST: + new->nclist.body = copynode(n->nclist.body); + new->nclist.pattern = copynode(n->nclist.pattern); + new->nclist.next = copynode(n->nclist.next); + break; + case NDEFUN: + case NARG: + new->narg.backquote = copynodelist(n->narg.backquote); + new->narg.text = nodesavestr(n->narg.text); + new->narg.next = copynode(n->narg.next); + break; + case NTO: + case NFROM: + case NFROMTO: + case NAPPEND: + case NTOOV: + 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(const struct nodelist *lp) +{ + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock = (char *) funcblock + ALIGN(sizeof(struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + +static char * +nodesavestr(const char *s) +{ + const char *p = s; + char *q = funcstring; + char *rtn = funcstring; + + while ((*q++ = *p++) != '\0') + continue; + funcstring = q; + return rtn; +} + +#ifdef ASH_GETOPTS +static int getopts (char *, char *, char **, int *, int *); +#endif + + +/* + * Process the shell command line arguments. + */ + +static void +procargs(argc, argv) + int argc; + char **argv; +{ + int i; + + argptr = argv; + if (argc > 0) + argptr++; + for (i = 0; i < NOPTS; i++) + optent_val(i) = 2; + options(1); + if (*argptr == NULL && minusc == NULL) + 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 (optent_val(i) == 2) + optent_val(i) = 0; + arg0 = argv[0]; + if (sflag == 0 && minusc == NULL) { + commandname = argv[0]; + arg0 = *argptr++; + setinputfile(arg0, 0); + commandname = arg0; + } + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (argptr && minusc && *argptr) + arg0 = *argptr++; + + shellparam.p = argptr; + shellparam.optind = 1; + shellparam.optoff = -1; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + optschanged(); +} + + + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +static inline void +minus_o(const char *name, int val) +{ + int i; + + if (name == NULL) { + out1str("Current option settings\n"); + for (i = 0; i < NOPTS; i++) + printf("%-16s%s\n", optent_name(optlist[i]), + optent_val(i) ? "on" : "off"); + } else { + for (i = 0; i < NOPTS; i++) + if (equal(name, optent_name(optlist[i]))) { + setoption(optent_letter(optlist[i]), val); + return; + } + error("Illegal option -o %s", name); + } +} + + +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) { + char *q; +#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ + if (*p == '\0') +#endif + q = *argptr++; + if (q == NULL || minusc != NULL) + error("Bad -c option"); + minusc = q; +#ifdef NOHACK + break; +#endif + } else if (c == 'o') { + minus_o(*argptr, val); + if (*argptr) + argptr++; + } else { + setoption(c, val); + } + } + } +} + + +static void +setoption(int flag, int val) +{ + int i; + + for (i = 0; i < NOPTS; i++) + if (optent_letter(optlist[i]) == flag) { + optent_val(i) = val; + if (val) { + /* #%$ hack for ksh semantics */ + if (flag == 'V') + Eflag = 0; + else if (flag == 'E') + Vflag = 0; + } + return; + } + error("Illegal option -%c", flag); + /* NOTREACHED */ +} + + + +/* + * Set the shell parameters. + */ + +static void +setparam(char **argv) +{ + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; + shellparam.optind = 1; + shellparam.optoff = -1; +} + + +/* + * Free the list of positional parameters. + */ + +static 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. + */ + +static int +shiftcmd(argc, argv) + 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); + shellparam.optind = 1; + shellparam.optoff = -1; + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +static int +setcmd(argc, argv) + int argc; + char **argv; +{ + if (argc == 1) + return showvarscmd(argc, argv); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +static void +getoptsreset(const char *value) +{ + shellparam.optind = number(value); + shellparam.optoff = -1; +} + +#ifdef BB_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 ASH_GETOPTS +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +static int +getoptscmd(argc, argv) + 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); +} + +/* + * Safe version of setvar, returns 1 on success 0 on failure. + */ + +static int +setvarsafe(name, val, flags) + const char *name, *val; + int flags; +{ + struct jmploc jmploc; + struct jmploc *volatile savehandler = handler; + int err = 0; +#ifdef __GNUC__ + (void) &err; +#endif + + if (setjmp(jmploc.loc)) + err = 1; + else { + handler = &jmploc; + setvar(name, val, flags); + } + handler = savehandler; + return err; +} + +static int +getopts(optstr, optvar, optfirst, myoptind, optoff) + char *optstr; + char *optvar; + char **optfirst; + int *myoptind; + int *optoff; +{ + char *p, *q; + char c = '?'; + int done = 0; + int err = 0; + char s[10]; + char **optnext = optfirst + *myoptind - 1; + + if (*myoptind <= 1 || *optoff < 0 || !(*(optnext - 1)) || + strlen(*(optnext - 1)) < *optoff) + p = NULL; + else + p = *(optnext - 1) + *optoff; + if (p == NULL || *p == '\0') { + /* Current word is done, advance */ + if (optnext == NULL) + return 1; + p = *optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + *myoptind = optnext - optfirst + 1; + 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 { + out2fmt("Illegal option -%c\n", c); + (void) unsetvar("OPTARG"); + } + c = '?'; + goto bad; + } + 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 { + out2fmt("No arg for -%c option\n", c); + (void) unsetvar("OPTARG"); + c = '?'; + } + goto bad; + } + + if (p == *optnext) + optnext++; + setvarsafe("OPTARG", p, 0); + p = NULL; + } + else + setvarsafe("OPTARG", "", 0); + *myoptind = optnext - optfirst + 1; + goto out; + +bad: + *myoptind = 1; + p = NULL; +out: + *optoff = p ? p - *(optnext - 1) : -1; + snprintf(s, sizeof(s), "%d", *myoptind); + err |= setvarsafe("OPTIND", s, VNOFUNC); + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(optvar, s, 0); + if (err) { + *myoptind = 1; + *optoff = -1; + exraise(EXERROR); + } + return done; +} +#endif + +/* + * 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; +} + +static void +flushall() { + INTOFF; + fflush(stdout); + INTON; +} + + +static void +out2fmt(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +/* + * Version of write which resumes after a signal is caught. + */ + +static int +xwrite(int fd, const char *buf, int nbytes) +{ + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } + } +} + + +/* + * 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 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 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 *list (int); +static union node *andor (void); +static union node *pipeline (void); +static union node *command (void); +static union node *simplecmd (void); +static void parsefname (void); +static void parseheredoc (void); +static int peektoken (void); +static int readtoken (void); +static int xxreadtoken (void); +static int readtoken1 (int, char const *, char *, int); +static int noexpand (char *); +static void synexpect (int) __attribute__((noreturn)); +static void synerror (const char *) __attribute__((noreturn)); +static void setprompt (int); + + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +static union node * +parsecmd(int interact) +{ + int t; + + tokpushback = 0; + doprompt = interact; + if (doprompt) + setprompt(1); + else + setprompt(0); + needprompt = 0; + t = readtoken(); + if (t == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +static union node * +list(nlflag) + int nlflag; +{ + union node *n1, *n2, *n3; + int tok; + + checkkwd = 2; + if (nlflag == 0 && tokendlist[peektoken()]) + return NULL; + n1 = NULL; + for (;;) { + n2 = andor(); + tok = readtoken(); + if (tok == TBACKGND) { + if (n2->type == NCMD || n2->type == NPIPE) { + n2->ncmd.backgnd = 1; + } else if (n2->type == NREDIR) { + n2->type = NBACKGND; + } else { + n3 = (union node *)stalloc(sizeof (struct nredir)); + n3->type = NBACKGND; + n3->nredir.n = n2; + n3->nredir.redirect = NULL; + n2 = n3; + } + } + 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) + return n1; + } else { + tokpushback++; + } + checkkwd = 2; + if (tokendlist[peektoken()]) + return n1; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return n1; + default: + if (nlflag) + synexpect(-1); + tokpushback++; + return n1; + } + } +} + + + +static union node * +andor() { + union node *n1, *n2, *n3; + int t; + + checkkwd = 1; + n1 = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n1; + } + checkkwd = 2; + 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() { + union node *n1, *n2, *pipenode; + struct nodelist *lp, *prev; + int negate; + + negate = 0; + TRACE(("pipeline: entered\n")); + if (readtoken() == TNOT) { + negate = !negate; + checkkwd = 1; + } 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 = 2; + 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() { + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + int t; + + redir = NULL; + n1 = NULL; + rpp = &redir; + + switch (readtoken()) { + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + n1->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + n2->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + if (readtoken() != TFI) + synexpect(TFI); + checkkwd = 1; + break; + case TWHILE: + case TUNTIL: { + int got; + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.ch1 = list(0); + if ((got=readtoken()) != TDO) { +TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); + synexpect(TDO); + } + n1->nbinary.ch2 = list(0); + if (readtoken() != TDONE) + synexpect(TDONE); + checkkwd = 1; + break; + } + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.var = wordtext; + checkkwd = 1; + if (readtoken() == TIN) { + app = ≈ + 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 { + static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, + '@', '=', '\0'}; + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = argvars; + 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 = 2; + if (readtoken() != TDO) + synexpect(TDO); + n1->nfor.body = list(0); + if (readtoken() != TDONE) + synexpect(TDONE); + checkkwd = 1; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + if (readtoken() != TWORD) + synexpect(TWORD); + n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + n2->narg.next = NULL; + do { + checkkwd = 1; + } while (readtoken() == TNL); + if (lasttoken != TIN) + synerror("expecting \"in\""); + cpp = &n1->ncase.cases; + checkkwd = 2, readtoken(); + do { + 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 (checkkwd = 2, readtoken() != TPIPE) + break; + app = &ap->narg.next; + readtoken(); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + synexpect(TRP); + cp->nclist.body = list(0); + + checkkwd = 2; + if ((t = readtoken()) != TESAC) { + if (t != TENDCASE) + synexpect(TENDCASE); + else + checkkwd = 2, readtoken(); + } + cpp = &cp->nclist.next; + } while(lasttoken != TESAC); + *cpp = NULL; + checkkwd = 1; + break; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + if (readtoken() != TRP) + synexpect(TRP); + checkkwd = 1; + break; + case TBEGIN: + n1 = list(0); + if (readtoken() != TEND) + synexpect(TEND); + checkkwd = 1; + break; + /* Handle an empty command like other simple commands. */ + case TSEMI: + case TAND: + case TOR: + case TNL: + case TEOF: + case TRP: + case TBACKGND: + /* + * An empty command before a ; doesn't make much sense, and + * should certainly be disallowed in the case of `if ;'. + */ + if (!redir) + synexpect(-1); + case TWORD: + case TREDIR: + tokpushback++; + n1 = simplecmd(); + return n1; + default: + synexpect(-1); + /* NOTREACHED */ + } + + /* Now check for redirection which may follow command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (n1->type != NSUBSHELL) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + + return n1; +} + + +static union node * +simplecmd() { + union node *args, **app; + union node *n = NULL; + union node *vars, **vpp; + union node **rpp, *redir; + + args = NULL; + app = &args; + vars = NULL; + vpp = &vars; + redir = NULL; + rpp = &redir; + + checkalias = 2; + for (;;) { + switch (readtoken()) { + case TWORD: + case TASSIGN: + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + if (lasttoken == TWORD) { + *app = n; + app = &n->narg.next; + } else { + *vpp = n; + vpp = &n->narg.next; + } + 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 + ) { + /* We have a function */ + if (readtoken() != TRP) + synexpect(TRP); + n->type = NDEFUN; + checkkwd = 2; + 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.backgnd = 0; + 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; +} + +static 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 (here->striptabs) { + while (*wordtext == '\t') + wordtext++; + } + if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) + synerror("Illegal eof marker for << redirection"); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + fixredir(n, wordtext, 0); + } else { + n->nfile.fname = makename(); + } +} + + +/* + * Input any here documents. + */ + +static void +parseheredoc() { + struct heredoc *here; + union node *n; + + while (heredoclist) { + here = heredoclist; + heredoclist = here->next; + 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; + } +} + +static int +peektoken() { + int t; + + t = readtoken(); + tokpushback++; + return (t); +} + +static int +readtoken() { + int t; + +#ifdef ASH_ALIAS + int savecheckalias = checkalias; + int savecheckkwd = checkkwd; + struct alias *ap; +#endif + +#ifdef DEBUG + int alreadyseen = tokpushback; +#endif + +#ifdef ASH_ALIAS +top: +#endif + + t = xxreadtoken(); + +#ifdef ASH_ALIAS + checkalias = savecheckalias; +#endif + + if (checkkwd) { + /* + * eat newlines + */ + if (checkkwd == 2) { + checkkwd = 0; + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } + checkkwd = 0; + /* + * check for keywords + */ + if (t == TWORD && !quoteflag) + { + const char *const *pp; + + if ((pp = findkwd(wordtext))) { + lasttoken = t = pp - parsekwd + KWDOFFSET; + TRACE(("keyword %s recognized\n", tokname[t])); + goto out; + } + } + } + + + if (t != TWORD) { + if (t != TREDIR) { + checkalias = 0; + } + } else if (checkalias == 2 && isassignment(wordtext)) { + lasttoken = t = TASSIGN; +#ifdef ASH_ALIAS + } else if (checkalias) { + if (!quoteflag && (ap = lookupalias(wordtext, 1)) != NULL) { + if (*ap->val) { + pushstring(ap->val, strlen(ap->val), ap); + } + checkkwd = savecheckkwd; + goto top; + } + checkalias = 0; +#endif + } +out: +#ifdef DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * In all cases, the variable startlinno is set to the number of the line + * on which the token starts. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define RETURN(token) return lasttoken = token + +static int +xxreadtoken() { + 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 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); + else + setprompt(0); + continue; + } + pungetc(); + goto breakloop; + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + if (pgetc() == ';') + RETURN(TENDCASE); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + default: + goto breakloop; + } + } +breakloop: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); +#undef RETURN +} + + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEARITH() {goto parsearith; parsearith_return:;} + +static int +readtoken1(firstc, syntax, eofmark, striptabs) + int firstc; + char const *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; + char const *prevsyntax; /* syntax before arithmetic */ +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &out; + (void) "ef; + (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(3, out); /* permit 3 calls to USTPUTC */ + switch(syntax[c]) { + case CNL: /* '\n' */ + if (syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + c = pgetc(); + goto loop; /* continue outer loop */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if ((eofmark == NULL || dblquote) && + dqvarnest == 0) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + case CBACK: /* backslash */ + c = pgetc2(); + if (c == PEOF) { + USTPUTC('\\', out); + pungetc(); + } else if (c == '\n') { + if (doprompt) + setprompt(2); + else + setprompt(0); + } else { + if (dblquote && c != '\\' && c != '`' && c != '$' + && (c != '"' || eofmark != NULL)) + USTPUTC('\\', out); + if (SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + else if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + USTPUTC(c, out); + quotef++; + } + break; + case CSQUOTE: + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + syntax = SQSYNTAX; + break; + case CDQUOTE: + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + syntax = DQSYNTAX; + dblquote = 1; + break; + case CENDQUOTE: + if (eofmark != NULL && arinest == 0 && + varnest == 0) { + USTPUTC(c, out); + } else { + if (arinest) { + syntax = ARISYNTAX; + dblquote = 0; + } else if (eofmark == NULL && + dqvarnest == 0) { + syntax = BASESYNTAX; + dblquote = 0; + } + quotef++; + } + 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 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 ASH_ALIAS + if (c != PEOA) +#endif + USTPUTC(c, out); + + } + c = pgetc_macro(); + } + } +endword: + if (syntax == ARISYNTAX) + synerror("Missing '))'"); + if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + synerror("Unterminated quoted string"); + if (varnest != 0) { + startlinno = plinno; + synerror("Missing '}'"); + } + USTPUTC('\0', out); + len = out - stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + PARSEREDIR(); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + return lasttoken = TWORD; +/* end of readtoken routine */ + + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + */ + +checkend: { + if (eofmark) { +#ifdef 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, strlen(line), NULL); + } + } + } + } + goto checkend_return; +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +parseredir: { + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc(); + if (c == '>') + np->type = NAPPEND; + else if (c == '&') + np->type = NTOFD; + else if (c == '|') + np->type = NTOOV; + 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 || + (c != '(' && c != '{' && !is_name(c) && !is_special(c)) + ) { + USTPUTC('$', out); + pungetc(); + } else if (c == '(') { /* $(command) or $((arith)) */ + if (pgetc() == '(') { + PARSEARITH(); + } else { + pungetc(); + PARSEBACKQNEW(); + } + } else { + USTPUTC(CTLVAR, out); + typeloc = out - 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 && is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (c > PEOA && is_in_name(c)); + } else if (is_digit(c)) { + do { + USTPUTC(c, out); + c = pgetc(); + } while (is_digit(c)); + } + else if (is_special(c)) { + USTPUTC(c, out); + c = pgetc(); + } + else +badsub: synerror("Bad substitution"); + + 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; + *(stackblock() + typeloc) = subtype | flags; + if (subtype != VSNORMAL) { + varnest++; + if (dblquote) { + 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; + int 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 - 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; + int 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); + else + setprompt(0); + /* + * 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) { + break; + } + /* fall through */ + + case PEOF: +#ifdef 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 - 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(0); + + 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; +} + +/* + * 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; +} + +} /* end of readtoken */ + + +/* + * Returns true if the text contains nothing to expand (no dollar signs + * or backquotes). + */ + +static int +noexpand(text) + char *text; + { + char *p; + char c; + + p = text; + while ((c = *p++) != '\0') { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + p++; + else if (BASESYNTAX[(int)c] == CCTL) + return 0; + } + return 1; +} + + +/* + * Return true if the argument is a legal variable name (a letter or + * underscore followed by zero or more letters, underscores, and digits). + */ + +static int +goodname(const char *name) +{ + const char *p; + + p = name; + if (! is_name(*p)) + return 0; + while (*++p) { + if (! is_in_name(*p)) + return 0; + } + return 1; +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +static void +synexpect(token) + int token; +{ + char msg[64]; + + if (token >= 0) { + snprintf(msg, 64, "%s unexpected (expecting %s)", + tokname[lasttoken], tokname[token]); + } else { + snprintf(msg, 64, "%s unexpected", tokname[lasttoken]); + } + synerror(msg); + /* NOTREACHED */ +} + + +static void +synerror(const char *msg) +{ + if (commandname) + out2fmt("%s: %d: ", commandname, startlinno); + out2fmt("Syntax error: %s\n", msg); + error((char *)NULL); + /* NOTREACHED */ +} + + +/* + * called by editline -- any expansions to the prompt + * should be added here. + */ +static void +setprompt(int whichprompt) +{ + char *prompt; + switch (whichprompt) { + case 1: + prompt = ps1val(); + break; + case 2: + prompt = ps2val(); + break; + default: /* 0 */ + prompt = ""; + } + putprompt(prompt); +} + + +/* + * 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(const union node *redir) +{ + int pip[2]; + int len = 0; + + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + xwrite(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) + xwrite(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 inline int +openredirect(const 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; + } + case NTOOV: + fname = redir->nfile.expfname; +#ifdef O_CREAT + if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + goto ecreate; +#else + if ((f = creat(fname, 0666)) < 0) + goto ecreate; +#endif + break; + case NAPPEND: + fname = redir->nfile.expfname; +#ifdef O_APPEND + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + goto ecreate; +#else + if ((f = open(fname, O_WRONLY)) < 0 + && (f = creat(fname, 0666)) < 0) + goto ecreate; + lseek(f, (off_t)0, 2); +#endif + 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)); +} + + +/* + * 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. + */ + +static void +redirect(union node *redir, int flags) +{ + union node *n; + struct redirtab *sv = NULL; + int i; + int fd; + int newfd; + int try; + int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */ + + if (flags & REDIR_PUSH) { + sv = ckmalloc(sizeof (struct redirtab)); + for (i = 0 ; i < 10 ; i++) + sv->renamed[i] = EMPTY; + sv->next = redirlist; + redirlist = sv; + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + try = 0; + if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && + n->ndup.dupfd == fd) + continue; /* redirect from/to same file descriptor */ + + INTOFF; + newfd = openredirect(n); + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + if (newfd == fd) { + try++; + } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { + switch (errno) { + case EBADF: + if (!try) { + dupredirect(n, newfd, fd1dup); + try++; + break; + } + /* FALLTHROUGH*/ + default: + if (newfd >= 0) { + close(newfd); + } + INTON; + error("%d: %m", fd); + /* NOTREACHED */ + } + } + if (!try) { + close(fd); + if (flags & REDIR_PUSH) { + sv->renamed[fd] = i; + } + } + } else if (fd != newfd) { + close(fd); + } + if (fd == 0) + fd0_redirected++; + if (!try) + dupredirect(n, newfd, fd1dup); + INTON; + } +} + + +static void +dupredirect(const union node *redir, int f, int fd1dup) +{ + int fd = redir->nfile.fd; + + if(fd==1) + fd1dup = 0; + if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (redir->ndup.dupfd!=1 || fd1dup!=1) + dup_as_newfd(redir->ndup.dupfd, fd); + } + return; + } + + if (f != fd) { + dup_as_newfd(f, fd); + close(f); + } + return; +} + + + +/* + * Undo the effects of the last redirection. + */ + +static void +popredir(void) +{ + struct redirtab *rp = redirlist; + int i; + + INTOFF; + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] != EMPTY) { + if (i == 0) + fd0_redirected--; + close(i); + if (rp->renamed[i] >= 0) { + dup_as_newfd(rp->renamed[i], i); + close(rp->renamed[i]); + } + } + } + redirlist = rp->next; + ckfree(rp); + INTON; +} + +/* + * Discard all saved file descriptors. + */ + +static void +clearredir(void) { + struct redirtab *rp; + int i; + + for (rp = redirlist ; rp ; rp = rp->next) { + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] >= 0) { + close(rp->renamed[i]); + } + rp->renamed[i] = EMPTY; + } + } +} + + +/* + * Copy a file descriptor to be >= to. Returns -1 + * if the source file descriptor is closed, EMPTY if there are no unused + * file descriptors left. + */ + +static int +dup_as_newfd(from, to) + 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; +} + +/*#ifdef __weak_alias +__weak_alias(getmode,_getmode) +__weak_alias(setmode,_setmode) +#endif*/ + +#ifndef S_ISTXT +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define S_ISTXT __S_ISVTX +#else +#define S_ISTXT S_ISVTX +#endif +#endif + +#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ +#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ + +typedef struct bitcmd { + char cmd; + char cmd2; + mode_t bits; +} BITCMD; + +#define CMD2_CLR 0x01 +#define CMD2_SET 0x02 +#define CMD2_GBITS 0x04 +#define CMD2_OBITS 0x08 +#define CMD2_UBITS 0x10 + +static BITCMD *addcmd (BITCMD *, int, int, int, u_int); +static void compress_mode (BITCMD *); +#ifdef SETMODE_DEBUG +static void dumpmode (BITCMD *); +#endif + +/* + * Given the old mode and an array of bitcmd structures, apply the operations + * described in the bitcmd structures to the old mode, and return the new mode. + * Note that there is no '=' command; a strict assignment is just a '-' (clear + * bits) followed by a '+' (set bits). + */ +static mode_t +getmode(bbox, omode) + const void *bbox; + mode_t omode; +{ + const BITCMD *set; + mode_t clrval, newmode, value; + + _DIAGASSERT(bbox != NULL); + + set = (const BITCMD *)bbox; + newmode = omode; + for (value = 0;; set++) + switch(set->cmd) { + /* + * When copying the user, group or other bits around, we "know" + * where the bits are in the mode so that we can do shifts to + * copy them around. If we don't use shifts, it gets real + * grundgy with lots of single bit checks and bit sets. + */ + case 'u': + value = (newmode & S_IRWXU) >> 6; + goto common; + + case 'g': + value = (newmode & S_IRWXG) >> 3; + goto common; + + case 'o': + value = newmode & S_IRWXO; +common: if (set->cmd2 & CMD2_CLR) { + clrval = + (set->cmd2 & CMD2_SET) ? S_IRWXO : value; + if (set->cmd2 & CMD2_UBITS) + newmode &= ~((clrval<<6) & set->bits); + if (set->cmd2 & CMD2_GBITS) + newmode &= ~((clrval<<3) & set->bits); + if (set->cmd2 & CMD2_OBITS) + newmode &= ~(clrval & set->bits); + } + if (set->cmd2 & CMD2_SET) { + if (set->cmd2 & CMD2_UBITS) + newmode |= (value<<6) & set->bits; + if (set->cmd2 & CMD2_GBITS) + newmode |= (value<<3) & set->bits; + if (set->cmd2 & CMD2_OBITS) + newmode |= value & set->bits; + } + break; + + case '+': + newmode |= set->bits; + break; + + case '-': + newmode &= ~set->bits; + break; + + case 'X': + if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) + newmode |= set->bits; + break; + + case '\0': + default: +#ifdef SETMODE_DEBUG + (void)printf("getmode:%04o -> %04o\n", omode, newmode); +#endif + return (newmode); + } +} + +#define ADDCMD(a, b, c, d) do { \ + if (set >= endset) { \ + BITCMD *newset; \ + setlen += SET_LEN_INCR; \ + newset = realloc(saveset, sizeof(BITCMD) * setlen); \ + if (newset == NULL) { \ + free(saveset); \ + return (NULL); \ + } \ + set = newset + (set - saveset); \ + saveset = newset; \ + endset = newset + (setlen - 2); \ + } \ + set = addcmd(set, (a), (b), (c), (d)); \ +} while (/*CONSTCOND*/0) + +#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) + +static void * +setmode(p) + const char *p; +{ + int perm, who; + char op, *ep; + BITCMD *set, *saveset, *endset; + sigset_t mysigset, sigoset; + mode_t mask; + int equalopdone = 0; /* pacify gcc */ + int permXbits, setlen; + + if (!*p) + return (NULL); + + /* + * Get a copy of the mask for the permissions that are mask relative. + * Flip the bits, we want what's not set. Since it's possible that + * the caller is opening files inside a signal handler, protect them + * as best we can. + */ + sigfillset(&mysigset); + (void)sigprocmask(SIG_BLOCK, &mysigset, &sigoset); + (void)umask(mask = umask(0)); + mask = ~mask; + (void)sigprocmask(SIG_SETMASK, &sigoset, NULL); + + setlen = SET_LEN + 2; + + if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL) + return (NULL); + saveset = set; + endset = set + (setlen - 2); + + /* + * If an absolute number, get it and return; disallow non-octal digits + * or illegal bits. + */ + if (is_digit((unsigned char)*p)) { + perm = (mode_t)strtol(p, &ep, 8); + if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) { + free(saveset); + return (NULL); + } + ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); + set->cmd = 0; + return (saveset); + } + + /* + * Build list of structures to set/clear/copy bits as described by + * each clause of the symbolic mode. + */ + for (;;) { + /* First, find out which bits might be modified. */ + for (who = 0;; ++p) { + switch (*p) { + case 'a': + who |= STANDARD_BITS; + break; + case 'u': + who |= S_ISUID|S_IRWXU; + break; + case 'g': + who |= S_ISGID|S_IRWXG; + break; + case 'o': + who |= S_IRWXO; + break; + default: + goto getop; + } + } + +getop: if ((op = *p++) != '+' && op != '-' && op != '=') { + free(saveset); + return (NULL); + } + if (op == '=') + equalopdone = 0; + + who &= ~S_ISTXT; + for (perm = 0, permXbits = 0;; ++p) { + switch (*p) { + case 'r': + perm |= S_IRUSR|S_IRGRP|S_IROTH; + break; + case 's': + /* + * If specific bits where requested and + * only "other" bits ignore set-id. + */ + if (who == 0 || (who & ~S_IRWXO)) + perm |= S_ISUID|S_ISGID; + break; + case 't': + /* + * If specific bits where requested and + * only "other" bits ignore set-id. + */ + if (who == 0 || (who & ~S_IRWXO)) { + who |= S_ISTXT; + perm |= S_ISTXT; + } + break; + case 'w': + perm |= S_IWUSR|S_IWGRP|S_IWOTH; + break; + case 'X': + permXbits = S_IXUSR|S_IXGRP|S_IXOTH; + break; + case 'x': + perm |= S_IXUSR|S_IXGRP|S_IXOTH; + break; + case 'u': + case 'g': + case 'o': + /* + * When ever we hit 'u', 'g', or 'o', we have + * to flush out any partial mode that we have, + * and then do the copying of the mode bits. + */ + if (perm) { + ADDCMD(op, who, perm, mask); + perm = 0; + } + if (op == '=') + equalopdone = 1; + if (op == '+' && permXbits) { + ADDCMD('X', who, permXbits, mask); + permXbits = 0; + } + ADDCMD(*p, who, op, mask); + break; + + default: + /* + * Add any permissions that we haven't already + * done. + */ + if (perm || (op == '=' && !equalopdone)) { + if (op == '=') + equalopdone = 1; + ADDCMD(op, who, perm, mask); + perm = 0; + } + if (permXbits) { + ADDCMD('X', who, permXbits, mask); + permXbits = 0; + } + goto apply; + } + } + +apply: if (!*p) + break; + if (*p != ',') + goto getop; + ++p; + } + set->cmd = 0; +#ifdef SETMODE_DEBUG + (void)printf("Before compress_mode()\n"); + dumpmode(saveset); +#endif + compress_mode(saveset); +#ifdef SETMODE_DEBUG + (void)printf("After compress_mode()\n"); + dumpmode(saveset); +#endif + return (saveset); +} + +static BITCMD * +addcmd(set, op, who, oparg, mask) + BITCMD *set; + int oparg, who; + int op; + u_int mask; +{ + + _DIAGASSERT(set != NULL); + + switch (op) { + case '=': + set->cmd = '-'; + set->bits = who ? who : STANDARD_BITS; + set++; + + op = '+'; + /* FALLTHROUGH */ + case '+': + case '-': + case 'X': + set->cmd = op; + set->bits = (who ? who : mask) & oparg; + break; + + case 'u': + case 'g': + case 'o': + set->cmd = op; + if (who) { + set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | + ((who & S_IRGRP) ? CMD2_GBITS : 0) | + ((who & S_IROTH) ? CMD2_OBITS : 0); + set->bits = (mode_t)~0; + } else { + set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; + set->bits = mask; + } + + if (oparg == '+') + set->cmd2 |= CMD2_SET; + else if (oparg == '-') + set->cmd2 |= CMD2_CLR; + else if (oparg == '=') + set->cmd2 |= CMD2_SET|CMD2_CLR; + break; + } + return (set + 1); +} + +#ifdef SETMODE_DEBUG +static void +dumpmode(set) + BITCMD *set; +{ + + _DIAGASSERT(set != NULL); + + for (; set->cmd; ++set) + (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", + set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", + set->cmd2 & CMD2_CLR ? " CLR" : "", + set->cmd2 & CMD2_SET ? " SET" : "", + set->cmd2 & CMD2_UBITS ? " UBITS" : "", + set->cmd2 & CMD2_GBITS ? " GBITS" : "", + set->cmd2 & CMD2_OBITS ? " OBITS" : ""); +} +#endif + +/* + * Given an array of bitcmd structures, compress by compacting consecutive + * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', + * 'g' and 'o' commands continue to be separate. They could probably be + * compacted, but it's not worth the effort. + */ +static void +compress_mode(set) + BITCMD *set; +{ + BITCMD *nset; + int setbits, clrbits, Xbits, op; + + _DIAGASSERT(set != NULL); + + for (nset = set;;) { + /* Copy over any 'u', 'g' and 'o' commands. */ + while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { + *set++ = *nset++; + if (!op) + return; + } + + for (setbits = clrbits = Xbits = 0;; nset++) { + if ((op = nset->cmd) == '-') { + clrbits |= nset->bits; + setbits &= ~nset->bits; + Xbits &= ~nset->bits; + } else if (op == '+') { + setbits |= nset->bits; + clrbits &= ~nset->bits; + Xbits &= ~nset->bits; + } else if (op == 'X') + Xbits |= nset->bits & ~setbits; + else + break; + } + if (clrbits) { + set->cmd = '-'; + set->cmd2 = 0; + set->bits = clrbits; + set++; + } + if (setbits) { + set->cmd = '+'; + set->cmd2 = 0; + set->bits = setbits; + set++; + } + if (Xbits) { + set->cmd = 'X'; + set->cmd2 = 0; + set->bits = Xbits; + set++; + } + } +} +#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 *); + + +static void +showtree(n) + union node *n; +{ + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static void +shtree(n, ind, pfx, fp) + 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, "", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + + +static void +shcmd(cmd, fp) + 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 NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NTOOV: 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(arg, fp) + union node *arg; + FILE *fp; + { + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + printf("\n", arg->type); + fflush(stdout); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch (*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + if (subtype == VSLENGTH) + putc('#', fp); + + while (*p != '=') + putc(*p++, fp); + + if (subtype & VSNUL) + putc(':', fp); + + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + case VSTRIMLEFT: + putc('#', fp); + break; + case VSTRIMLEFTMAX: + putc('#', fp); + putc('#', fp); + break; + case VSTRIMRIGHT: + putc('%', fp); + break; + case VSTRIMRIGHTMAX: + putc('%', fp); + putc('%', fp); + break; + case VSLENGTH: + break; + default: + printf("", subtype); + } + 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(amount, pfx, fp) + int amount; + char *pfx; + FILE *fp; +{ + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} +#endif + + + +/* + * Debugging stuff. + */ + + +#ifdef DEBUG +FILE *tracefile; + +#if DEBUG == 2 +static int debug = 1; +#else +static int debug = 0; +#endif + + +static void +trputc(c) + int c; +{ + if (tracefile == NULL) + return; + putc(c, tracefile); + if (c == '\n') + fflush(tracefile); +} + +static void +trace(const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + if (tracefile != NULL) { + (void) vfprintf(tracefile, fmt, va); + if (strchr(fmt, '\n')) + (void) fflush(tracefile); + } + va_end(va); +} + + +static void +trputs(s) + const char *s; +{ + if (tracefile == NULL) + return; + fputs(s, tracefile); + if (strchr(s, '\n')) + fflush(tracefile); +} + + +static void +trstring(s) + char *s; +{ + char *p; + char c; + + if (tracefile == NULL) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch (*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; + case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +} + + +static void +trargs(ap) + char **ap; +{ + if (tracefile == NULL) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } + fflush(tracefile); +} + + +static void +opentrace() { + char s[100]; +#ifdef O_APPEND + int flags; +#endif + + if (!debug) + return; +#ifdef not_this_way + { + char *p; + if ((p = getenv("HOME")) == NULL) { + if (geteuid() == 0) + p = "/"; + else + p = "/tmp"; + } + strcpy(s, p); + strcat(s, "/trace"); + } +#else + strcpy(s, "./trace"); +#endif /* not_this_way */ + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + return; + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + fputs("\nTracing started.\n", tracefile); + fflush(tracefile); +} +#endif /* DEBUG */ + + +/* + * The trap builtin. + */ + +static int +trapcmd(argc, argv) + int argc; + char **argv; +{ + char *action; + char **ap; + int signo; + + if (argc <= 1) { + for (signo = 0 ; signo < NSIG ; signo++) { + if (trap[signo] != NULL) { + char *p; + const char *sn; + + p = single_quote(trap[signo]); + sn = sys_siglist[signo]; + if(sn==NULL) + sn = u_signal_names(0, &signo, 0); + if(sn==NULL) + sn = "???"; + printf("trap -- %s %s\n", p, sn); + stunalloc(p); + } + } + return 0; + } + ap = argv + 1; + if (argc == 2) + 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; +} + + + + + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +static void +setsignal(int signo) +{ + int action; + char *t; + 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; +#ifdef JOBS + case SIGTSTP: + case SIGTTOU: + if (mflag) + action = S_IGN; + break; +#endif + } + } + + t = &sigmode[signo - 1]; + if (*t == 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)) { + *t = S_IGN; /* don't hard ignore these */ + } else + *t = S_HARD_IGN; + } else { + *t = S_RESET; /* force to be set */ + } + } + if (*t == S_HARD_IGN || *t == 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; + sigemptyset(&act.sa_mask); + sigaction(signo, &act, 0); +} + +/* + * Ignore a signal. + */ + +static void +ignoresig(signo) + 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. + */ + +static void +onsig(int signo) +{ + if (signo == SIGINT && trap[SIGINT] == NULL) { + onint(); + return; + } + gotsig[signo - 1] = 1; + pendingsigs++; +} + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +static void +dotrap(void) +{ + int i; + int savestatus; + + for (;;) { + for (i = 1 ; ; i++) { + if (gotsig[i - 1]) + break; + if (i >= NSIG - 1) + goto done; + } + gotsig[i - 1] = 0; + savestatus=exitstatus; + evalstring(trap[i], 0); + exitstatus=savestatus; + } +done: + pendingsigs = 0; +} + +/* + * Called to exit the shell. + */ + +static void +exitshell(int status) +{ + struct jmploc loc1, loc2; + char *p; + + TRACE(("exitshell(%d) pid=%d\n", status, getpid())); + if (setjmp(loc1.loc)) { + goto l1; + } + if (setjmp(loc2.loc)) { + goto l2; + } + handler = &loc1; + if ((p = trap[0]) != NULL && *p != '\0') { + trap[0] = NULL; + evalstring(p, 0); + } +l1: handler = &loc2; /* probably unnecessary */ + flushall(); +#ifdef JOBS + setjobctl(0); +#endif +l2: _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; +} + +static struct var **hashvar (const char *); +static void showvars (const char *, int, int); +static struct var **findvar (struct var **, const char *); + +/* + * Initialize the varable symbol tables and import the environment + */ + +/* + * This routine initializes the builtin variables. It is called when the + * shell is initialized and again when a shell procedure is spawned. + */ + +static void +initvar() { + const struct varinit *ip; + struct var *vp; + struct var **vpp; + + for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { + if ((vp->flags & VEXPORT) == 0) { + vpp = hashvar(ip->text); + vp->next = *vpp; + *vpp = vp; + vp->text = strdup(ip->text); + vp->flags = ip->flags; + vp->func = ip->func; + } + } + /* + * PS1 depends on uid + */ + if ((vps1.flags & VEXPORT) == 0) { + vpp = hashvar("PS1="); + vps1.next = *vpp; + *vpp = &vps1; + vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# "); + vps1.flags = VSTRFIXED|VTEXTFIXED; + } +} + +/* + * Set the value of a variable. The flags argument is ored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +static void +setvar(name, val, flags) + const char *name, *val; + int flags; +{ + const char *p; + int len; + int namelen; + char *nameeq; + int isbad; + int vallen = 0; + + isbad = 0; + p = name; + if (! is_name(*p)) + isbad = 1; + p++; + for (;;) { + if (! is_in_name(*p)) { + if (*p == '\0' || *p == '=') + break; + isbad = 1; + } + p++; + } + namelen = p - name; + if (isbad) + error("%.*s: bad variable name", namelen, name); + len = namelen + 2; /* 2 is space for '=' and '\0' */ + if (val == NULL) { + flags |= VUNSET; + } else { + len += vallen = strlen(val); + } + INTOFF; + nameeq = ckmalloc(len); + memcpy(nameeq, name, namelen); + nameeq[namelen] = '='; + if (val) { + memcpy(nameeq + namelen + 1, val, vallen + 1); + } else { + nameeq[namelen + 1] = '\0'; + } + setvareq(nameeq, flags); + 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. + */ + +static void +setvareq(s, flags) + char *s; + int flags; +{ + struct var *vp, **vpp; + + vpp = hashvar(s); + flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); + if ((vp = *findvar(vpp, s))) { + if (vp->flags & VREADONLY) { + size_t len = strchr(s, '=') - s; + error("%.*s: is read only", len, s); + } + INTOFF; + + if (vp->func && (flags & VNOFUNC) == 0) + (*vp->func)(strchr(s, '=') + 1); + + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + + vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags; + vp->text = s; + + /* + * We could roll this to a function, to handle it as + * a regular variable function callback, but why bother? + */ + if (iflag && (vp == &vmpath || (vp == &vmail && !mpathset()))) + chkmail(1); + INTON; + return; + } + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags; + vp->text = s; + vp->next = *vpp; + vp->func = NULL; + *vpp = vp; +} + + + +/* + * Process a linked list of variable assignments. + */ + +static void +listsetvar(mylist) + struct strlist *mylist; + { + struct strlist *lp; + + INTOFF; + for (lp = mylist ; lp ; lp = lp->next) { + setvareq(savestr(lp->text), 0); + } + INTON; +} + + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +static const char * +lookupvar(name) + const char *name; + { + struct var *v; + + if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { + return strchr(v->text, '=') + 1; + } + return NULL; +} + + + +/* + * Search the environment of a builtin command. + */ + +static const char * +bltinlookup(const char *name) +{ + const struct strlist *sp; + + for (sp = cmdenviron ; sp ; sp = sp->next) { + if (varequal(sp->text, name)) + return strchr(sp->text, '=') + 1; + } + return lookupvar(name); +} + + + +/* + * Generate a list of exported variables. This routine is used to construct + * the third argument to execve when executing a program. + */ + +static char ** +environment() { + int nenv; + struct var **vpp; + struct var *vp; + char **env; + char **ep; + + nenv = 0; + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + nenv++; + } + ep = env = stalloc((nenv + 1) * sizeof *env); + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + *ep++ = vp->text; + } + *ep = NULL; + return env; +} + + +/* + * Called when a shell procedure is invoked to clear out nonexported + * variables. It is also necessary to reallocate variables of with + * VSTACK set since these are currently allocated on the stack. + */ + +static void +shprocvar(void) { + struct var **vpp; + struct var *vp, **prev; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (prev = vpp ; (vp = *prev) != NULL ; ) { + if ((vp->flags & VEXPORT) == 0) { + *prev = vp->next; + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + if ((vp->flags & VSTRFIXED) == 0) + ckfree(vp); + } else { + if (vp->flags & VSTACK) { + vp->text = savestr(vp->text); + vp->flags &=~ VSTACK; + } + prev = &vp->next; + } + } + } + initvar(); +} + + + +/* + * Command to list all variables which are set. Currently this command + * is invoked from the set command when the set command is called without + * any variables. + */ + +static int +showvarscmd(argc, argv) + int argc; + char **argv; +{ + showvars(nullstr, VUNSET, VUNSET); + return 0; +} + + + +/* + * The export and readonly commands. + */ + +static int +exportcmd(argc, argv) + int argc; + char **argv; +{ + struct var *vp; + char *name; + const char *p; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + int pflag; + + listsetvar(cmdenviron); + pflag = (nextopt("p") == 'p'); + if (argc > 1 && !pflag) { + while ((name = *argptr++) != NULL) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + if ((vp = *findvar(hashvar(name), name))) { + vp->flags |= flag; + goto found; + } + } + setvar(name, p, flag); +found:; + } + } else { + showvars(argv[0], flag, 0); + } + return 0; +} + + +/* + * The "local" command. + */ + +/* funcnest nonzero if we are currently evaluating a function */ + +static int +localcmd(argc, argv) + int argc; + char **argv; +{ + char *name; + + if (! funcnest) + error("Not in a function"); + while ((name = *argptr++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +static void +mklocal(name) + 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 optet_vals); + lvp->text = memcpy(p, optet_vals, sizeof optet_vals); + vp = NULL; + } else { + vpp = hashvar(name); + vp = *findvar(vpp, name); + if (vp == NULL) { + if (strchr(name, '=')) + setvareq(savestr(name), VSTRFIXED); + else + setvar(name, NULL, VSTRFIXED); + vp = *vpp; /* the new variable */ + lvp->text = NULL; + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (strchr(name, '=')) + setvareq(savestr(name), 0); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + + +/* + * Called after a function returns. + */ + +static void +poplocalvars() { + struct localvar *lvp; + struct var *vp; + + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + if (vp == NULL) { /* $- saved */ + memcpy(optet_vals, lvp->text, sizeof optet_vals); + ckfree(lvp->text); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + (void)unsetvar(vp->text); + } else { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } +} + + +static int +setvarcmd(argc, argv) + int argc; + char **argv; +{ + if (argc <= 2) + return unsetcmd(argc, argv); + else if (argc == 3) + setvar(argv[1], argv[2], 0); + else + error("List assignment not implemented"); + return 0; +} + + +/* + * The unset builtin command. We unset the function before we unset the + * variable to allow a function to be unset when there is a readonly variable + * with the same name. + */ + +static int +unsetcmd(argc, argv) + int argc; + char **argv; +{ + char **ap; + int i; + int flg_func = 0; + int flg_var = 0; + int ret = 0; + + while ((i = nextopt("vf")) != '\0') { + if (i == 'f') + flg_func = 1; + else + flg_var = 1; + } + if (flg_func == 0 && flg_var == 0) + flg_var = 1; + + for (ap = argptr; *ap ; ap++) { + if (flg_func) + unsetfunc(*ap); + if (flg_var) + ret |= unsetvar(*ap); + } + return ret; +} + + +/* + * Unset the specified variable. + */ + +static int +unsetvar(const char *s) +{ + struct var **vpp; + struct var *vp; + + vpp = findvar(hashvar(s), s); + vp = *vpp; + if (vp) { + if (vp->flags & VREADONLY) + return (1); + INTOFF; + if (*(strchr(vp->text, '=') + 1) != '\0') + setvar(s, nullstr, 0); + vp->flags &= ~VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + } + INTON; + return (0); + } + + return (0); +} + + + +/* + * 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]; +} + + + +/* + * Returns true if the two strings specify the same varable. The first + * variable name is terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +static int +varequal(const char *p, const char *q) +{ + while (*p == *q++) { + if (*p++ == '=') + return 1; + } + if (*p == '=' && *(q - 1) == '\0') + return 1; + return 0; +} + +static void +showvars(const char *myprefix, int mask, int xor) +{ + struct var **vpp; + struct var *vp; + const char *sep = myprefix == nullstr ? myprefix : spcstr; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if ((vp->flags & mask) ^ xor) { + char *p; + int len; + + p = strchr(vp->text, '=') + 1; + len = p - vp->text; + p = single_quote(p); + + printf("%s%s%.*s%s\n", myprefix, sep, len, + vp->text, p); + stunalloc(p); + } + } + } +} + +static struct var ** +findvar(struct var **vpp, const char *name) +{ + for (; *vpp; vpp = &(*vpp)->next) { + if (varequal((*vpp)->text, name)) { + break; + } + } + return vpp; +} + +/* + * Copyright (c) 1999 Herbert Xu + * This file contains code for the times builtin. + * $Id: ash.c,v 1.17 2001/08/02 05:02:45 andersen Exp $ + */ +static int timescmd (int argc, char **argv) +{ + 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; +} + +#ifdef ASH_MATH_SUPPORT +/* The let builtin. */ +int letcmd(int argc, char **argv) +{ + int errcode; + long result=0; + if (argc == 2) { + char *tmp, *expression, p[13]; + expression = strchr(argv[1], '='); + if (!expression) { + /* Cannot use 'error()' here, or the return code + * will be incorrect */ + out2fmt("sh: let: syntax error: \"%s\"\n", argv[1]); + return 0; + } + *expression = '\0'; + tmp = ++expression; + result = arith(tmp, &errcode); + if (errcode < 0) { + /* Cannot use 'error()' here, or the return code + * will be incorrect */ + out2fmt("sh: let: "); + if(errcode == -2) + out2fmt("divide by zero"); + else + out2fmt("syntax error: \"%s=%s\"\n", argv[1], expression); + return 0; + } + snprintf(p, 12, "%ld", result); + setvar(argv[1], savestr(p), 0); + } else if (argc >= 3) + synerror("invalid operand"); + return !result; +} +#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. + * + * 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 index 000000000..16ec2f823 --- /dev/null +++ b/busybox/shell/cmdedit.c @@ -0,0 +1,1521 @@ +/* vi: set sw=4 ts=4: */ +/* + * Termios command line History and Editting. + * + * Copyright (c) 1986-2001 may safely be consumed by a BSD or GPL license. + * Written by: Vladimir Oleynik + * + * Used ideas: + * Adam Rogoyski + * Dave Cinege + * Jakub Jelinek (c) 1995 + * Erik Andersen (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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +#ifdef BB_LOCALE_SUPPORT +#define Isprint(c) isprint((c)) +#else +#define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') ) +#endif + +#ifndef TEST + +#define D(x) + +#else + +#define BB_FEATURE_COMMAND_EDITING +#define BB_FEATURE_COMMAND_TAB_COMPLETION +#define BB_FEATURE_COMMAND_USERNAME_COMPLETION +#define BB_FEATURE_NONPRINTABLE_INVERSE_PUT +#define BB_FEATURE_CLEAN_UP + +#define D(x) x + +#endif /* TEST */ + +#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION +#include +#include +#endif + +#ifdef BB_FEATURE_COMMAND_EDITING + +#ifndef BB_FEATURE_COMMAND_TAB_COMPLETION +#undef BB_FEATURE_COMMAND_USERNAME_COMPLETION +#endif + +#if defined(BB_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(BB_FEATURE_SH_FANCY_PROMPT) +#define BB_FEATURE_GETUSERNAME_AND_HOMEDIR +#endif + +#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR +# ifndef TEST +# include "pwd_grp/pwd.h" +# else +# include +# endif /* TEST */ +#endif /* advanced FEATURES */ + + + +struct history { + char *s; + struct history *p; + struct history *n; +}; + +/* Maximum length of the linked list for the command line history */ +static const int MAX_HISTORY = 15; + +/* First element in command line list */ +static struct history *his_front = NULL; + +/* Last element in command line list */ +static struct history *his_end = NULL; + + +#include +#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 int history_counter = 0; /* Number of commands in history list */ +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 BB_FEATURE_SH_FANCY_PROMPT + const +#endif +char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */ + +#ifdef BB_FEATURE_GETUSERNAME_AND_HOMEDIR +static char *user_buf = ""; +static char *home_pwd_buf = ""; +static int my_euid; +#endif + +#ifdef BB_FEATURE_SH_FANCY_PROMPT +static char *hostname_buf = ""; +static int num_ok_lines = 1; +#endif + + +#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION + +#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR +static int my_euid; +#endif + +static int my_uid; +static int my_gid; + +#endif /* BB_FEATURE_COMMAND_TAB_COMPLETION */ + +/* It seems that libc5 doesn't know what a sighandler_t is... */ +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) +typedef void (*sighandler_t) (int); +#endif + +static void cmdedit_setwidth(int w, int redraw_flg); + +static void win_changed(int nsig) +{ + struct winsize win = { 0, 0, 0, 0 }; + static sighandler_t previous_SIGWINCH_handler; /* for reset */ + + /* emulate || signal call */ + if (nsig == -SIGWINCH || nsig == SIGWINCH) { + ioctl(0, TIOCGWINSZ, &win); + if (win.ws_col > 0) { + cmdedit_setwidth(win.ws_col, 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(fileno(stdin), (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); +#ifdef BB_FEATURE_CLEAN_UP + if (his_front) { + struct history *n; + + while (his_front != his_end) { + n = his_front->n; + free(his_front->s); + free(his_front); + his_front = n; + } + } +#endif +} + + +/* 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 BB_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) +{ + fputs(s, stdout); +} +static inline void beep(void) +{ + putchar('\007'); +} + +/* Move back one charactor */ +/* 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 BB_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 *)unknown; + } + + while (*prmt_ptr) { + pbuf = buf; + pbuf[1] = 0; + c = *prmt_ptr++; + if (c == '\\') { + const char *cp = prmt_ptr; + int l; + + c = process_escape_sequence(&prmt_ptr); + if(prmt_ptr==cp) { + if (*cp == 0) + break; + c = *prmt_ptr++; + switch (c) { +#ifdef BB_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 BB_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) 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 *)unknown) + free(pwd_buf); + cmdedit_prompt = prmt_mem_ptr; + cmdedit_prmt_len = prmt_len - sub_len; + put_prompt(); +} +#endif + + +/* draw promt, 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 charactor */ +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 BB_FEATURE_GETUSERNAME_AND_HOMEDIR + struct passwd *entry; + + my_euid = geteuid(); + entry = getpwuid(my_euid); + if (entry) { + user_buf = xstrdup(entry->pw_name); + home_pwd_buf = xstrdup(entry->pw_dir); + } +#endif + +#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION + +#ifndef BB_FEATURE_GETUSERNAME_AND_HOMEDIR + my_euid = geteuid(); +#endif + my_uid = getuid(); + my_gid = getgid(); +#endif /* BB_FEATURE_COMMAND_TAB_COMPLETION */ + handlers_sets |= SET_ATEXIT; + atexit(cmdedit_reset_term); /* be sure to do this only once */ + } +} + +#ifdef BB_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 BB_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)) { + + temp = xmalloc(3 + strlen(entry->pw_name)); + sprintf(temp, "~%s/", entry->pw_name); + matches = xrealloc(matches, (nm + 1) * sizeof(char *)); + + matches[nm++] = temp; + } + } + + endpwent(); + (*num_matches) = nm; + return (matches); + } +} +#endif /* BB_FEATURE_COMMAND_USERNAME_COMPLETION */ + +enum { + FIND_EXE_ONLY = 0, + FIND_DIR_ONLY = 1, + FIND_FILE_ONLY = 2, +}; + +static int path_parse(char ***p, int flags) +{ + int npth; + char *tmp; + char *pth; + + /* if not setenv PATH variable, to search cur dir "." */ + if (flags != FIND_EXE_ONLY || (pth = getenv("PATH")) == 0 || + /* PATH= or PATH=: */ + *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; /* : */ + } else + break; + } + + *p = xmalloc(npth * sizeof(char *)); + + tmp = pth; + (*p)[0] = 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; /* : */ + } 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 BB_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) == TRUE)) + 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 BB_FEATURE_NONPRINTABLE_INVERSE_PUT + if (matchBuf[i] == '\t') /* algorithm equivalent */ + int_buf[j] = ' ' | QUOT; +#endif + } +#ifdef BB_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; +} + + +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 == FALSE) { + + 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 BB_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 1) { + char *tmp1; + + beep(); + if (!matches) + return; /* not found */ + /* sort */ + qsort(matches, num_matches, sizeof(char *), match_compare); + + /* find minimal match */ + tmp = 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 i, col, l; + int sav_cursor = cursor; /* change goto_new_line() */ + + /* Go to the next line */ + goto_new_line(); + for (i = 0, col = 0; i < num_matches; i++) { + l = strlen(matches[i]); + if (l < 14) + l = 14; + printf("%-14s ", matches[i]); + if ((l += 2) > 16) + while (l % 16) { + putchar(' '); + l++; + } + col += l; + col -= (col / cmdedit_termw) * cmdedit_termw; + if (col > 60 && matches[i + 1] != NULL) { + putchar('\n'); + col = 0; + } + } + /* Go to the next line and rewrite */ + putchar('\n'); + redraw(0, len - sav_cursor); + } + } +} +#endif /* BB_FEATURE_COMMAND_TAB_COMPLETION */ + +static void get_previous_history(struct history **hp, struct history *p) +{ + if ((*hp)->s) + free((*hp)->s); + (*hp)->s = xstrdup(command_ps); + *hp = p; +} + +static inline void get_next_history(struct history **hp) +{ + get_previous_history(hp, (*hp)->n); +} + +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 (sortof 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; + struct history *hp = his_end; + + /* 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); +#ifndef linux + /* 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; +#endif + 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(); + command[0] = 0; + len = 0; + lastWasTab = FALSE; + put_prompt(); + break; + case 4: + /* Control-d -- Delete one character, or exit + * if the len=0 and no chars to delete */ + if (len == 0) { +prepare_to_die: +#if !defined(BB_ASH) + printf("exit"); + goto_new_line(); + /* cmdedit_reset_term() called in atexit */ + exit(EXIT_SUCCESS); +#else + break_out = -1; /* for control stoped jobs */ + 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 BB_FEATURE_COMMAND_TAB_COMPLETION + input_tab(&lastWasTab); +#endif + break; + case 14: + /* Control-n -- Get next command in history */ + if (hp && hp->n && hp->n->s) { + get_next_history(&hp); + goto rewrite_line; + } else { + beep(); + } + break; + case 16: + /* Control-p -- Get previous command from history */ + if (hp && hp->p) { + get_previous_history(&hp, hp->p); + goto rewrite_line; + } else { + beep(); + } + break; + case 21: + /* Control-U -- Clear line before cursor */ + if (cursor) { + strcpy(command, command + cursor); + redraw(cmdedit_y, len -= cursor); + } + 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; + } + switch (c) { +#ifdef BB_FEATURE_COMMAND_TAB_COMPLETION + case '\t': /* Alt-Tab */ + + input_tab(&lastWasTab); + break; +#endif + case 'A': + /* Up Arrow -- Get previous command from history */ + if (hp && hp->p) { + get_previous_history(&hp, hp->p); + goto rewrite_line; + } else { + beep(); + } + break; + case 'B': + /* Down Arrow -- Get next command in history */ + if (hp && hp->n && hp->n->s) { + get_next_history(&hp); + goto rewrite_line; + } else { + beep(); + } + break; + + /* Rewrite the line with the selected history item */ + rewrite_line: + /* change command */ + len = strlen(strcpy(command, hp->s)); + /* redraw and go to end line */ + redraw(cmdedit_y, 0); + break; + 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 (Ctrl-A) */ + input_backward(cursor); + break; + case '4': + case 'F': + /* End (Ctrl-E) */ + input_end(); + break; + default: + if (!(c >= '1' && c <= '9')) + c = 0; + beep(); + } + if (c >= '1' && c <= '9') + do + if (safe_read(0, &c, 1) < 1) + goto prepare_to_die; + while (c != '~'); + break; + } + + default: /* If it's regular input, do the normal thing */ +#ifdef BB_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; + + /* Handle command history log */ + if (len) { /* no put empty line */ + + struct history *h = his_end; + char *ss; + + ss = xstrdup(command); /* duplicate */ + + if (h == 0) { + /* No previous history -- this memory is never freed */ + h = his_front = xmalloc(sizeof(struct history)); + h->n = xmalloc(sizeof(struct history)); + + h->p = NULL; + h->s = ss; + h->n->p = h; + h->n->n = NULL; + h->n->s = NULL; + his_end = h->n; + history_counter++; + } else { + /* Add a new history command -- this memory is never freed */ + h->n = xmalloc(sizeof(struct history)); + + h->n->p = h; + h->n->n = NULL; + h->n->s = NULL; + h->s = ss; + his_end = h->n; + + /* After max history, remove the oldest command */ + if (history_counter >= MAX_HISTORY) { + + struct history *p = his_front->n; + + p->p = NULL; + free(his_front->s); + free(his_front); + his_front = p; + } else { + history_counter++; + } + } +#if defined(BB_FEATURE_SH_FANCY_PROMPT) + num_ok_lines++; +#endif + } + if(break_out>0) { + command[len++] = '\n'; /* set '\n' */ + command[len] = 0; + } +#if defined(BB_FEATURE_CLEAN_UP) && defined(BB_FEATURE_COMMAND_TAB_COMPLETION) + input_tab(0); /* strong free */ +#endif +#if defined(BB_FEATURE_SH_FANCY_PROMPT) + free(cmdedit_prompt); +#endif + cmdedit_reset_term(); + return len; +} + + + +#endif /* BB_FEATURE_COMMAND_EDITING */ + + +#ifdef TEST + +const char *applet_name = "debug stuff usage"; +const char *memory_exhausted = "Memory exhausted"; + +#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT +#include +#endif + +int main(int argc, char **argv) +{ + char buff[BUFSIZ]; + char *prompt = +#if defined(BB_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 BB_FEATURE_NONPRINTABLE_INVERSE_PUT + setlocale(LC_ALL, ""); +#endif + while(1) { + int l; + cmdedit_read_input(prompt, buff); + l = strlen(buff); + if(l==0) + break; + if(l > 0 && buff[l-1] == '\n') + buff[l-1] = 0; + printf("*** cmdedit_read_input() returned line =%s=\n", buff); + } + printf("*** cmdedit_read_input() detect ^C\n"); + return 0; +} + +#endif /* TEST */ diff --git a/busybox/shell/cmdedit.h b/busybox/shell/cmdedit.h new file mode 100644 index 000000000..83893572a --- /dev/null +++ b/busybox/shell/cmdedit.h @@ -0,0 +1,6 @@ +#ifndef CMDEDIT_H +#define CMDEDIT_H + +int cmdedit_read_input(char* promptStr, char* command); + +#endif /* CMDEDIT_H */ diff --git a/busybox/shell/hush.c b/busybox/shell/hush.c new file mode 100644 index 000000000..0e619f80e --- /dev/null +++ b/busybox/shell/hush.c @@ -0,0 +1,2692 @@ +/* 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 + * + * 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) 2000 by Lineo, Inc., and + * written by Erik Andersen , . + * 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 /* isalpha, isdigit */ +#include /* getpid */ +#include /* getenv, atoi */ +#include /* strchr */ +#include /* popen etc. */ +#include /* glob, of course */ +#include /* va_list */ +#include +#include +#include /* should be pretty obvious */ + +#include /* ulimit */ +#include +#include +#include + +/* #include */ +/* #define DEBUG_SHELL */ + +#ifdef BB_VER +#include "busybox.h" +#include "cmdedit.h" +#else +#define applet_name "hush" +#include "standalone.h" +#define hush_main main +#undef BB_FEATURE_SH_FANCY_PROMPT +#endif + +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_SNTX = 12 +} reserved_style; +#define FLAG_END (1<, 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) { + 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_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(); +/* "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 void lookup_param(o_string *dest, struct p_context *ctx, o_string *src); +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); +static int parse_string_outer(const char *s); +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 *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_not_written}, + {"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==unknown) + cwd = NULL; /* xgetcwd(arg) called free(arg) */ + cwd = xgetcwd((char *)cwd); + if (!cwd) + cwd = unknown; + return cwd; +} + + +/* built-in 'cd ' 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) + 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) { + error_msg("%s: no current job", child->argv[0]); + return EXIT_FAILURE; + } + } else { + if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { + 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) { + 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 { + 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 && nargv[1] == NULL) + return EXIT_FAILURE; + + /* XXX search through $PATH is missing */ + input = fopen(child->argv[1], "r"); + if (!input) { + 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); + if (o->data != NULL) 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 BB_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 BB_FEATURE_SH_FANCY_PROMPT + /* Set up the prompt */ + if (promptmode == 1) { + if (PS1) + 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 BB_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) + 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; + 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!) */ + 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; + 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]); + putenv(strdup(child->argv[i])); + } + 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 BB_FEATURE_SH_STANDALONE_SHELL + { + int argc_l; + char** argv_l=child->argv; + char *name = child->argv[0]; + +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + /* Following discussions from November 2000 on the busybox mailing + * list, the default configuration, (without + * get_last_path_component()) lets the user force use of an + * external command by specifying the full (with slashes) filename. + * If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then applets + * _aways_ override external commands, so if you want to run + * /bin/cat, it will use BusyBox cat even if /bin/cat exists on the + * filesystem and is _not_ busybox. Some systems may want this, + * most do not. */ + name = get_last_path_component(name); +#endif + /* 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); + 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) + perror_msg("waitpid"); + + /* move the shell to the foreground */ + //if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) + // 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; + + 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 = 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); + set_local_var(child->argv[i], export_me); + } + return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ + } + 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); + for (i=0; is_assignment(child->argv[i]); i++) { + putenv(strdup(child->argv[i])); + } + 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) perror_msg_and_die("pipe"); + nextout = pipefds[1]; + } else { + nextout=1; + pipefds[0] = -1; + } + + /* XXX test for failed fork()? */ + if (!(child->pid = fork())) { + /* 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) +{ + int rcode=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; + for (;pi;pi=pi->next) { + 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) continue; + 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) continue; + if (pi->num_progs == 0) continue; + 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) + perror_msg("tcsetpgrp-3"); + rcode = checkjobs(pi); + /* move the shell to the foreground */ + if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) + perror_msg("tcsetpgrp-4"); + } else { + rcode = checkjobs(pi); + } + debug_printf("checkjobs returned %d\n",rcode); + } + last_return_code=rcode; + if ( rmode == RES_IF || rmode == RES_ELIF ) + next_if_code=rcode; /* can be overwritten a number of times */ + 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; inum_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; igl_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) + error_msg_and_die("out of memory during glob"); + if (gr != 0) { /* GLOB_ABORTED ? */ + 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) { + 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) { + 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; + 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_DO | FLAG_START }, + { "while", RES_WHILE, FLAG_DO | FLAG_START }, + { "until", RES_UNTIL, FLAG_DO | FLAG_START }, + { "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) + rdata, 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"); + *new = *ctx; /* physical copy */ + initialize_context(ctx); + ctx->stack=new; + } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<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"); + 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) { + 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) { + error_msg("ambiguous redirect"); + return 1; + } + } else { + child->argv = glob_target->gl_pathv; + } + 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; + + ctx->child=prog; + /* 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; + + 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; numlength; 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) perror_msg_and_die("pipe"); + pid=fork(); + if (pid<0) { + 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 void lookup_param(o_string *dest, struct p_context *ctx, o_string *src) +{ + const char *p=NULL; + if (src->data) { + p = getenv(src->data); + if (!p) + p = get_local_var(src->data); + } + if (p) parse_string(dest, ctx, p); /* recursion */ + b_free(src); +} + +/* 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; + o_string alt=NULL_O_STRING; + char sep[]=" "; + int ch = input->peek(input); /* first character after the $ */ + debug_printf("handle_dollar: ch=%c\n",ch); + if (isalpha(ch)) { + while(ch=b_peek(input),isalnum(ch) || ch=='_') { + b_getch(input); + b_addchr(&alt,ch); + } + lookup_param(dest, ctx, &alt); + } else if (isdigit(ch)) { + i = ch-'0'; /* XXX is $0 special? */ + if (i 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_getch(input); + /* XXX maybe someone will try to escape the '}' */ + while(ch=b_getch(input),ch!=EOF && ch!='}') { + b_addchr(&alt,ch); + } + if (ch != '}') { + syntax(); + return 1; + } + lookup_param(dest, ctx, &alt); + break; + case '(': + b_getch(input); + process_command_subs(dest, ctx, input, ')'); + break; + case '*': + sep[0]=ifs[0]; + for (i=1; iquote); + } + /* 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 */ + done_word(dest, ctx); + /* 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 exeception is + * from builtin_source() */ +int parse_stream_outer(struct in_str *inp) +{ + + struct p_context ctx; + o_string temp=NULL_O_STRING; + int rcode; + do { + initialize_context(&ctx); + update_ifs_map(); + inp->promptmode=1; + rcode = parse_stream(&temp, &ctx, inp, '\n'); + done_word(&temp, &ctx); + done_pipe(&ctx,PIPE_SEQ); + run_list(ctx.list_head); + b_free(&temp); + } while (rcode != -1); /* loop on syntax errors, return on EOF */ + return 0; +} + +static int parse_string_outer(const char *s) +{ + struct in_str input; + setup_string_in_str(&input, s); + return parse_stream_outer(&input); +} + +static int parse_file_outer(FILE *f) +{ + int rcode; + struct in_str input; + setup_file_in_str(&input, f); + rcode = parse_stream_outer(&input); + 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() +{ + 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 BB_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); + 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 + 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(fileno(stdin)) && isatty(fileno(stdout))) { + interactive++; + } + + debug_printf("\ninteractive=%d\n", interactive); + if (interactive) { + /* Looks like they want an interactive shell */ + fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n"); + 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 = xfopen(argv[optind], "r"); + opt = parse_file_outer(input); + +#ifdef BB_FEATURE_CLEAN_UP + fclose(input); + if (cwd && cwd != 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); +} diff --git a/busybox/shell/lash.c b/busybox/shell/lash.c new file mode 100644 index 000000000..0af669116 --- /dev/null +++ b/busybox/shell/lash.c @@ -0,0 +1,1632 @@ +/* vi: set sw=4 ts=4: */ +/* + * lash -- the BusyBox Lame-Ass SHell + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * 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.c + */ + +//For debugging/development on the shell only... +//#define DEBUG_SHELL + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "cmdedit.h" + +#ifdef BB_LOCALE_SUPPORT +#include +#endif + +#include +#define expand_t glob_t + + +static const int MAX_READ = 128; /* size of input buffer for `read' builtin */ +#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" + + +enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE, + REDIRECT_APPEND +}; + +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; + + +struct jobset { + struct job *head; /* head of list of running jobs */ + struct job *fg; /* current foreground job */ +}; + +struct redir_struct { + enum redir_type type; /* type of redirection */ + int fd; /* file descriptor being redirected */ + char *filename; /* file to redirect fd to */ +}; + +struct child_prog { + pid_t pid; /* 0 if exited */ + char **argv; /* program name and arguments */ + int num_redirects; /* elements in redirection array */ + struct redir_struct *redirects; /* I/O redirects */ + int is_stopped; /* is the program currently running? */ + struct job *family; /* pointer back to the child's parent 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 pid_t shell_pgrp; +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 ' 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 = 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) { + error_msg("%s: no current job", child->argv[0]); + return EXIT_FAILURE; + } + } else { + if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { + 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) { + 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 { + 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 = 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 BB_FEATURE_SH_FANCY_PROMPT + if (strncmp(v, "PS1=", 4)==0) + PS1 = getenv("PS1"); +#endif + +#ifdef BB_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 */ + strcpy(string, child->argv[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=" + ** 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) + 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; +} + + +/* 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); + if (cmd->progs[i].redirects) + free(cmd->progs[i].redirects); + } + if (cmd->progs) + free(cmd->progs); + if (cmd->text) + free(cmd->text); + if (cmd->cmdbuf) + 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) + perror_msg("waitpid"); +} + +/* 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!) */ + perror_msg("error opening %s", redir->filename); + return 1; + } + + if (openfd != redir->fd) { + if (squirrel && redir->fd < 3) { + squirrel[redir->fd] = dup(redir->fd); + } + 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); + } + } +} + +static inline void cmdedit_set_initial_prompt(void) +{ +#ifndef BB_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 BB_FEATURE_SH_FANCY_PROMPT + /* Set up the prompt */ + if (shell_context == 0) { + if (PS1) + 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 BB_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] = 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 = strdup(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); + 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. */ + 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) { + 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; + 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) { + 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, *chptr; + int argc_l = 0; + int done = 0; + int argv_alloced; + int i, saw_quote = 0; + char quote = '\0'; + int count; + struct child_prog *prog; + + /* 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->redirects = NULL; + prog->is_stopped = 0; + prog->family = job; + + 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) { + 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; + + 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) { + 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) { + 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) { + 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; + + case '&': /* background */ + *inbg = 1; + case ';': /* multiple commands */ + done = 1; + return_command = *command_ptr + (src - *command_ptr) + 1; + break; + + case '\\': + src++; + if (!*src) { + 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 BB_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) { + applet_name=x->cmd; + exit (x->function(child)); + } + } +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + /* Check if the command matches any busybox internal + * commands ("applets") here. Following discussions from + * November 2000 on busybox@opensource.lineo.com, don't use + * 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]; + +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + /* If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then + * if you run /bin/cat, it will use BusyBox cat even if + * /bin/cat exists on the filesystem and is _not_ busybox. + * Some systems want this, others do not. Choose wisely. :-) + */ + name = get_last_path_component(name); +#endif + + { + 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); + perror_msg_and_die("%s", child->argv[0]); +} + +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; + + 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) + perror_msg("tcsetpgrp"); + } +} + +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) 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 squirrel[] = {-1, -1, -1}; + int rcode; + setup_redirects(child, squirrel); + rcode = x->function(child); + restore_redirects(squirrel); + return rcode; + } + } + } + + if (!(child->pid = fork())) { + /* 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; + pid_t parent_pgrp; + int i; + int inbg; + int status; + newjob.job_list = &job_list; + newjob.job_context = DEFAULT_CONTEXT; + + /* save current owner of TTY so we can restore it on exit */ + parent_pgrp = tcgetpgrp(shell_terminal); + + 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) == FALSE) { + 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) + 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; + } + } 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) + perror_msg("tcsetpgrp"); + } + } + } + free(command); + + /* return controlling TTY back to parent process group before exiting */ + if (tcsetpgrp(shell_terminal, parent_pgrp)) + perror_msg("tcsetpgrp"); + + /* return exit status if called with "-c" */ + if (input == NULL && WIFEXITED(status)) + return WEXITSTATUS(status); + + return 0; +} + + +#ifdef BB_FEATURE_CLEAN_UP +void free_memory(void) +{ + if (cwd) { + free(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 + +/* 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() +{ + /* 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(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 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) + error_msg_and_die("multiple -c arguments"); + local_pending_command = xstrdup(argv[optind]); + optind++; + argv = argv+optind; + break; + case 'i': + interactive = TRUE; + break; + default: + 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(fileno(stdin)) && isatty(fileno(stdout))) { + 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 */ + printf( "\n\n" BB_BANNER " Built-in shell (lash)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); + } else if (local_pending_command==NULL) { + //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]); + input = 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 = unknown; + +#ifdef BB_FEATURE_CLEAN_UP + atexit(free_memory); +#endif + +#ifdef BB_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 index 000000000..92a0f8536 --- /dev/null +++ b/busybox/shell/msh.c @@ -0,0 +1,4868 @@ +/* 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmdedit.h" +#include "busybox.h" + + +/* -------- sh.h -------- */ +/* + * shell + */ + +#define LINELIM 2100 +#define NPUSH 8 /* limit to input nesting */ + +#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 defintions + */ +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 & */ + +/* + * actions determining the environment of a process + */ +#define BIT(i) (1<<(i)) +#define FEXEC BIT(0) /* execute without forking */ + +/* + * 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) + +static char **dolv; +static int dolc; +static int exstat; +static char gflg; +static int interactive; /* 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 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 */ + +static struct wdblock *wdlist; +static struct wdblock *iolist; + +/* + * parsing & execution environment + */ +static struct env { + char *linep; + struct io *iobase; + struct io *iop; + xint *errpt; + 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 ))(void); + +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 (unsigned u, 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 (void); +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 +#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 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 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 *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)()); + +/* + * 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 (*fn)()); +static int remap (int fd ); +static int openpipe (int *pv ); +static void closepipe (int *pv ); +static struct io *setbase (struct io *ip ); + +static struct ioarg temparg; /* temporary for PUSHIO */ +#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, int *pforked ); +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(void); +static int dohelp(void); +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, void (*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)(), int key); +static void badid(char *s ); +static int doset(struct op *t ); +static void varput (char *s, int out ); +static int dotimes(void); +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 (*fn)()); +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}, + {"{", '{'}, + {"}", '}'}, + {0, 0}, +}; + + +struct builtincmd { + const char *name; + int (*builtinfunc)(); +}; +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} +}; + +/* Globals */ +extern char **environ; /* environment pointer */ +static char **dolv; +static int dolc; +static int exstat; +static char gflg; +static int interactive; /* 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]; +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 struct ioarg ioargstack[NPUSH]; +static struct io iostack[NPUSH]; +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 *elinep = line+sizeof(line)-5; +static char *null = ""; +static int heedint =1; +static struct env e ={line, iostack, iostack-1, (xint *)NULL, FDBASE, (struct env *)NULL}; +static void (*qflag)(int) = SIG_IGN; +static char shellname[] = "/bin/sh"; +static char search[] = ":/bin:/usr/bin"; +static int startl; +static int peeksym; +static int nlseen; +static int iounit = IODEFAULT; +static YYSTYPE yylval; +static struct iobuf sharedbuf = {AFID_NOBUF}; +static struct iobuf mainbuf = {AFID_NOBUF}; +static unsigned bufid = AFID_ID; /* buffer id counter */ +static struct ioarg temparg = {0, 0, 0, AFID_NOBUF, 0}; +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; + + +#ifdef BB_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)(); + + 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, shellname); + export(shell); + + homedir = lookup("HOME"); + if (homedir->value == null) + setval(homedir, "/"); + export(homedir); + + setval(lookup("$"), itoa(getpid(), 5)); + + path = lookup("PATH"); + if (path->value == null) + setval(path, search); + export(path); + + ifs = lookup("IFS"); + if (ifs->value == null) + setval(ifs, " \t\n"); + + prompt = lookup("PS1"); +#ifdef BB_FEATURE_SH_FANCY_PROMPT + if (prompt->value == null) +#endif + setval(prompt, "$ "); + if (geteuid() == 0) { + setval(prompt, "# "); + prompt->status &= ~EXPORT; + } + cprompt = lookup("PS2"); +#ifdef BB_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; + if (newfile(name = *++argv)) + exit(1); + } + } + setdash(); + if (e.iop < iostack) { + PUSHIO(afile, 0, iof); + if (isatty(0) && isatty(1) && !cflag) { + interactive++; + printf( "\n\n" BB_BANNER " Built-in shell (msh)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); + } + } + 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)); + + for (;;) { + if (interactive && e.iop <= iostack) { +#ifdef BB_FEATURE_COMMAND_EDITING + current_prompt=prompt->value; +#else + prs(prompt->value); +#endif + } + onecommand(); + } +} + +static void +setdash() +{ + register char *cp; + register int c; + char m['z'-'a'+1]; + + cp = m; + for (c='a'; c<='z'; c++) + if (flag[c]) + *cp++ = c; + *cp = 0; + setval(lookup("-"), m); +} + +static int +newfile(s) +register char *s; +{ + register int f; + + if (strcmp(s, "-") != 0) { + f = open(s, 0); + if (f < 0) { + prs(s); + err(": cannot open"); + return(1); + } + } else + f = 0; + next(remap(f)); + return(0); +} + +static void +onecommand() +{ + register int i; + jmp_buf m1; + + 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) { + 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']) + 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() +{ + 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; + + 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; + + 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, -1)); +} + +static char * +itoa(u, n) +register unsigned u; +int n; +{ + register char *cp; + static char s[20]; + int m; + + m = 0; + if (n < 0 && (int) u < 0) { + m++; + u = -u; + } + cp = s+sizeof(s); + *--cp = 0; + do { + *--cp = u%10 + '0'; + u /= 10; + } while (--n > 0 || u); + if (m) + *--cp = '-'; + return(cp); +} + +static void +next(f) +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])) /* not an internal symbol ($# etc) */ + vp->status |= RONLY; +} + +static int +isassign(s) +register char *s; +{ + if (!isalpha((int)*s)) + return(0); + for (; *s != '='; s++) + if (*s == 0 || !isalnum(*s)) + return(0); + return(1); +} + +static int +assign(s, cf) +register char *s; +int cf; +{ + register char *cp; + struct var *vp; + + if (!isalpha(*s)) + return(0); + for (cp = s; *cp != '='; cp++) + if (*cp == 0 || !isalnum(*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; +{ + if (!isalpha(*cp++)) + return(0); + while (*cp) + if (!isalnum(*cp++)) + 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)) { + 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(65000); + brktop = brkaddr + 65000; + + 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() +{ + 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; + + t = command(cf); + if (t != NULL) { + while ((c = yylex(0)) == '|') { + if ((p = command(CONTIN)) == NULL) + SYNTAXERR; + if (t->type != TPAREN && t->type != TCOM) { + /* shell statement */ + t = block(TPAREN, t, NOBLOCK, NOWORDS); + } + t = block(TPIPE, t, p, NOWORDS); + } + peeksym = c; + } + return(t); +} + +static struct op * +andor() +{ + register struct op *t, *p; + register int c; + + t = pipeline(0); + if (t != NULL) { + while ((c = yylex(0)) == LOGAND || c == LOGOR) { + if ((p = pipeline(CONTIN)) == NULL) + SYNTAXERR; + t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); + } + peeksym = c; + } + return(t); +} + +static struct op * +c_list() +{ + register struct op *t, *p; + register int c; + + 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); + } + peeksym = c; + } + return(t); +} + + +static int +synio(cf) +int cf; +{ + register struct ioword *iop; + register int i; + register int c; + + 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); + return(1); +} + +static void +musthave(c, cf) +int c, cf; +{ + if ((peeksym = yylex(cf)) != c) + 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; + + 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; + + iosave = iolist; + iolist = NULL; + if (multiline) + cf |= CONTIN; + while (synio(cf)) + cf = 0; + switch (c = yylex(cf)) { + 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; + } + while (synio(0)) + ; + t = namelist(t); + iolist = iosave; + 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) + t = list(t, casepart()); + return(t); +} + +static struct op * +casepart() +{ + register struct op *t; + + t = newtp(); + t->type = TPAT; + t->words = pattern(); + musthave(')', 0); + t->left = c_list(); + if ((peeksym = yylex(CONTIN)) != ESAC) + musthave(BREAK, CONTIN); + 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; +{ + 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; + + t = newtp(); + t->type = type; + t->left = t1; + t->right = t2; + t->words = wp; + return(t); +} + +static int +rlookup(n) +register char *n; +{ + register struct res *rp; + + for (rp = restab; rp->r_name; rp++) + if (strcmp(rp->r_name, n) == 0) + return(rp->r_val); + return(0); +} + +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; + return(t); +} + +static struct op * +namelist(t) +register struct op *t; +{ + 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; + e.linep = line; + atstart = startl; + startl = 0; + yylval.i = 0; + +loop: + while ((c = my_getc(0)) == ' ' || c == '\t') + ; + 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 '#': + while ((c = my_getc(0)) != 0 && c != '\n') + ; + unget(c); + goto loop; + + case 0: + return(c); + + case '$': + *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 ';': + if ((c1 = dual(c)) != '\0') { + startl = 1; + return(c1); + } + startl = 1; + 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 BB_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]; + + *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 BB_FEATURE_COMMAND_EDITING + current_prompt=cprompt->value; +#else + prs(cprompt->value); +#endif + } + *e.linep++ = c; + } + *e.linep++ = c; + return(0); +} + +static int +dual(c) +register int c; +{ + char s[3]; + register char *cp = s; + + *cp++ = c; + *cp++ = my_getc(0); + *cp = 0; + if ((c = rlookup(s)) == 0) + unget(*--cp); + return(c); +} + +static void +diag(ec) +register int ec; +{ + register int c; + + 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) { + 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 brkcon bc; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) ℘ +#endif + + + if (t == NULL) + return(0); + rv = 0; + a = areanum++; + wp = (wp2 = t->words) != NULL + ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY) + : NULL; + + switch(t->type) { + case TPAREN: + case TCOM: + { + int child; + rv = forkexec(t, pin, pout, act, wp, &child); + if (child) { + exstat = rv; + leave(); + } + } + 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; + + 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 = ""; + if ((t1 = findcase(t->left, cp)) != NULL) + rv = execute(t1, pin, pout, 0); + 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); + } + return(rv); +} + +static int +forkexec( register struct op *t, int *pin, int *pout, int act, char **wp, int *pforked) +{ + int i, rv; + int (*shcom)() = NULL; + register int f; + char *cp = NULL; + struct ioword **iopp; + int resetsig; + char **owp; + + int *hpin = pin; + int *hpout = pout; + int hforked; + char *hwp; + int hinteractive; + int hintr; + struct brkcon * hbrklist; + int hexecflg; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &pin; + (void) &pout; + (void) ℘ + (void) &shcom; + (void) &cp; + (void) &resetsig; + (void) &owp; +#endif + + owp = wp; + resetsig = 0; + *pforked = 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']) + echo (cp ? wp: owp); + if (cp == NULL && t->ioact == NULL) { + while ((cp = *owp++) != NULL && assign(cp, COPYV)) + ; + return(setstatus(0)); + } + else if (cp != NULL) + shcom = inbuilt(cp); + } + t->words = wp; + f = act; + if (shcom == NULL && (f & FEXEC) == 0) { + + hpin = pin; + hpout = pout; + hforked = *pforked; + hwp = *wp; + hinteractive = interactive; + hintr = intr; + hbrklist = brklist; + hexecflg = execflg; + + i = vfork(); + if (i != 0) { + /* who wrote this crappy non vfork safe shit? */ + pin = hpin; + pout = hpout; + *pforked = hforked; + *wp = hwp; + interactive = hinteractive; + intr = hintr; + brklist = hbrklist; + execflg = hexecflg; + + *pforked = 0; + if (i == -1) + return(rv); + if (pin != NULL) + closepipe(pin); + return(pout==NULL? setstatus(waitfor(i,0)): 0); + } + + if (interactive) { + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + resetsig = 1; + } + interactive = 0; + intr = 0; + (*pforked)++; + 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"); + 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"); + return(-1); + } + while (*iopp) + if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) + return(rv); + } + if (shcom) + return(setstatus((*shcom)(t))); + /* should use FIOCEXCL */ + for (i=FDBASE; itype == TPAREN) + exit(execute(t->left, NOPIPE, NOPIPE, FEXEC)); + if (wp[0] == NULL) + exit(0); + + cp = rexecve(wp[0], wp, makenv()); + prs(wp[0]); prs(": "); warn(cp); + if (!execflg) + trap[0] = NULL; + 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; + + 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) + return((struct op **)NULL); + if (t->type == TLIST) { + if ((tp = find1case(t->left, w)) != NULL) + return(tp); + t1 = t->right; /* TPAT */ + } else + t1 = t; + for (wp = t1->words; *wp;) + if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) + return(&t1->left); + 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 BB_FEATURE_SH_STANDALONE_SHELL + char *name = c; +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + name = get_last_path_component(name); +#endif + 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 + + 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';) + ; + + execve(e.linep, v, envp); + switch (errno) { + case ENOEXEC: + *v = e.linep; + tp = *--v; + *v = e.linep; + execve("/bin/sh", v, envp); + *v = tp; + return("no Shell"); + + case ENOMEM: + return("program too big"); + + 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(argp, f) +struct ioarg *argp; +int (*f)(); +{ + 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 + + 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(); + } + wdlist = swdlist; + iolist = siolist; + failpt = ofail; + outtree = otree; + freearea(areanum--); + return(rv); +} + +/* -------- do.c -------- */ + +/* + * built-in commands: doX + */ + +static int dohelp() +{ + 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 BB_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() +{ + 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()); + 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; + + if ((cp = t->words[1]) == NULL) + return(0); + sp = any('/', cp)? ":": path->value; + while (*sp) { + tp = e.linep; + while (*sp && (*tp = *sp++) != ':') + tp++; + if (tp != e.linep) + *tp++ = '/'; + for (i = 0; (*tp++ = cp[i++]) != '\0';) + ; + if ((i = open(e.linep, 0)) >= 0) { + exstat = 0; + next(remap(i)); + return(exstat); + } + } + 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, void (*f)(int)) +{ + 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)); + 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(wp, f, key) +register char **wp; +void (*f)(); +int key; +{ + 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)) { + write(out, s, strlen(s)); + write(out, "\n", 1); + } +} + + +/* + * Copyright (c) 1999 Herbert Xu + * This file contains code for the times builtin. + */ +static int dotimes () +{ + 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))() +{ + const struct builtincmd *bp; + + for (bp = builtincmds; bp->name != NULL; bp++) + if (strcmp(bp->name, s) == 0) + return(bp->builtinfunc); + + return((int(*)())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) ℘ + (void) ≈ +#endif + 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() + +{ + register struct wdblock *wb; + register struct var *vp; + + wb = NULL; + for (vp = vlist; vp; vp = vp->next) + if (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; + + 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 + 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; + + 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)) + 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)) + scanequals = 0; + } + *e.linep++ = c; + } + *e.linep++ = 0; + return(sp); +} + +/* + * Get characters, substituting for ` and $ + */ +static int +subgetc(ec, quoted) +register int ec; +int quoted; +{ + register char c; + +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; + + c = readc(); + s = e.linep; + if (c != '{') { + *e.linep++ = c; + if (isalpha(c)) { + while ((c = readc())!=0 && isalnum(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; +{ + register int i; + char *cp; + int pf[2]; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &cp; +#endif + for (cp = e.iop->argp->aword; *cp != '`'; cp++) + if (*cp == 0) { + err("no closing `"); + return(0); + } + if (openpipe(pf) < 0) + return(0); + if ((i = vfork()) == -1) { + closepipe(pf); + err("try again"); + return(0); + } + if (i != 0) { + e.iop->argp->aword = ++cp; + close(pf[1]); + PUSHIO(afile, remap(pf[0]), quoted? qgravechar: gravechar); + return(1); + } + *cp = 0; + /* allow trapped signals */ + for (i=0; i<=_NSIG; i++) + if (ourtrap[i] && signal(i, SIG_IGN) != SIG_IGN) + signal(i, SIG_DFL); + dup2(pf[1], 1); + closepipe(pf); + flag['e'] = 0; + flag['v'] = 0; + flag['n'] = 0; + cp = strsave(e.iop->argp->aword, 0); + areanum = 1; + freehere(areanum); + freearea(areanum); /* free old space */ + e.oenv = NULL; + e.iop = (e.iobase = iostack) - 1; + unquote(cp); + interactive = 0; + PUSHIO(aword, cp, nlchar); + onecommand(); + 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; iw_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; iw_nword; i++) + DELETE(cl->w_words[i]); + DELETE(cl); + } + for(i=0; iw_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; iw_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; kw_words; + for (i=0; iw_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 != '\'' && 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; + + for (; e.iop >= e.iobase; e.iop--) + 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 BB_FEATURE_COMMAND_EDITING + current_prompt=prompt->value; +#else + prs(prompt->value); +#endif + } + } + } + if (e.iop >= iostack) + return(0); + leave(); + /* NOTREACHED */ + return(0); +} + +static void +ioecho(c) +char c; +{ + if (flag['v']) + write(2, &c, sizeof c); +} + +static void +pushio(argp, fn) +struct ioarg *argp; +int (*fn)(); +{ + if (++e.iop >= &iostack[NPUSH]) { + e.iop--; + err("Shell input nested too deeply"); + gflg++; + return; + } + e.iop->iofn = fn; + + if (argp->afid != AFID_NOBUF) + e.iop->argp = argp; + else { + e.iop->argp = ioargstack + (e.iop - iostack); + *e.iop->argp = *argp; + e.iop->argp->afbuf = e.iop == &iostack[0] ? &mainbuf : &sharedbuf; + if (isatty(e.iop->argp->afile) == 0 && + (e.iop == &iostack[0] || + lseek(e.iop->argp->afile, 0L, 1) != -1)) { + if (++bufid == AFID_NOBUF) + bufid = AFID_ID; + e.iop->argp->afid = bufid; + } + } + + 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 == gravechar || fn == qgravechar) + e.iop->task = XGRAVE; + else + e.iop->task = XOTHER; +} + +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 BB_FEATURE_COMMAND_EDITING + if (interactive) { + 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&0177: (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; + + 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, 0)); +} + +static void +closef(i) +register int i; +{ + if (i > 2) + close(i); +} + +static void +closeall() +{ + register int u; + + for (u=NUFILE; u= 0 && fd < e.iofd); + for (i=0; ih_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; + + /* 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; + + tf = mkstemp(tname); + if (tf < 0) + return; + *name = strsave(tname, areanum); + if (newenv(setjmp(errpt = ev)) != 0) + unlink(tname); + else { + pushio(e.iop->argp, e.iop->iofn); + e.iobase = e.iop; + for (;;) { + if (interactive && e.iop <= iostack) { +#ifdef BB_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 == 0) + return(-1); + 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; + + 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; + + 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/sleep.c b/busybox/sleep.c new file mode 100644 index 000000000..3bcab88ee --- /dev/null +++ b/busybox/sleep.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sleep implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int sleep_main(int argc, char **argv) +{ + if ((argc < 2) || (**(argv + 1) == '-')) { + show_usage(); + } + + if (sleep(atoi(*(++argv))) != 0) + perror_msg_and_die("sleep"); + return EXIT_SUCCESS; +} diff --git a/busybox/sort.c b/busybox/sort.c new file mode 100644 index 000000000..4f4979cc5 --- /dev/null +++ b/busybox/sort.c @@ -0,0 +1,106 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sort implementation for busybox + * + * + * Copyright (C) 2000 by 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 +#include +#include +#include "busybox.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, opt, nlines = 0; + int (*compare)(const void *, const void *) = compare_ascii; +#ifdef BB_FEATURE_SORT_REVERSE + int reverse = FALSE; +#endif +#ifdef BB_FEATURE_SORT_UNIQUE + int unique = FALSE; +#endif + + while ((opt = getopt(argc, argv, "nru")) != -1) { + switch (opt) { + case 'n': + compare = compare_numeric; + break; +#ifdef BB_FEATURE_SORT_REVERSE + case 'r': + reverse = TRUE; + break; +#endif +#ifdef BB_FEATURE_SORT_UNIQUE + case 'u': + unique = TRUE; + break; +#endif + default: + show_usage(); + } + } + + /* read the input */ + for (i = optind; i == optind || i < argc; i++) { + if (argv[i] == NULL) + fp = stdin; + else + fp = xfopen(argv[i], "r"); + + while ((line = get_line_from_file(fp)) != NULL) { + lines = xrealloc(lines, sizeof(char *) * (nlines + 1)); + chomp(line); + lines[nlines++] = line; + } + } + + /* sort it */ + qsort(lines, nlines, sizeof(char *), compare); + + /* print it */ +#ifdef BB_FEATURE_SORT_REVERSE + if (reverse) { + for (i = --nlines; 0 <= i; i--) +#ifdef BB_FEATURE_SORT_UNIQUE + if((!unique) || (i == nlines) || (strcmp(lines[i + 1], lines[i]))) +#endif + puts(lines[i]); + } else +#endif + for (i = 0; i < nlines; i++) +#ifdef BB_FEATURE_SORT_UNIQUE + if((!unique) || (!i) || (strcmp(lines[i - 1], lines[i]))) +#endif + puts(lines[i]); + return EXIT_SUCCESS; +} diff --git a/busybox/stty.c b/busybox/stty.c new file mode 100644 index 000000000..2e00a496d --- /dev/null +++ b/busybox/stty.c @@ -0,0 +1,1376 @@ +/* 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 + + Special for busybox ported by Vladimir Oleynik 2001 + + */ + +//#define TEST + +#include +#include +#include + +#include +#include + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#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 +}; + +/* What to output and how. */ +enum output_type { + changed, all, recoverable /* Default, -a, -g. */ +}; + +/* Which member(s) of `struct termios' a mode uses. */ +enum mode_type { + 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; /* Which structure element to change. */ + char flags; /* Setting and display options. */ + unsigned long bits; /* Bits to set for this mode. */ + unsigned long mask; /* Other bits to turn off for this mode. */ +}; + +static const struct mode_info mode_info[] = { + {"parenb", control, REV, PARENB, 0 }, + {"parodd", control, REV, PARODD, 0 }, + {"cs5", control, 0, CS5, CSIZE}, + {"cs6", control, 0, CS6, CSIZE}, + {"cs7", control, 0, CS7, CSIZE}, + {"cs8", control, 0, CS8, CSIZE}, + {"hupcl", control, REV, HUPCL, 0 }, + {"hup", control, REV | OMIT, HUPCL, 0 }, + {"cstopb", control, REV, CSTOPB, 0 }, + {"cread", control, SANE_SET | REV, CREAD, 0 }, + {"clocal", control, REV, CLOCAL, 0 }, +#ifdef CRTSCTS + {"crtscts", control, REV, CRTSCTS, 0 }, +#endif + {"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 }, + {"brkint", input, SANE_SET | REV, BRKINT, 0 }, + {"ignpar", input, REV, IGNPAR, 0 }, + {"parmrk", input, REV, PARMRK, 0 }, + {"inpck", input, REV, INPCK, 0 }, + {"istrip", input, REV, ISTRIP, 0 }, + {"inlcr", input, SANE_UNSET | REV, INLCR, 0 }, + {"igncr", input, SANE_UNSET | REV, IGNCR, 0 }, + {"icrnl", input, SANE_SET | REV, ICRNL, 0 }, + {"ixon", input, REV, IXON, 0 }, + {"ixoff", input, SANE_UNSET | REV, IXOFF, 0 }, + {"tandem", input, REV | OMIT, IXOFF, 0 }, +#ifdef IUCLC + {"iuclc", input, SANE_UNSET | REV, IUCLC, 0 }, +#endif +#ifdef IXANY + {"ixany", input, SANE_UNSET | REV, IXANY, 0 }, +#endif +#ifdef IMAXBEL + {"imaxbel", input, SANE_SET | REV, IMAXBEL, 0 }, +#endif + {"opost", output, SANE_SET | REV, OPOST, 0 }, +#ifdef OLCUC + {"olcuc", output, SANE_UNSET | REV, OLCUC, 0 }, +#endif +#ifdef OCRNL + {"ocrnl", output, SANE_UNSET | REV, OCRNL, 0 }, +#endif +#ifdef ONLCR + {"onlcr", output, SANE_SET | REV, ONLCR, 0 }, +#endif +#ifdef ONOCR + {"onocr", output, SANE_UNSET | REV, ONOCR, 0 }, +#endif +#ifdef ONLRET + {"onlret", output, SANE_UNSET | REV, ONLRET, 0 }, +#endif +#ifdef OFILL + {"ofill", output, SANE_UNSET | REV, OFILL, 0 }, +#endif +#ifdef OFDEL + {"ofdel", output, SANE_UNSET | REV, OFDEL, 0 }, +#endif +#ifdef NLDLY + {"nl1", output, SANE_UNSET, NL1, NLDLY}, + {"nl0", output, SANE_SET, NL0, NLDLY}, +#endif +#ifdef CRDLY + {"cr3", output, SANE_UNSET, CR3, CRDLY}, + {"cr2", output, SANE_UNSET, CR2, CRDLY}, + {"cr1", output, SANE_UNSET, CR1, CRDLY}, + {"cr0", output, SANE_SET, CR0, CRDLY}, +#endif + +#ifdef TABDLY + {"tab3", output, SANE_UNSET, TAB3, TABDLY}, + {"tab2", output, SANE_UNSET, TAB2, TABDLY}, + {"tab1", output, SANE_UNSET, TAB1, TABDLY}, + {"tab0", output, SANE_SET, TAB0, TABDLY}, +#else +# ifdef OXTABS + {"tab3", output, SANE_UNSET, OXTABS, 0 }, +# endif +#endif + +#ifdef BSDLY + {"bs1", output, SANE_UNSET, BS1, BSDLY}, + {"bs0", output, SANE_SET, BS0, BSDLY}, +#endif +#ifdef VTDLY + {"vt1", output, SANE_UNSET, VT1, VTDLY}, + {"vt0", output, SANE_SET, VT0, VTDLY}, +#endif +#ifdef FFDLY + {"ff1", output, SANE_UNSET, FF1, FFDLY}, + {"ff0", output, SANE_SET, FF0, FFDLY}, +#endif + {"isig", local, SANE_SET | REV, ISIG, 0 }, + {"icanon", local, SANE_SET | REV, ICANON, 0 }, +#ifdef IEXTEN + {"iexten", local, SANE_SET | REV, IEXTEN, 0 }, +#endif + {"echo", local, SANE_SET | REV, ECHO, 0 }, + {"echoe", local, SANE_SET | REV, ECHOE, 0 }, + {"crterase", local, REV | OMIT, ECHOE, 0 }, + {"echok", local, SANE_SET | REV, ECHOK, 0 }, + {"echonl", local, SANE_UNSET | REV, ECHONL, 0 }, + {"noflsh", local, SANE_UNSET | REV, NOFLSH, 0 }, +#ifdef XCASE + {"xcase", local, SANE_UNSET | REV, XCASE, 0 }, +#endif +#ifdef TOSTOP + {"tostop", local, SANE_UNSET | REV, TOSTOP, 0 }, +#endif +#ifdef ECHOPRT + {"echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 }, + {"prterase", local, REV | OMIT, ECHOPRT, 0 }, +#endif +#ifdef ECHOCTL + {"echoctl", local, SANE_SET | REV, ECHOCTL, 0 }, + {"ctlecho", local, REV | OMIT, ECHOCTL, 0 }, +#endif +#ifdef ECHOKE + {"echoke", local, SANE_SET | REV, ECHOKE, 0 }, + {"crtkill", local, REV | OMIT, ECHOKE, 0 }, +#endif + {evenp, combination, REV | OMIT, 0, 0 }, + {parity, combination, REV | OMIT, 0, 0 }, + {stty_oddp, combination, REV | OMIT, 0, 0 }, + {stty_nl, combination, REV | OMIT, 0, 0 }, + {stty_ek, combination, OMIT, 0, 0 }, + {stty_sane, combination, OMIT, 0, 0 }, + {cooked, combination, REV | OMIT, 0, 0 }, + {raw, combination, REV | OMIT, 0, 0 }, + {stty_pass8, combination, REV | OMIT, 0, 0 }, + {litout, combination, REV | OMIT, 0, 0 }, + {cbreak, combination, REV | OMIT, 0, 0 }, +#ifdef IXANY + {decctlq, combination, REV | OMIT, 0, 0 }, +#endif +#if defined (TABDLY) || defined (OXTABS) + {stty_tabs, combination, REV | OMIT, 0, 0 }, +#endif +#if defined(XCASE) && defined(IUCLC) && defined(OLCUC) + {stty_lcase, combination, REV | OMIT, 0, 0 }, + {stty_LCASE, combination, REV | OMIT, 0, 0 }, +#endif + {stty_crt, combination, OMIT, 0, 0 }, + {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'. */ + int 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 unsigned long baud_to_value(speed_t speed); +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, + const char *device_name); +static void display_changed(struct termios *mode); +static void display_recoverable(struct termios *mode); +static void display_settings(enum output_type output_type, + struct termios *mode, int fd, + const char *device_name); +static void display_speed(struct termios *mode, int fancy); +static void display_window_size(int fancy, int fd, + const char *device_name); +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, + const char *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; + enum output_type output_type; + 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; + const char *device_name; + + output_type = 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_type = all; + break; + + case 'g': + recoverable_output = 1; + output_type = recoverable; + break; + + case 'F': + if (file_name) + 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) + 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)) + 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 = open(device_name, O_RDONLY | O_NONBLOCK); + if (fd < 0) + perror_msg_and_die("%s", device_name); + if ((fdflags = fcntl(fd, F_GETFL)) == -1 + || fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) + perror_msg_and_die("%s: couldn't reset non-blocking mode", + device_name); + } else { + fd = 0; + device_name = "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_msg_and_die("%s", device_name); + + if (verbose_output || recoverable_output || noargs) { + max_col = screen_columns(); + current_col = 0; + display_settings(output_type, &mode, fd, device_name); + return EXIT_SUCCESS; + } + + speed_was_set = 0; + require_set_attr = 0; + k = optind; + while (k < argc) { + int match_found = 0; + int reversed = 0; + int i; + + if (argv[k][0] == '-') { + ++argv[k]; + 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) + 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) + 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) + 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) + 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) + error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + set_window_size((int) parse_number(argv[k], stty_suffixes), + -1, fd, device_name); + } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) { + if (k == argc - 1) + error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + set_window_size(-1, + (int) parse_number(argv[k], stty_suffixes), + fd, device_name); + } else if (STREQ(argv[k], "size")) { + max_col = screen_columns(); + current_col = 0; + display_window_size(0, fd, device_name); + } +#endif +#ifdef HAVE_C_LINE + else if (STREQ(argv[k], "line")) { + if (k == argc - 1) + error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + mode.c_line = parse_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 + error_msg_and_die("invalid argument `%s'", argv[k]); + } + k++; + } + + if (require_set_attr) { + struct termios new_mode; + + if (tcsetattr(fd, TCSADRAIN, &mode)) + perror_msg_and_die("%s", device_name); + + /* 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_msg_and_die("%s", device_name); + + /* 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 + error_msg_and_die ("%s: unable to perform all requested operations", + device_name); + } + } + + 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 & ~info->mask & ~info->bits; + else + *bitsp = (*bitsp & ~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 = parse_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 = parse_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 == input_speed || type == both_speeds) + cfsetispeed(mode, baud); + if (type == output_speed || type == both_speeds) + 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, const char *device_name) +{ + struct winsize win; + + if (get_win_size(fd, &win)) { + if (errno != EINVAL) + perror_msg_and_die("%s", device_name); + memset(&win, 0, sizeof(win)); + } + + if (rows >= 0) + win.ws_row = rows; + if (cols >= 0) + win.ws_col = cols; + +# ifdef TIOCSSIZE + /* Alexander Dupuy 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 = 1; + win.ws_col = 1; + + if (ioctl(fd, TIOCSWINSZ, (char *) &win)) + perror_msg_and_die("%s", device_name); + + if (ioctl(fd, TIOCSSIZE, (char *) &ttysz)) + perror_msg_and_die("%s", device_name); + return; + } +# endif + + if (ioctl(fd, TIOCSWINSZ, (char *) &win)) + perror_msg_and_die("%s", device_name); +} + +static void display_window_size(int fancy, int fd, const char *device_name) +{ + struct winsize win; + + if (get_win_size(fd, &win)) { + if (errno != EINVAL) + perror_msg_and_die("%s", device_name); + if (!fancy) + perror_msg_and_die("%s: no size information for this device", + device_name); + } 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) +{ +#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 + + if (getenv("COLUMNS")) + return atoi(getenv("COLUMNS")); + return 80; +} + +static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode) +{ + switch (type) { + case control: + return &mode->c_cflag; + + case input: + return &mode->c_iflag; + + case output: + return &mode->c_oflag; + + case local: + return &mode->c_lflag; + + default: /* combination: */ + return NULL; + } +} + +static void +display_settings(enum output_type output_type, struct termios *mode, + int fd, const char *device_name) +{ + switch (output_type) { + case changed: + display_changed(mode); + break; + + case all: + display_all(mode, fd, device_name); + break; + + case recoverable: + display_recoverable(mode); + break; + } +} + +static void display_changed(struct termios *mode) +{ + 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, const char *device_name) +{ + 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, device_name); +#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) +{ + if (cfgetispeed(mode) == 0 || cfgetispeed(mode) == cfgetospeed(mode)) + wrapf(fancy ? "speed %lu baud;" : "%lu\n", + baud_to_value(cfgetospeed(mode))); + else + wrapf(fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n", + baud_to_value(cfgetispeed(mode)), + baud_to_value(cfgetospeed(mode))); + if (!fancy) + current_col = 0; +} + +static void display_recoverable(struct termios *mode) +{ + 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; +} + +struct speed_map { + speed_t speed; /* Internal form. */ + unsigned long value; /* Numeric 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}, + {B19200, 19200}, + {B38400, 38400}, +#ifdef B57600 + {B57600, 57600}, +#endif +#ifdef B115200 + {B115200, 115200}, +#endif +#ifdef B230400 + {B230400, 230400}, +#endif +#ifdef B460800 + {B460800, 460800}, +#endif +}; + +static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map)); + +static speed_t string_to_baud(const char *arg) +{ + int i; + + for (i = 0; i < NUM_SPEEDS; ++i) + if (parse_number(arg, 0) == speeds[i].value) + return speeds[i].speed; + return (speed_t) - 1; +} + +static unsigned long baud_to_value(speed_t speed) +{ + int i; + + for (i = 0; i < NUM_SPEEDS; ++i) + if (speed == speeds[i].speed) + return speeds[i].value; + return 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 & ~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 & ~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 ""; + + if (ch >= 32) { + if (ch < 127) + *bpout++ = ch; + else if (ch == 127) { + *bpout++ = '^'; + *bpout++ = '?'; + } else { + *bpout++ = 'M', *bpout++ = '-'; + if (ch >= 128 + 32) { + if (ch < 128 + 127) + *bpout++ = ch - 128; + else { + *bpout++ = '^'; + *bpout++ = '?'; + } + } else { + *bpout++ = '^'; + *bpout++ = ch - 128 + 64; + } + } + } else { + *bpout++ = '^'; + *bpout++ = ch + 64; + } + *bpout = '\0'; + return (const char *) buf; +} + +#ifdef TEST + +const char *applet_name = "stty"; + +#endif + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/swaponoff.c b/busybox/swaponoff.c new file mode 100644 index 000000000..ce0e2c6cc --- /dev/null +++ b/busybox/swaponoff.c @@ -0,0 +1,115 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini swapon/swapoff implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +/* libc5 doesn't have sys/swap.h, define these here. */ +extern int swapon (__const char *__path, int __flags); +extern int swapoff (__const char *__path); +#else +#include +#endif + +#include "busybox.h" + +static int whichApp; + +static const int SWAPON_APP = 1; +static const int SWAPOFF_APP = 2; + + +static void swap_enable_disable(char *device) +{ + int status; + + if (whichApp == SWAPON_APP) + status = swapon(device, 0); + else + status = swapoff(device); + + if (status != 0) + perror_msg_and_die(applet_name); +} + +static void do_em_all() +{ + struct mntent *m; + FILE *f = setmntent("/etc/fstab", "r"); + + if (f == NULL) + perror_msg_and_die("/etc/fstab"); + while ((m = getmntent(f)) != NULL) { + if (strcmp(m->mnt_type, MNTTYPE_SWAP)==0) { + swap_enable_disable(m->mnt_fsname); + } + } + endmntent(f); + exit(EXIT_SUCCESS); +} + + +extern int swap_on_off_main(int argc, char **argv) +{ + if (strcmp(applet_name, "swapon") == 0) { + whichApp = SWAPON_APP; + } else { + 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) + error_msg_and_die("/etc/fstab file missing"); + } + do_em_all(); + break; + default: + goto usage_and_exit; + } + } + swap_enable_disable(*argv); + return EXIT_SUCCESS; + + usage_and_exit: + show_usage(); +} diff --git a/busybox/sync.c b/busybox/sync.c new file mode 100644 index 000000000..ee22ae109 --- /dev/null +++ b/busybox/sync.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sync implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int sync_main(int argc, char **argv) +{ + if (argc > 1 && **(argv + 1) == '-') + show_usage(); + sync(); + return(EXIT_SUCCESS); +} diff --git a/busybox/sysklogd/klogd.c b/busybox/sysklogd/klogd.c new file mode 100644 index 000000000..d7b54e9c8 --- /dev/null +++ b/busybox/sysklogd/klogd.c @@ -0,0 +1,153 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini klogd implementation for busybox + * + * Copyright (C) 2001 by Gennady Feldman . + * Changes: Made this a standalone busybox module which uses standalone + * syslog() client interface. + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * Copyright (C) 2000 by Karl M. Hegbloom + * + * "circular buffer" Copyright (C) 2000 by Gennady Feldman + * + * Maintainer: Gennady Feldman 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 +#include +#include /* for our signal() handlers */ +#include /* strncpy() */ +#include /* errno and friends */ +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +# ifdef __alpha__ +# define klogctl syslog +# endif +#else +# include +#endif + +#include "busybox.h" + +static void klogd_signal(int sig) +{ + klogctl(7, NULL, 0); + klogctl(0, 0, 0); + //logMessage(0, "Kernel log daemon exiting."); + syslog_msg(LOG_DAEMON, 0, "Kernel log daemon exiting."); + exit(TRUE); +} + +static void doKlogd (void) __attribute__ ((noreturn)); +static void doKlogd (void) +{ + int priority = LOG_INFO; + char log_buffer[4096]; + int i, n, lastc; + char *start; + + /* 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); + + syslog_msg(LOG_DAEMON, 0, "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) { + char message[80]; + + if (errno == EINTR) + continue; + snprintf(message, 79, "klogd: Error return from sys_sycall: %d - %s.\n", + errno, strerror(errno)); + syslog_msg(LOG_DAEMON, LOG_SYSLOG | LOG_ERR, message); + exit(1); + } + + /* klogctl buffer parsing modelled after code in dmesg.c */ + start=&log_buffer[0]; + lastc='\0'; + for (i=0; i') i++; + start = &log_buffer[i]; + } + if (log_buffer[i] == '\n') { + log_buffer[i] = '\0'; /* zero terminate this message */ + syslog_msg(LOG_DAEMON, LOG_KERN | priority, 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; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "n")) > 0) { + switch (opt) { + case 'n': + doFork = FALSE; + break; + default: + show_usage(); + } + } + + if (doFork == TRUE) { + if (daemon(0, 1) < 0) + perror_msg_and_die("daemon"); + } + doKlogd(); + + 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 index 000000000..9f730915f --- /dev/null +++ b/busybox/sysklogd/logger.c @@ -0,0 +1,200 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logger implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include + +#include "busybox.h" +#if !defined BB_SYSLOGD + +#define SYSLOG_NAMES +#include + +#else +#include +# 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) + error_msg_and_die("unknown facility name: %s", save); + *s++ = '.'; + } else { + s = save; + } + lev = decode(s, prioritynames); + if (lev < 0) + 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, len, opt; + char *message=NULL, buf[1024], name[128]; + + /* Fill out the name string early (may be overwritten later) */ + my_getpwuid(name, geteuid()); + + /* 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': + strncpy(name, optarg, sizeof(name)); + break; + default: + 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 { + len = 1; /* for the '\0' */ + message=xcalloc(1, 1); + for (i = optind; i < argc; i++) { + len += strlen(argv[i]); + len += 1; /* for the space between the args */ + message = xrealloc(message, len); + strcat(message, argv[i]); + strcat(message, " "); + } + message[strlen(message)-1] = '\0'; + 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. + * + * 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 index 000000000..d3349625c --- /dev/null +++ b/busybox/sysklogd/logread.c @@ -0,0 +1,144 @@ +/* vi: set sw=4 ts=4: */ +/* + * circular buffer syslog implementation for busybox + * + * Copyright (C) 2000 by Gennady Feldman + * + * Maintainer: Gennady Feldman 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#if __GNU_LIBRARY__ < 5 +#error Sorry. Looks like you are using libc5. +#error libc5 shm support isnt good enough. +#error Please disable BB_FEATURE_IPC_SYSLOG +#endif + + +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; + + /* no options, no getopt */ + if (argc > 1) + 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"); + + sem_down(log_semid); + // Read Memory + i=buf->head; + + //printf("head: %i tail: %i size: %i\n",buf->head,buf->tail,buf->size); + if (buf->head == buf->tail) { + printf("\n"); + } + + while ( i != buf->tail) { + printf("%s", buf->data+i); + i+= strlen(buf->data+i) + 1; + if (i >= buf->size ) + i=0; + } + sem_up(log_semid); + +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 index 000000000..14219eb54 --- /dev/null +++ b/busybox/sysklogd/syslogd.c @@ -0,0 +1,639 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini syslogd implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * Copyright (C) 2000 by Karl M. Hegbloom + * + * "circular buffer" Copyright (C) 2001 by Gennady Feldman + * + * Maintainer: Gennady Feldman 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +/* SYSLOG_NAMES defined to pull some extra junk from syslog.h */ +#define SYSLOG_NAMES +#include +#include + +/* Path for the file where all log messages are written */ +#define __LOG_FILE "/var/log/messages" + +/* Path to the unix socket */ +static char lfile[BUFSIZ]; + +static char *logFilePath = __LOG_FILE; + +/* interval between marks in seconds */ +static int MarkInterval = 20 * 60; + +/* localhost's name */ +static char LocalHostName[32]; + +#ifdef BB_FEATURE_REMOTE_LOG +#include +/* udp socket for logging to remote host */ +static int remotefd = -1; +/* 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 + +/* circular buffer variables/structures */ +#ifdef BB_FEATURE_IPC_SYSLOG + +#include +#include +#include + +/* 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 +int data_size = 16000; // data size +int shm_size = 16000 + sizeof(*buf); // our buffer 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 ) + 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 ) + 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) + perror_msg_and_die("shmget"); + + + if ((buf = shmat(shmid, NULL, 0)) == NULL) + perror_msg_and_die("shmat"); + + + buf->size=data_size; + 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) + perror_msg_and_die("semget"); + }else + 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 +/* 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 BB_FEATURE_IPC_SYSLOG + if ((circular_logging == TRUE) && (buf != NULL)){ + char b[1024]; + va_start (arguments, fmt); + vsprintf (b, 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); + 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); + } + } +} + +static void logMessage (int pri, char *msg) +{ + time_t now; + char *timestamp; + static char res[20] = ""; + 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 BB_FEATURE_REMOTE_LOG + /* send message to remote logger */ + if ( -1 != remotefd){ +static const int IOV_COUNT = 2; + struct iovec iov[IOV_COUNT]; + struct iovec *v = iov; + + memset(&res, 0, sizeof(res)); + snprintf(res, sizeof(res), "<%d>", pri); + v->iov_base = res ; + v->iov_len = strlen(res); + v++; + + v->iov_base = msg; + v->iov_len = strlen(msg); + + if ( -1 == writev(remotefd,iov, IOV_COUNT)){ + error_msg_and_die("syslogd: cannot write to remote file handle on" + "%s:%d",RemoteHost,RemotePort); + } + } + if (local_logging == TRUE) +#endif + /* now spew out the message to wherever it is supposed to go */ + 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 BB_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 DODEBUG and BUFFERS_GO_IN_BSS are + * enabled, we otherwise get a "storage size isn't constant error. */ +#define BUFSIZE 1023 +static int serveConnection (int conn) +{ + RESERVE_BB_BUFFER(tmpbuf, BUFSIZE + 1); + int n_read; + + n_read = read (conn, tmpbuf, BUFSIZE ); + + if (n_read > 0) { + + int pri = (LOG_USER | LOG_NOTICE); + char line[ BUFSIZE + 1 ]; + unsigned char c; + + char *p = tmpbuf, *q = line; + + tmpbuf[ n_read - 1 ] = '\0'; + + while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) { + if (c == '<') { + /* Parse the magic priority number. */ + 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'; + /* Now log it */ + logMessage (pri, line); + } + RELEASE_BB_BUFFER (tmpbuf); + return n_read; +} + + +#ifdef BB_FEATURE_REMOTE_LOG +static void init_RemoteLog (void){ + + struct sockaddr_in remoteaddr; + struct hostent *hostinfo; + int len = sizeof(remoteaddr); + + memset(&remoteaddr, 0, len); + + remotefd = socket(AF_INET, SOCK_DGRAM, 0); + + if (remotefd < 0) { + error_msg_and_die("syslogd: cannot create socket"); + } + + hostinfo = xgethostbyname(RemoteHost); + + remoteaddr.sin_family = AF_INET; + remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; + remoteaddr.sin_port = htons(RemotePort); + + /* + Since we are using UDP sockets, connect just sets the default host and port + for future operations + */ + if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){ + error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort); + } + +} +#endif + +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_STREAM, 0)) < 0) + 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)) || (listen (sock_fd, 5))) + perror_msg_and_die ("Could not connect to socket " _PATH_LOG); + + if (chmod (lfile, 0666) < 0) + perror_msg_and_die ("Could not set permission on " _PATH_LOG); + + FD_ZERO (&fds); + FD_SET (sock_fd, &fds); + +#ifdef BB_FEATURE_IPC_SYSLOG + if (circular_logging == TRUE ){ + ipcsyslog_init(); + } +#endif + + #ifdef BB_FEATURE_REMOTE_LOG + if (doRemoteLog == TRUE){ + init_RemoteLog(); + } + #endif + + logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER); + + for (;;) { + + fd_set readfds; + int n_ready; + int fd; + + memcpy (&readfds, &fds, sizeof (fds)); + + if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) { + if (errno == EINTR) continue; /* alarm may have happened. */ + perror_msg_and_die ("select error"); + } + + for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) { + if (FD_ISSET (fd, &readfds)) { + + --n_ready; + + if (fd == sock_fd) { + int conn; + + //printf("New Connection request.\n"); + if ((conn = accept (sock_fd, (struct sockaddr *) &sunx, &addrLength)) < 0) { + perror_msg_and_die ("accept error"); + } + + FD_SET(conn, &fds); + //printf("conn: %i, set_size: %i\n",conn,FD_SETSIZE); + } else { + //printf("Serving connection: %i\n",fd); + if ( serveConnection(fd) <= 0 ) { + close (fd); + FD_CLR(fd, &fds); + } + } /* fd == sock_fd */ + }/* FD_ISSET() */ + }/* for */ + } /* 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:R:LC")) > 0) { + switch (opt) { + case 'm': + MarkInterval = atoi(optarg) * 60; + break; + case 'n': + doFork = FALSE; + break; + case 'O': + logFilePath = strdup(optarg); + break; +#ifdef BB_FEATURE_REMOTE_LOG + case 'R': + RemoteHost = strdup(optarg); + if ( (p = strchr(RemoteHost, ':'))){ + RemotePort = atoi(p+1); + *p = '\0'; + } + doRemoteLog = TRUE; + break; + case 'L': + local_logging = TRUE; + break; +#endif +#ifdef BB_FEATURE_IPC_SYSLOG + case 'C': + circular_logging = TRUE; + break; +#endif + default: + show_usage(); + } + } + +#ifdef BB_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 (daemon(0, 1) < 0) + perror_msg_and_die("daemon"); + } + doSyslogd(); + + return EXIT_SUCCESS; +} + +/* +Local Variables +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/syslogd.c b/busybox/syslogd.c new file mode 100644 index 000000000..14219eb54 --- /dev/null +++ b/busybox/syslogd.c @@ -0,0 +1,639 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini syslogd implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * Copyright (C) 2000 by Karl M. Hegbloom + * + * "circular buffer" Copyright (C) 2001 by Gennady Feldman + * + * Maintainer: Gennady Feldman 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +/* SYSLOG_NAMES defined to pull some extra junk from syslog.h */ +#define SYSLOG_NAMES +#include +#include + +/* Path for the file where all log messages are written */ +#define __LOG_FILE "/var/log/messages" + +/* Path to the unix socket */ +static char lfile[BUFSIZ]; + +static char *logFilePath = __LOG_FILE; + +/* interval between marks in seconds */ +static int MarkInterval = 20 * 60; + +/* localhost's name */ +static char LocalHostName[32]; + +#ifdef BB_FEATURE_REMOTE_LOG +#include +/* udp socket for logging to remote host */ +static int remotefd = -1; +/* 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 + +/* circular buffer variables/structures */ +#ifdef BB_FEATURE_IPC_SYSLOG + +#include +#include +#include + +/* 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 +int data_size = 16000; // data size +int shm_size = 16000 + sizeof(*buf); // our buffer 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 ) + 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 ) + 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) + perror_msg_and_die("shmget"); + + + if ((buf = shmat(shmid, NULL, 0)) == NULL) + perror_msg_and_die("shmat"); + + + buf->size=data_size; + 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) + perror_msg_and_die("semget"); + }else + 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 +/* 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 BB_FEATURE_IPC_SYSLOG + if ((circular_logging == TRUE) && (buf != NULL)){ + char b[1024]; + va_start (arguments, fmt); + vsprintf (b, 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); + 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); + } + } +} + +static void logMessage (int pri, char *msg) +{ + time_t now; + char *timestamp; + static char res[20] = ""; + 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 BB_FEATURE_REMOTE_LOG + /* send message to remote logger */ + if ( -1 != remotefd){ +static const int IOV_COUNT = 2; + struct iovec iov[IOV_COUNT]; + struct iovec *v = iov; + + memset(&res, 0, sizeof(res)); + snprintf(res, sizeof(res), "<%d>", pri); + v->iov_base = res ; + v->iov_len = strlen(res); + v++; + + v->iov_base = msg; + v->iov_len = strlen(msg); + + if ( -1 == writev(remotefd,iov, IOV_COUNT)){ + error_msg_and_die("syslogd: cannot write to remote file handle on" + "%s:%d",RemoteHost,RemotePort); + } + } + if (local_logging == TRUE) +#endif + /* now spew out the message to wherever it is supposed to go */ + 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 BB_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 DODEBUG and BUFFERS_GO_IN_BSS are + * enabled, we otherwise get a "storage size isn't constant error. */ +#define BUFSIZE 1023 +static int serveConnection (int conn) +{ + RESERVE_BB_BUFFER(tmpbuf, BUFSIZE + 1); + int n_read; + + n_read = read (conn, tmpbuf, BUFSIZE ); + + if (n_read > 0) { + + int pri = (LOG_USER | LOG_NOTICE); + char line[ BUFSIZE + 1 ]; + unsigned char c; + + char *p = tmpbuf, *q = line; + + tmpbuf[ n_read - 1 ] = '\0'; + + while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) { + if (c == '<') { + /* Parse the magic priority number. */ + 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'; + /* Now log it */ + logMessage (pri, line); + } + RELEASE_BB_BUFFER (tmpbuf); + return n_read; +} + + +#ifdef BB_FEATURE_REMOTE_LOG +static void init_RemoteLog (void){ + + struct sockaddr_in remoteaddr; + struct hostent *hostinfo; + int len = sizeof(remoteaddr); + + memset(&remoteaddr, 0, len); + + remotefd = socket(AF_INET, SOCK_DGRAM, 0); + + if (remotefd < 0) { + error_msg_and_die("syslogd: cannot create socket"); + } + + hostinfo = xgethostbyname(RemoteHost); + + remoteaddr.sin_family = AF_INET; + remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; + remoteaddr.sin_port = htons(RemotePort); + + /* + Since we are using UDP sockets, connect just sets the default host and port + for future operations + */ + if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){ + error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort); + } + +} +#endif + +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_STREAM, 0)) < 0) + 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)) || (listen (sock_fd, 5))) + perror_msg_and_die ("Could not connect to socket " _PATH_LOG); + + if (chmod (lfile, 0666) < 0) + perror_msg_and_die ("Could not set permission on " _PATH_LOG); + + FD_ZERO (&fds); + FD_SET (sock_fd, &fds); + +#ifdef BB_FEATURE_IPC_SYSLOG + if (circular_logging == TRUE ){ + ipcsyslog_init(); + } +#endif + + #ifdef BB_FEATURE_REMOTE_LOG + if (doRemoteLog == TRUE){ + init_RemoteLog(); + } + #endif + + logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER); + + for (;;) { + + fd_set readfds; + int n_ready; + int fd; + + memcpy (&readfds, &fds, sizeof (fds)); + + if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) { + if (errno == EINTR) continue; /* alarm may have happened. */ + perror_msg_and_die ("select error"); + } + + for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) { + if (FD_ISSET (fd, &readfds)) { + + --n_ready; + + if (fd == sock_fd) { + int conn; + + //printf("New Connection request.\n"); + if ((conn = accept (sock_fd, (struct sockaddr *) &sunx, &addrLength)) < 0) { + perror_msg_and_die ("accept error"); + } + + FD_SET(conn, &fds); + //printf("conn: %i, set_size: %i\n",conn,FD_SETSIZE); + } else { + //printf("Serving connection: %i\n",fd); + if ( serveConnection(fd) <= 0 ) { + close (fd); + FD_CLR(fd, &fds); + } + } /* fd == sock_fd */ + }/* FD_ISSET() */ + }/* for */ + } /* 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:R:LC")) > 0) { + switch (opt) { + case 'm': + MarkInterval = atoi(optarg) * 60; + break; + case 'n': + doFork = FALSE; + break; + case 'O': + logFilePath = strdup(optarg); + break; +#ifdef BB_FEATURE_REMOTE_LOG + case 'R': + RemoteHost = strdup(optarg); + if ( (p = strchr(RemoteHost, ':'))){ + RemotePort = atoi(p+1); + *p = '\0'; + } + doRemoteLog = TRUE; + break; + case 'L': + local_logging = TRUE; + break; +#endif +#ifdef BB_FEATURE_IPC_SYSLOG + case 'C': + circular_logging = TRUE; + break; +#endif + default: + show_usage(); + } + } + +#ifdef BB_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 (daemon(0, 1) < 0) + perror_msg_and_die("daemon"); + } + doSyslogd(); + + return EXIT_SUCCESS; +} + +/* +Local Variables +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/tail.c b/busybox/tail.c new file mode 100644 index 000000000..90cc2a6ef --- /dev/null +++ b/busybox/tail.c @@ -0,0 +1,251 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tail implementation for busybox + * + * + * Copyright (C) 2001 by 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 +#include +#include +#include +#include +#include +#include "busybox.h" + +static const struct suffix_mult tail_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1048576 }, + { NULL, 0 } +}; + +static const int BYTES = 0; +static const int LINES = 1; + +static char *tailbuf; +static int taillen; +static int newline; + +static void tailbuf_append(char *buf, int len) +{ + tailbuf = xrealloc(tailbuf, taillen + len); + memcpy(tailbuf + taillen, buf, len); + taillen += len; +} + +static void tailbuf_trunc() +{ + char *s; + s = memchr(tailbuf, '\n', taillen); + memmove(tailbuf, s + 1, taillen - ((s + 1) - tailbuf)); + taillen -= (s + 1) - tailbuf; + newline = 0; +} + +int tail_main(int argc, char **argv) +{ + int from_top = 0, units = LINES, count = 10, sleep_period = 1; + int show_headers = 0, hide_headers = 0, follow = 0; + int *fds, nfiles = 0, status = EXIT_SUCCESS, nread, nwrite, seen = 0; + char *s, *start, *end, buf[BUFSIZ]; + int i, opt; + + while ((opt = getopt(argc, argv, "c:fhn:q:s:v")) > 0) { + switch (opt) { + case 'f': + follow = 1; + break; +#ifdef BB_FEATURE_FANCY_TAIL + case 'c': + units = BYTES; + /* FALLS THROUGH */ +#endif + case 'n': + count = parse_number(optarg, tail_suffixes); + if (count < 0) + count = -count; + if (optarg[0] == '+') + from_top = 1; + break; +#ifdef BB_FEATURE_FANCY_TAIL + case 'q': + hide_headers = 1; + break; + case 's': + sleep_period = parse_number(optarg, 0); + break; + case 'v': + show_headers = 1; + break; +#endif + default: + show_usage(); + } + } + + /* open all the files */ + fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1)); + if (argc == optind) { + fds[nfiles++] = STDIN_FILENO; + argv[optind] = "standard input"; + } else { + for (i = optind; i < argc; i++) { + if (strcmp(argv[i], "-") == 0) { + fds[nfiles++] = STDIN_FILENO; + argv[i] = "standard input"; + } else if ((fds[nfiles++] = open(argv[i], O_RDONLY)) < 0) { + perror_msg("%s", argv[i]); + status = EXIT_FAILURE; + } + } + } + +#ifdef BB_FEATURE_FANCY_TAIL + /* tail the files */ + if (!from_top && units == BYTES) + tailbuf = xmalloc(count); +#endif + + for (i = 0; i < nfiles; i++) { + if (fds[i] == -1) + continue; + if (!count) { + lseek(fds[i], 0, SEEK_END); + continue; + } + seen = 0; + if (show_headers || (!hide_headers && nfiles > 1)) + printf("%s==> %s <==\n", i == 0 ? "" : "\n", argv[optind + i]); + while ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0) { + if (from_top) { +#ifdef BB_FEATURE_FANCY_TAIL + if (units == BYTES) { + if (count - 1 <= seen) + nwrite = nread; + else if (count - 1 <= seen + nread) + nwrite = nread + seen - (count - 1); + else + nwrite = 0; + seen += nread; + } else { +#else + { +#endif + if (count - 1 <= seen) + nwrite = nread; + else { + nwrite = 0; + for (s = memchr(buf, '\n', nread); s != NULL; + s = memchr(s+1, '\n', nread - (s + 1 - buf))) { + if (count - 1 <= ++seen) { + nwrite = nread - (s + 1 - buf); + break; + } + } + } + } + if (full_write(STDOUT_FILENO, buf + nread - nwrite, + nwrite) < 0) { + perror_msg("write"); + status = EXIT_FAILURE; + break; + } + } else { +#ifdef BB_FEATURE_FANCY_TAIL + if (units == BYTES) { + if (nread < count) { + memmove(tailbuf, tailbuf + nread, count - nread); + memcpy(tailbuf + count - nread, buf, nread); + } else { + memcpy(tailbuf, buf + nread - count, count); + } + seen += nread; + } else { +#else + { +#endif + for (start = buf, end = memchr(buf, '\n', nread); + end != NULL; start = end+1, + end = memchr(start, '\n', nread - (start - buf))) { + if (newline && count <= seen) + tailbuf_trunc(); + tailbuf_append(start, end - start + 1); + seen++; + newline = 1; + } + if (newline && count <= seen && nread - (start - buf) > 0) + tailbuf_trunc(); + tailbuf_append(start, nread - (start - buf)); + } + } + } + + if (nread < 0) { + perror_msg("read"); + status = EXIT_FAILURE; + } + +#ifdef BB_FEATURE_FANCY_TAIL + if (!from_top && units == BYTES) { + if (count < seen) + seen = count; + if (full_write(STDOUT_FILENO, tailbuf + count - seen, seen) < 0) { + perror_msg("write"); + status = EXIT_FAILURE; + } + } +#endif + + if (!from_top && units == LINES) { + if (full_write(STDOUT_FILENO, tailbuf, taillen) < 0) { + perror_msg("write"); + status = EXIT_FAILURE; + } + } + + taillen = 0; + } + + while (follow) { + sleep(sleep_period); + + for (i = 0; i < nfiles; i++) { + if (fds[i] == -1) + continue; + + if ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0) { + if (show_headers || (!hide_headers && nfiles > 1)) + printf("\n==> %s <==\n", argv[optind + i]); + + do { + full_write(STDOUT_FILENO, buf, nread); + } while ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0); + } + + if (nread < 0) { + perror_msg("read"); + status = EXIT_FAILURE; + } + } + } + + return status; +} diff --git a/busybox/tar.c b/busybox/tar.c new file mode 100644 index 000000000..cf65798ff --- /dev/null +++ b/busybox/tar.c @@ -0,0 +1,1148 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tar implementation for busybox + * + * Note, that as of BusyBox-0.43, tar has been completely rewritten from the + * ground up. It still has remnents of the old code lying about, but it is + * very different now (i.e., cleaner, less global variables, etc.) + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* Tar file constants */ +#ifndef MAJOR +#define MAJOR(dev) (((dev)>>8)&0xff) +#define MINOR(dev) ((dev)&0xff) +#endif + +enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */ + +/* POSIX tar Header Block, from POSIX 1003.1-1990 */ +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; + + +/* A few useful constants */ +#define TAR_MAGIC "ustar" /* ustar and a null */ +#define TAR_VERSION " " /* Be compatable with GNU tar format */ +static const int TAR_MAGIC_LEN = 6; +static const int TAR_VERSION_LEN = 2; +static const int TAR_BLOCK_SIZE = 512; + +/* 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; + +/* This struct ignores magic, non-numeric user name, + * non-numeric group name, and the checksum, since + * these are all ignored by BusyBox tar. */ +struct TarInfo +{ + int tarFd; /* An open file descriptor for reading from the tarball */ + char * name; /* File name */ + mode_t mode; /* Unix mode, including device bits. */ + uid_t uid; /* Numeric UID */ + gid_t gid; /* Numeric GID */ + size_t size; /* Size of file */ + time_t mtime; /* Last-modified time */ + enum TarFileType type; /* Regular, directory, link, etc. */ + char * linkname; /* Name for symbolic and hard links */ + long devmajor; /* Major number for special device */ + long devminor; /* Minor number for special device */ +}; +typedef struct TarInfo TarInfo; + +/* Local procedures to restore files from a tar file. */ +static int readTarFile(int tarFd, int extractFlag, int listFlag, + int tostdoutFlag, int verboseFlag, char** extractList, + char** excludeList); + +#ifdef BB_FEATURE_TAR_CREATE +/* Local procedures to save files into a tar file. */ +static int writeTarFile(const char* tarName, int verboseFlag, char **argv, + char** excludeList); +#endif + +#if defined BB_FEATURE_TAR_EXCLUDE +static struct option longopts[] = { + { "exclude", 1, NULL, 'e' }, + { NULL, 0, NULL, 0 } +}; +#endif + +extern int tar_main(int argc, char **argv) +{ + char** excludeList=NULL; + char** extractList=NULL; + const char *tarName="-"; + const char *cwd=NULL; +#if defined BB_FEATURE_TAR_EXCLUDE + int excludeListSize=0; + FILE *fileList; + char file[256]; +#endif +#if defined BB_FEATURE_TAR_GZIP + FILE *comp_file = NULL; + int unzipFlag = FALSE; +#endif + int listFlag = FALSE; + int extractFlag = FALSE; + int createFlag = FALSE; + int verboseFlag = FALSE; + int tostdoutFlag = FALSE; + int status = FALSE; + int opt; + pid_t pid; + + if (argc <= 1) + show_usage(); + + if (argv[1][0] != '-') { + char *tmp = xmalloc(strlen(argv[1]) + 2); + tmp[0] = '-'; + strcpy(tmp + 1, argv[1]); + argv[1] = tmp; + } + + while ( +#ifndef BB_FEATURE_TAR_EXCLUDE + (opt = getopt(argc, argv, "cxtzvOf:pC:")) +#else + (opt = getopt_long(argc, argv, "cxtzvOf:X:pC:", longopts, NULL)) +#endif + > 0) { + switch (opt) { + case 'c': + if (extractFlag == TRUE || listFlag == TRUE) + goto flagError; + createFlag = TRUE; + break; + case 'x': + if (listFlag == TRUE || createFlag == TRUE) + goto flagError; + extractFlag = TRUE; + break; + case 't': + if (extractFlag == TRUE || createFlag == TRUE) + goto flagError; + listFlag = TRUE; + break; +#ifdef BB_FEATURE_TAR_GZIP + case 'z': + unzipFlag = TRUE; + break; +#endif + case 'v': + verboseFlag = TRUE; + break; + case 'O': + tostdoutFlag = TRUE; + break; + case 'f': + if (*tarName != '-') + error_msg_and_die( "Only one 'f' option allowed"); + tarName = optarg; + break; +#if defined BB_FEATURE_TAR_EXCLUDE + case 'e': + excludeList=xrealloc( excludeList, + sizeof(char *) * (excludeListSize+2)); + excludeList[excludeListSize] = optarg; + /* Tack a NULL onto the end of the list */ + excludeList[++excludeListSize] = NULL; + case 'X': + fileList = xfopen(optarg, "r"); + while (fgets(file, sizeof(file), fileList) != NULL) { + excludeList = xrealloc(excludeList, + sizeof(char *) * (excludeListSize+2)); + chomp(file); + excludeList[excludeListSize] = xstrdup(file); + /* Tack a NULL onto the end of the list */ + excludeList[++excludeListSize] = NULL; + } + fclose(fileList); + break; +#endif + case 'p': + break; + case 'C': + cwd = xgetcwd((char *)cwd); + if (chdir(optarg)) { + printf("cd: %s: %s\n", optarg, strerror(errno)); + return EXIT_FAILURE; + } + break; + default: + show_usage(); + } + } + + /* + * Do the correct type of action supplying the rest of the + * command line arguments as the list of files to process. + */ + if (createFlag == TRUE) { +#ifndef BB_FEATURE_TAR_CREATE + error_msg_and_die( "This version of tar was not compiled with tar creation support."); +#else +#ifdef BB_FEATURE_TAR_GZIP + if (unzipFlag==TRUE) + error_msg_and_die("Creation of compressed not internally support by tar, pipe to busybox gunzip"); +#endif + status = writeTarFile(tarName, verboseFlag, argv + optind, excludeList); +#endif + } + if (listFlag == TRUE || extractFlag == TRUE) { + int tarFd; + if (argv[optind]) + extractList = argv + optind; + /* Open the tar file for reading. */ + if (!strcmp(tarName, "-")) + tarFd = fileno(stdin); + else + tarFd = open(tarName, O_RDONLY); + if (tarFd < 0) + perror_msg_and_die("Error opening '%s'", tarName); + +#ifdef BB_FEATURE_TAR_GZIP + /* unzip tarFd in a seperate process */ + if (unzipFlag == TRUE) { + comp_file = fdopen(tarFd, "r"); + + /* set the buffer size */ + setvbuf(comp_file, NULL, _IOFBF, 0x8000); + + if ((tarFd = fileno(gz_open(comp_file, &pid))) == EXIT_FAILURE) { + error_msg_and_die("Couldnt unzip file"); + } + } +#endif + status = readTarFile(tarFd, extractFlag, listFlag, tostdoutFlag, + verboseFlag, extractList, excludeList); + close(tarFd); +#ifdef BB_FEATURE_TAR_GZIP + if (unzipFlag == TRUE) { + gz_close(pid); + fclose(comp_file); + } +#endif + } + + if (cwd) + chdir(cwd); + if (status == TRUE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + + flagError: + error_msg_and_die( "Exactly one of 'c', 'x' or 't' must be specified"); +} + +static void +fixUpPermissions(TarInfo *header) +{ + struct utimbuf t; + /* Now set permissions etc. for the new file */ + chown(header->name, header->uid, header->gid); + chmod(header->name, header->mode); + /* Reset the time */ + t.actime = time(0); + t.modtime = header->mtime; + utime(header->name, &t); +} + +static int +tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + size_t writeSize; + size_t readSize; + size_t actualWriteSz; + char buffer[20 * TAR_BLOCK_SIZE]; + size_t size = header->size; + int outFd=fileno(stdout); + + /* Open the file to be written, if a file is supposed to be written */ + if (extractFlag==TRUE && tostdoutFlag==FALSE) { + /* Create the path to the file, just in case it isn't there... + * This should not screw up path permissions or anything. */ + char *dir = dirname (header->name); + make_directory (dir, -1, FILEUTILS_RECUR); + free (dir); + if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, + header->mode & ~S_IFMT)) < 0) { + error_msg(io_error, header->name, strerror(errno)); + return( FALSE); + } + } + + /* Write out the file, if we are supposed to be doing that */ + while ( size > 0 ) { + actualWriteSz=0; + if ( size > sizeof(buffer) ) + writeSize = readSize = sizeof(buffer); + else { + int mod = size % TAR_BLOCK_SIZE; + if ( mod != 0 ) + readSize = size + (TAR_BLOCK_SIZE - mod); + else + readSize = size; + writeSize = size; + } + if ( (readSize = full_read(header->tarFd, buffer, readSize)) <= 0 ) { + /* Tarball seems to have a problem */ + error_msg("Unexpected EOF in archive"); + return( FALSE); + } + if ( readSize < writeSize ) + writeSize = readSize; + + /* Write out the file, if we are supposed to be doing that */ + if (extractFlag==TRUE) { + + if ((actualWriteSz=full_write(outFd, buffer, writeSize)) != writeSize ) { + /* Output file seems to have a problem */ + error_msg(io_error, header->name, strerror(errno)); + return( FALSE); + } + } else { + actualWriteSz=writeSize; + } + + size -= actualWriteSz; + } + + /* Now we are done writing the file out, so try + * and fix up the permissions and whatnot */ + if (extractFlag==TRUE && tostdoutFlag==FALSE) { + close(outFd); + fixUpPermissions(header); + } + return( TRUE); +} + +static int +tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + if (extractFlag==FALSE || tostdoutFlag==TRUE) + return( TRUE); + + if (make_directory(header->name, header->mode, FILEUTILS_RECUR) < 0) + return( FALSE); + + fixUpPermissions(header); + return( TRUE); +} + +static int +tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + if (extractFlag==FALSE || tostdoutFlag==TRUE) + return( TRUE); + + if (link(header->linkname, header->name) < 0) { + perror_msg("%s: Cannot create hard link to '%s'", header->name, + header->linkname); + return( FALSE); + } + + /* Now set permissions etc. for the new directory */ + fixUpPermissions(header); + return( TRUE); +} + +static int +tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + if (extractFlag==FALSE || tostdoutFlag==TRUE) + return( TRUE); + +#ifdef S_ISLNK + if (symlink(header->linkname, header->name) < 0) { + perror_msg("%s: Cannot create symlink to '%s'", header->name, + header->linkname); + return( FALSE); + } + /* Try to change ownership of the symlink. + * If libs doesn't support that, don't bother. + * Changing the pointed-to-file is the Wrong Thing(tm). + */ +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) + lchown(header->name, header->uid, header->gid); +#endif + + /* Do not change permissions or date on symlink, + * since it changes the pointed to file instead. duh. */ +#else + error_msg("%s: Cannot create symlink to '%s': %s", + header->name, header->linkname, + "symlinks not supported"); +#endif + return( TRUE); +} + +static int +tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag) +{ + if (extractFlag==FALSE || tostdoutFlag==TRUE) + return( TRUE); + + if (S_ISCHR(header->mode) || S_ISBLK(header->mode) || S_ISSOCK(header->mode)) { + if (mknod(header->name, header->mode, makedev(header->devmajor, header->devminor)) < 0) { + perror_msg("%s: Cannot mknod", header->name); + return( FALSE); + } + } else if (S_ISFIFO(header->mode)) { + if (mkfifo(header->name, header->mode) < 0) { + perror_msg("%s: Cannot mkfifo", header->name); + return( FALSE); + } + } + + /* Now set permissions etc. for the new directory */ + fixUpPermissions(header); + return( TRUE); +} + +/* Parse the tar header and fill in the nice struct with the details */ +static int +readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header) +{ + int i; + long chksum, sum=0; + unsigned char *s = (unsigned char *)rawHeader; + + header->name = rawHeader->name; + /* Check for and relativify any absolute paths */ + if ( *(header->name) == '/' ) { + static int alreadyWarned=FALSE; + + while (*(header->name) == '/') + header->name++; + + if (alreadyWarned == FALSE) { + error_msg("Removing leading '/' from member names"); + alreadyWarned = TRUE; + } + } + + header->mode = strtol(rawHeader->mode, NULL, 8); + header->uid = strtol(rawHeader->uid, NULL, 8); + header->gid = strtol(rawHeader->gid, NULL, 8); + header->size = strtol(rawHeader->size, NULL, 8); + header->mtime = strtol(rawHeader->mtime, NULL, 8); + chksum = strtol(rawHeader->chksum, NULL, 8); + header->type = rawHeader->typeflag; + header->linkname = rawHeader->linkname; + header->devmajor = strtol(rawHeader->devmajor, NULL, 8); + header->devminor = strtol(rawHeader->devminor, NULL, 8); + + /* Check the checksum */ + for (i = sizeof(*rawHeader); i-- != 0;) { + sum += *s++; + } + /* Remove the effects of the checksum field (replace + * with blanks for the purposes of the checksum) */ + s = rawHeader->chksum; + for (i = sizeof(rawHeader->chksum) ; i-- != 0;) { + sum -= *s++; + } + sum += ' ' * sizeof(rawHeader->chksum); + if (sum == chksum ) + return ( TRUE); + return( FALSE); +} + +static int exclude_file(char **excluded_files, const char *file) +{ + int i; + + if (excluded_files == NULL) + return 0; + + for (i = 0; excluded_files[i] != NULL; i++) { + if (excluded_files[i][0] == '/') { + if (fnmatch(excluded_files[i], 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[i], p, + FNM_PATHNAME | FNM_LEADING_DIR) == 0) + return 1; + } + } + } + + return 0; +} + +static int extract_file(char **extract_files, const char *file) +{ + int i; + + if (extract_files == NULL) + return 1; + + for (i = 0; extract_files[i] != NULL; i++) { + if (fnmatch(extract_files[i], file, FNM_LEADING_DIR) == 0) + return 1; + } + + return 0; +} + +/* + * Read a tar file and extract or list the specified files within it. + * If the list is empty than all files are extracted or listed. + */ +static int readTarFile(int tarFd, int extractFlag, int listFlag, + int tostdoutFlag, int verboseFlag, char** extractList, + char** excludeList) +{ + int status; + int errorFlag=FALSE; + int skipNextHeaderFlag=FALSE; + TarHeader rawHeader; + TarInfo header; + + /* Read the tar file, and iterate over it one file at a time */ + while ( (status = full_read(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) { + + /* Try to read the header */ + if ( readTarHeader(&rawHeader, &header) == FALSE ) { + if ( *(header.name) == '\0' ) { + goto endgame; + } else { + errorFlag=TRUE; + error_msg("Bad tar header, skipping"); + continue; + } + } + if ( *(header.name) == '\0' ) + continue; + header.tarFd = tarFd; + + /* Skip funky extra GNU headers that precede long files */ + if ( (header.type == GNULONGNAME) || (header.type == GNULONGLINK) ) { + skipNextHeaderFlag=TRUE; + if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) + errorFlag = TRUE; + continue; + } + if ( skipNextHeaderFlag == TRUE ) { + skipNextHeaderFlag=FALSE; + error_msg(name_longer_than_foo, NAME_SIZE); + if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) + errorFlag = TRUE; + continue; + } + +#if defined BB_FEATURE_TAR_EXCLUDE + if (exclude_file(excludeList, header.name)) { + /* There are not the droids you're looking for, move along */ + /* If it is a regular file, pretend to extract it with + * the extractFlag set to FALSE, so the junk in the tarball + * is properly skipped over */ + if ( header.type==REGTYPE || header.type==REGTYPE0 ) { + if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) + errorFlag = TRUE; + } + continue; + } +#endif + + if (!extract_file(extractList, header.name)) { + /* There are not the droids you're looking for, move along */ + /* If it is a regular file, pretend to extract it with + * the extractFlag set to FALSE, so the junk in the tarball + * is properly skipped over */ + if ( header.type==REGTYPE || header.type==REGTYPE0 ) { + if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) + errorFlag = TRUE; + } + continue; + } + + if (listFlag == TRUE) { + /* Special treatment if the list (-t) flag is on */ + if (verboseFlag == TRUE) { + int len, len1; + char buf[35]; + struct tm *tm = localtime (&(header.mtime)); + + len=printf("%s ", mode_string(header.mode)); + my_getpwuid(buf, header.uid); + if (! *buf) + len+=printf("%d", header.uid); + else + len+=printf("%s", buf); + my_getgrgid(buf, header.gid); + if (! *buf) + len+=printf("/%-d ", header.gid); + else + len+=printf("/%-s ", buf); + + if (header.type==CHRTYPE || header.type==BLKTYPE) { + len1=snprintf(buf, sizeof(buf), "%ld,%-ld ", + header.devmajor, header.devminor); + } else { + len1=snprintf(buf, sizeof(buf), "%lu ", (long)header.size); + } + /* Jump through some hoops to make the columns match up */ + for(;(len+len1)<31;len++) + printf(" "); + printf(buf); + + /* Use ISO 8610 time format */ + if (tm) { + printf ("%04d-%02d-%02d %02d:%02d:%02d ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + } + printf("%s", header.name); + if (verboseFlag == TRUE) { + if (header.type==LNKTYPE) /* If this is a link, say so */ + printf(" link to %s", header.linkname); + else if (header.type==SYMTYPE) + printf(" -> %s", header.linkname); + } + printf("\n"); + } + + /* List contents if we are supposed to do that */ + if (verboseFlag == TRUE && extractFlag == TRUE) { + /* Now the normal listing */ + FILE *vbFd = stdout; + if (tostdoutFlag == TRUE) // If the archive goes to stdout, verbose to stderr + vbFd = stderr; + fprintf(vbFd, "%s\n", header.name); + } + + /* Remove files if we would overwrite them */ + if (extractFlag == TRUE && tostdoutFlag == FALSE) + unlink(header.name); + + /* If we got here, we can be certain we have a legitimate + * header to work with. So work with it. */ + switch ( header.type ) { + case REGTYPE: + case REGTYPE0: + /* If the name ends in a '/' then assume it is + * supposed to be a directory, and fall through */ + if (!last_char_is(header.name,'/')) { + if (tarExtractRegularFile(&header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; + } + case DIRTYPE: + if (tarExtractDirectory( &header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; + case LNKTYPE: + if (tarExtractHardLink( &header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; + case SYMTYPE: + if (tarExtractSymLink( &header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; + case CHRTYPE: + case BLKTYPE: + case FIFOTYPE: + if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE) + errorFlag=TRUE; + break; +#if 0 + /* Handled earlier */ + case GNULONGNAME: + case GNULONGLINK: + skipNextHeaderFlag=TRUE; + break; +#endif + default: + error_msg("Unknown file type '%c' in tar file", header.type); + close( tarFd); + return( FALSE); + } + } + close(tarFd); + if (status > 0) { + /* Bummer - we read a partial header */ + perror_msg("Error reading tar file"); + return ( FALSE); + } + else if (errorFlag==TRUE) { + error_msg( "Error exit delayed from previous errors"); + return( FALSE); + } else + return( status); + + /* Stuff to do when we are done */ +endgame: + close( tarFd); + if ( *(header.name) == '\0' ) { + if (errorFlag==TRUE) + error_msg( "Error exit delayed from previous errors"); + else + return( TRUE); + } + return( FALSE); +} + + +#ifdef BB_FEATURE_TAR_CREATE + +/* +** writeTarFile(), writeFileToTarball(), and writeTarHeader() are +** the only functions that deal with the HardLinkInfo structure. +** Even these functions use the xxxHardLinkInfo() functions. +*/ +typedef struct HardLinkInfo 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 */ + char** 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; + + +/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ +static void +addHardLinkInfo (HardLinkInfo **hlInfoHeadPtr, dev_t dev, ino_t ino, + short linkCount, const char *name) +{ + /* Note: hlInfoHeadPtr can never be NULL! */ + HardLinkInfo *hlInfo; + + hlInfo = (HardLinkInfo *)xmalloc(sizeof(HardLinkInfo)+strlen(name)+1); + if (hlInfo) { + hlInfo->next = *hlInfoHeadPtr; + *hlInfoHeadPtr = hlInfo; + hlInfo->dev = dev; + hlInfo->ino = ino; + hlInfo->linkCount = linkCount; + strcpy(hlInfo->name, name); + } + return; +} + +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 HardLinkInfo * +findHardLinkInfo (HardLinkInfo *hlInfo, dev_t dev, ino_t ino) +{ + while(hlInfo) { + if ((ino == hlInfo->ino) && (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 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) */ + my_getpwuid(header.uname, statbuf->st_uid); + if (! *header.uname) + strcpy(header.uname, "root"); + my_getgrgid(header.gname, statbuf->st_gid); + if (! *header.uname) + strcpy(header.uname, "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 { + 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=full_write(tbInfo->tarFd, (char*)&header, sizeof(struct TarHeader))) < 0) { + error_msg(io_error, real_name, strerror(errno)); + return ( FALSE); + } + /* Pad the header up to the tar block size */ + for (; sizetarFd, "\0", 1); + } + /* Now do the verbose thing (or not) */ + if (tbInfo->verboseFlag==TRUE) { + FILE *vbFd = stdout; + if (tbInfo->tarFd == fileno(stdout)) // If the archive goes to stdout, verbose to stderr + vbFd = stderr; + fprintf(vbFd, "%s\n", header.name); + } + + return ( TRUE); +} + + +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->st_dev, + statbuf->st_ino); + if (tbInfo->hlInfo == NULL) + addHardLinkInfo (&tbInfo->hlInfoHead, statbuf->st_dev, + statbuf->st_ino, statbuf->st_nlink, fileName); + } + + /* It is against the rules to archive a socket */ + if (S_ISSOCK(statbuf->st_mode)) { + 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) { + 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) { + error_msg("Removing leading '/' from member names"); + alreadyWarned=TRUE; + } + header_name++; + } + + if (strlen(fileName) >= NAME_SIZE) { + error_msg(name_longer_than_foo, NAME_SIZE); + return ( TRUE); + } + + if (header_name[0] == '\0') + return TRUE; + +#if defined BB_FEATURE_TAR_EXCLUDE + if (exclude_file(tbInfo->excludeList, header_name)) { + return SKIP; + } +#endif + + 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; + char buffer[BUFSIZ]; + ssize_t size=0, readSize=0; + + /* open the file we want to archive, and make sure all is well */ + if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { + error_msg("%s: Cannot open: %s", fileName, strerror(errno)); + return( FALSE); + } + + /* write the file to the archive */ + while ( (size = full_read(inputFileFd, buffer, sizeof(buffer))) > 0 ) { + if (full_write(tbInfo->tarFd, buffer, size) != size ) { + /* Output file seems to have a problem */ + error_msg(io_error, fileName, strerror(errno)); + return( FALSE); + } + readSize+=size; + } + if (size == -1) { + error_msg(io_error, fileName, strerror(errno)); + return( FALSE); + } + /* 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 int writeTarFile(const char* tarName, int verboseFlag, char **argv, + char** excludeList) +{ + int tarFd=-1; + int errorFlag=FALSE; + ssize_t size; + struct TarBallInfo tbInfo; + tbInfo.verboseFlag = verboseFlag; + tbInfo.hlInfoHead = NULL; + + /* Make sure there is at least one file to tar up. */ + if (*argv == NULL) + error_msg_and_die("Cowardly refusing to create an empty archive"); + + /* Open the tar file for writing. */ + if (!strcmp(tarName, "-")) + tbInfo.tarFd = fileno(stdout); + else + tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (tbInfo.tarFd < 0) { + perror_msg( "Error opening '%s'", tarName); + freeHardLinkInfo(&tbInfo.hlInfoHead); + return ( FALSE); + } + tbInfo.excludeList=excludeList; + /* Store the stat info for the tarball's file, so + * can avoid including the tarball into itself.... */ + if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) + error_msg_and_die(io_error, tarName, strerror(errno)); + + /* Read the directory/files and iterate over them one at a time */ + while (*argv != NULL) { + if (recursive_action(*argv++, TRUE, FALSE, FALSE, + writeFileToTarball, writeFileToTarball, + (void*) &tbInfo) == FALSE) { + errorFlag = TRUE; + } + } + /* 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(tarFd); + if (errorFlag == TRUE) { + error_msg("Error exit delayed from previous errors"); + freeHardLinkInfo(&tbInfo.hlInfoHead); + return(FALSE); + } + freeHardLinkInfo(&tbInfo.hlInfoHead); + return( TRUE); +} + + +#endif + diff --git a/busybox/tee.c b/busybox/tee.c new file mode 100644 index 000000000..439cf7dc5 --- /dev/null +++ b/busybox/tee.c @@ -0,0 +1,68 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tee implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 "busybox.h" +#include +#include + +int +tee_main(int argc, char **argv) +{ + char *mode = "w"; + int c, i, status = 0, nfiles = 0; + FILE **files; + + while ((c = getopt(argc, argv, "a")) != EOF) { + switch (c) { + case 'a': + mode = "a"; + break; + default: + show_usage(); + } + } + + files = (FILE **)xmalloc(sizeof(FILE *) * (argc - optind + 1)); + files[nfiles++] = stdout; + while (optind < argc) { + if ((files[nfiles++] = fopen(argv[optind++], mode)) == NULL) { + nfiles--; + perror_msg("%s", argv[optind-1]); + status = 1; + } + } + + while ((c = getchar()) != EOF) + for (i = 0; i < nfiles; i++) + putc(c, files[i]); + + return status; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/telnet.c b/busybox/telnet.c new file mode 100644 index 000000000..ce82a0ee8 --- /dev/null +++ b/busybox/telnet.c @@ -0,0 +1,711 @@ +/* vi: set sw=4 ts=4: */ +/* + * telnet implementation for busybox + * + * Author: Tomi Ollila + * 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 + * + * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#if 0 +static const int DOTRACE = 1; +#endif + +#ifdef DOTRACE +#include /* 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 +#else +#include +#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) +#else +static struct Globalvars G; +#endif + +static inline void iacflush() +{ + write(G.netfd, G.iacbuf, G.iaclen); + G.iaclen = 0; +} + +/* Function prototypes */ +static int getport(char * p); +static struct in_addr getserver(char * p); +static int create_socket(); +static void setup_sockaddr_in(struct sockaddr_in * addr, int port); +static int remote_connect(struct in_addr addr, int port); +static void rawmode(); +static void cookmode(); +static void do_linemode(); +static void will_charmode(); +static void telopt(byte c); +static int subneg(byte c); +#if 0 +static int local_bind(int port); +#endif + +/* Some globals */ +static int one = 1; + +#ifdef BB_FEATURE_TELNET_TTYPE +static char *ttype; +#endif + +static void doexit(int ev) +{ + cookmode(); + exit(ev); +} + +static void conescape() +{ + 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 */ + + int i; + byte * p = G.buf; + + for (i = len; i > 0; i--, p++) + { + if (*p == 0x1d) + { + conescape(); + return; + } + if (*p == 0xff) + *p = 0x7f; + } + write(G.netfd, G.buf, len); +} + + +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) == TRUE) + 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 BB_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 + +/* void putiacstring (subneg strings) */ + +/* ******************************* */ + +static char const escapecharis[] = "\r\nEscape character is "; + +static void setConMode() +{ + 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() +{ + G.charmode = CHM_TRY; + G.telflags |= (UF_ECHO | UF_SGA); + setConMode(); + + putiac2(DO, TELOPT_ECHO); + putiac2(DO, TELOPT_SGA); + iacflush(); +} + +static void do_linemode() +{ + 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() +{ + /* 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() +{ + /* 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 BB_FEATURE_TELNET_TTYPE +static inline void to_ttype() +{ + /* Tell server we will (or won't) do TTYPE */ + + if(ttype) + putiac2(WILL, TELOPT_TTYPE); + else + putiac2(WONT, TELOPT_TTYPE); + + return; +} +#endif + +static void telopt(byte c) +{ + switch (c) + { + case TELOPT_ECHO: to_echo(c); break; + case TELOPT_SGA: to_sga(c); break; +#ifdef BB_FEATURE_TELNET_TTYPE + case TELOPT_TTYPE: to_ttype(c); break; +#endif + default: to_notsup(c); break; + } +} + + +/* ******************************* */ + +/* subnegotiation -- ignore all (except TTYPE) */ + +static int subneg(byte c) +{ + switch (G.telstate) + { + case TS_SUB1: + if (c == IAC) + G.telstate = TS_SUB2; +#ifdef BB_FEATURE_TELNET_TTYPE + else + if (c == TELOPT_TTYPE) + putiac_subopt(TELOPT_TTYPE,ttype); +#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() +{ + tcsetattr(0, TCSADRAIN, &G.termios_raw); +} + +static void cookmode() +{ + tcsetattr(0, TCSADRAIN, &G.termios_def); +} + +extern int telnet_main(int argc, char** argv) +{ + struct in_addr host; + int port; + int len; +#ifdef USE_POLL + struct pollfd ufds[2]; +#else + fd_set readfds; + int maxfd; +#endif + +#ifdef BB_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) show_usage(); + port = (argc > 2)? getport(argv[2]): 23; + + host = getserver(argv[1]); + + G.netfd = remote_connect(host, port); + + 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); + } + } + } +} + +static int getport(char * p) +{ + unsigned int port = atoi(p); + + if ((unsigned)(port - 1 ) > 65534) + { + error_msg_and_die("%s: bad port number", p); + } + return port; +} + +static struct in_addr getserver(char * host) +{ + struct in_addr addr; + + struct hostent * he; + he = xgethostbyname(host); + memcpy(&addr, he->h_addr, sizeof addr); + + TRACE(1, ("addr: %s\n", inet_ntoa(addr))); + + return addr; +} + +static int create_socket() +{ + return socket(AF_INET, SOCK_STREAM, 0); +} + +static void setup_sockaddr_in(struct sockaddr_in * addr, int port) +{ + memset(addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; + addr->sin_port = htons(port); +} + +#if 0 +static int local_bind(int port) +{ + struct sockaddr_in s_addr; + int s = create_socket(); + + setup_sockaddr_in(&s_addr, port); + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); + + if (bind(s, &s_addr, sizeof s_addr) < 0) + { + char * e = sys_errlist[errno]; + syserrorexit("bind"); + exit(1); + } + listen(s, 1); + + return s; +} +#endif + +static int remote_connect(struct in_addr addr, int port) +{ + struct sockaddr_in s_addr; + int s = create_socket(); + + setup_sockaddr_in(&s_addr, port); + s_addr.sin_addr = addr; + + setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); + + if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0) + { + perror_msg_and_die("Unable to connect to remote host"); + } + return s; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + diff --git a/busybox/test.c b/busybox/test.c new file mode 100644 index 000000000..9c66cbb87 --- /dev/null +++ b/busybox/test.c @@ -0,0 +1,579 @@ +/* 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 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 +#include +#include +#include +#include +#include +#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 ::= +*/ + +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} +}; + +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(); +static int oexpr(); +static int aexpr(); +static int nexpr(); +static int binop(); +static int primary(); +static int filstat(); +static int getn(); +static int newerf(); +static int olderf(); +static int equalf(); +static void syntax(); +static int test_eaccess(); +static int is_a_group_member(); +static void initialize_group_array(); + +extern int +test_main(int argc, char** argv) +{ + int res; + + if (strcmp(applet_name, "[") == 0) { + if (strcmp(argv[--argc], "]")) + 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(op, msg) + char *op; + char *msg; +{ + if (op && *op) + error_msg_and_die("%s: %s", op, msg); + else + error_msg_and_die("%s", msg); +} + +static int +oexpr(n) + enum token n; +{ + int res; + + res = aexpr(n); + if (t_lex(*++t_wp) == BOR) + return oexpr(t_lex(*++t_wp)) || res; + t_wp--; + return res; +} + +static int +aexpr(n) + enum token n; +{ + int res; + + res = nexpr(n); + if (t_lex(*++t_wp) == BAND) + return aexpr(t_lex(*++t_wp)) && res; + t_wp--; + return res; +} + +static int +nexpr(n) + enum token n; /* token */ +{ + if (n == UNOT) + return !nexpr(t_lex(*++t_wp)); + return primary(n); +} + +static int +primary(n) + enum token n; +{ + int 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(nm, mode) + 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(s) + 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 int +getn(s) + char *s; +{ + char *p; + long r; + + errno = 0; + r = strtol(s, &p, 10); + + if (errno != 0) + error_msg_and_die("%s: out of range", s); + + while (isspace(*p)) + p++; + + if (*p) + error_msg_and_die("%s: bad number", s); + + return (int) r; +} + +static int +newerf (f1, f2) +char *f1, *f2; +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_mtime > b2.st_mtime); +} + +static int +olderf (f1, f2) +char *f1, *f2; +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_mtime < b2.st_mtime); +} + +static int +equalf (f1, f2) +char *f1, *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 (path, mode) +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) +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/tests/.cvsignore b/busybox/tests/.cvsignore new file mode 100644 index 000000000..3645cf92f --- /dev/null +++ b/busybox/tests/.cvsignore @@ -0,0 +1,16 @@ +cp +cp_*.bb +cp_*.gnu +cp_tests +date +df +du +ln +ln_*.bb +ln_*.gnu +ln_tests +mv +mv_*.bb +mv_*.gnu +mv_tests +syslog_test diff --git a/busybox/tests/Makefile b/busybox/tests/Makefile new file mode 100644 index 000000000..8ad304d86 --- /dev/null +++ b/busybox/tests/Makefile @@ -0,0 +1,34 @@ +# busybox/tests/Makefile - Run through all defined tests. +# ------------------------ +# Copyright (C) 2000 Karl M. Hegbloom GPL + +all:: message_header + +message_header: + @echo + @echo BusyBox Test Suite. + @echo + (cd ..; tests/busybox.REGRESS.sh) + +clean:: + +distclean: clean + +.PHONY: all clean distclean message_header + +include $(wildcard *_tests.mk) + +BBL := $(shell pushd .. >/dev/null && \ + ${MAKE} busybox.links >/dev/null && \ + popd >/dev/null && \ + cat ../busybox.links | \ + sed -e 's,.*/\(.*\)$$,\1,') + +../busybox: + cd .. && ${MAKE} busybox + +${BBL}: ../busybox + rm -f $@ + ln ../busybox $@ + +syslog_test: syslog_test.c diff --git a/busybox/tests/cp_tests.mk b/busybox/tests/cp_tests.mk new file mode 100644 index 000000000..b96c5cea6 --- /dev/null +++ b/busybox/tests/cp_tests.mk @@ -0,0 +1,360 @@ +# cp_tests.mk - Set of test cases for busybox cp +# ------------- +# Copyright (C) 2000 Karl M. Hegbloom GPL +# + +# GNU `cp' +GCP = /bin/cp +# BusyBox `cp' +BCP = $(shell pwd)/cp + +all:: cp_tests +clean:: cp_clean + +cp_clean: + - rm -rf cp_tests cp_*.{gnu,bb} cp + +# check_cp_dir_to_dir_wo_a removed from this list; see below +cp_tests: cp_clean cp check_exists check_simple_cp check_cp_symlnk \ + check_cp_symlink_w_a check_cp_files_to_dir check_cp_files_to_dir_w_d \ + check_cp_files_to_dir_w_p check_cp_files_to_dir_w_p_and_d \ + check_cp_dir_to_dir_w_a \ + check_cp_dir_to_dir_w_a_take_two + +check_exists: + @echo; + @echo "No output from diff means busybox cp is functioning properly."; + @echo "Some tests might show timestamp differences that are Ok."; + + @echo; + @echo Verify that busybox cp exists; + @echo ------------------------------; + [ -x ${BCP} ] || exit 0 + + @echo; + mkdir cp_tests; + +check_simple_cp: + @echo Copy a file to a copy of the file; + @echo ------------------------------; + cd cp_tests; \ + echo A file > afile; \ + ls -l afile > ../cp_afile_afilecopy.gnu; \ + ${GCP} afile afilecopy; \ + ls -l afile afilecopy >> ../cp_afile_afilecopy.gnu; + + @echo; + rm -rf cp_tests/*; + + @echo; + cd cp_tests; \ + echo A file > afile; \ + ls -l afile > ../cp_afile_afilecopy.bb; \ + ${BCP} afile afilecopy; \ + ls -l afile afilecopy >> ../cp_afile_afilecopy.bb; + + @echo; + @echo Might show timestamp differences. + -diff -u cp_afile_afilecopy.gnu cp_afile_afilecopy.bb; + + @echo; + rm -rf cp_tests/*; + +check_cp_symlnk: + @echo; echo Copy a file pointed to by a symlink; + @echo ------------------------------; + cd cp_tests; \ + mkdir here there; \ + echo A file > afile; \ + cd here; \ + ln -s ../afile .; \ + + @echo; + cd cp_tests; \ + ls -lR . > ../cp_symlink.gnu; \ + ${GCP} here/afile there; \ + ls -lR . >> ../cp_symlink.gnu; + + @echo; + rm -rf cp_tests/there/*; + + sleep 1; + + @echo; + cd cp_tests; \ + ls -lR . > ../cp_symlink.bb; \ + ${BCP} here/afile there; \ + ls -lR . >> ../cp_symlink.bb; + + @echo; + @echo Will show timestamp difference. + -diff -u cp_symlink.gnu cp_symlink.bb; + + @echo; + rm -rf cp_tests/* + +check_cp_symlink_w_a: + @echo; echo Copy a symlink, useing the -a switch.; + @echo ------------------------------; + cd cp_tests; \ + echo A file > afile; \ + mkdir here there; \ + cd here; \ + ln -s ../afile . + + cd cp_tests; \ + ls -lR . > ../cp_a_symlink.gnu; \ + ${GCP} -a here/afile there; \ + ls -lR . >> ../cp_a_symlink.gnu; + + @echo; + rm -rf cp_tests/there/*; + + sleep 1; + + @echo; + cd cp_tests; \ + echo A file > afile; \ + ls -lR . > ../cp_a_symlink.bb; \ + ${BCP} -a here/afile there; \ + ls -lR . >> ../cp_a_symlink.bb; + + @echo; + diff -u cp_a_symlink.gnu cp_a_symlink.bb; + + @echo; + rm -rf cp_tests/*; + + +check_cp_files_to_dir: + # Copy a set of files to a directory. + @echo; echo Copy a set of files to a directory.; + @echo ------------------------------; + cd cp_tests; \ + echo A file number one > afile1; \ + echo A file number two, blah. > afile2; \ + ln -s afile1 symlink1; \ + mkdir there; + + cd cp_tests; \ + ${GCP} afile1 afile2 symlink1 there/; \ + ls -lR > ../cp_files_dir.gnu; + + @echo; + rm -rf cp_tests/there/*; + + @echo; + cd cp_tests; \ + ${BCP} afile1 afile2 symlink1 there/; \ + ls -lR > ../cp_files_dir.bb; + + @echo; + diff -u cp_files_dir.gnu cp_files_dir.bb; + + @echo; + rm -rf cp_tests/*; + +check_cp_files_to_dir_w_d: + # Copy a set of files to a directory with the -d switch. + @echo; echo Copy a set of files to a directory with the -d switch.; + @echo ------------------------------; + cd cp_tests; \ + echo A file number one > afile1; \ + echo A file number two, blah. > afile2; \ + ln -s afile1 symlink1; \ + mkdir there1; \ + ${GCP} -d afile1 afile2 symlink1 there1/; \ + ls -lR > ../cp_d_files_dir.gnu; + + @echo; + rm -rf cp_tests/{afile{1,2},symlink1,there1}; + + @echo; + cd cp_tests; \ + echo A file number one > afile1; \ + echo A file number two, blah. > afile2; \ + ln -s afile1 symlink1; \ + mkdir there1; \ + ${BCP} -d afile1 afile2 symlink1 there1/; \ + ls -lR > ../cp_d_files_dir.bb; + + @echo; + diff -u cp_d_files_dir.gnu cp_d_files_dir.bb; + + @echo; + rm -rf cp_tests/{afile{1,2},symlink1,there1}; + +check_cp_files_to_dir_w_p: + # Copy a set of files to a directory with the -p switch. + @echo; echo Copy a set of files to a directory with the -p switch.; + @echo ------------------------------; + cd cp_tests; \ + echo A file number one > afile1; \ + echo A file number two, blah. > afile2; \ + touch --date='Sat Jan 29 21:24:08 PST 2000' afile1; \ + ln -s afile1 symlink1; \ + mkdir there1; \ + ${GCP} -p afile1 afile2 symlink1 there1/; \ + ls -lR > ../cp_p_files_dir.gnu; + + @echo; + rm -rf cp_tests/{afile{1,2},symlink1,there1}; + + @echo; + cd cp_tests; \ + echo A file number one > afile1; \ + echo A file number two, blah. > afile2; \ + touch --date='Sat Jan 29 21:24:08 PST 2000' afile1; \ + ln -s afile1 symlink1; \ + mkdir there1; \ + ${BCP} -p afile1 afile2 symlink1 there1/; \ + ls -lR > ../cp_p_files_dir.bb; + + @echo; + diff -u cp_p_files_dir.gnu cp_p_files_dir.bb; + + @echo; + rm -rf cp_tests/{afile{1,2},symlink1,there1}; + + +check_cp_files_to_dir_w_p_and_d: + @echo; echo Copy a set of files to a directory with -p and -d switches. + @echo ------------------------------; + cd cp_tests; \ + echo A file number one > afile1; \ + echo A file number two, blah. > afile2; \ + touch --date='Sat Jan 29 21:24:08 PST 2000' afile1; \ + ln -s afile1 symlink1; \ + mkdir there1; \ + ${GCP} -p -d afile1 afile2 symlink1 there1/; \ + ls -lR > ../cp_pd_files_dir.gnu; + + @echo; + rm -rf cp_tests/{afile{1,2},symlink1,there1}; + + @echo; + cd cp_tests; \ + echo A file number one > afile1; \ + echo A file number two, blah. > afile2; \ + touch --date='Sat Jan 29 21:24:08 PST 2000' afile1; \ + ln -s afile1 symlink1; \ + mkdir there1; \ + ${BCP} -p -d afile1 afile2 symlink1 there1/; \ + ls -lR > ../cp_pd_files_dir.bb; + + @echo; + diff -u cp_pd_files_dir.gnu cp_pd_files_dir.bb; + + @echo; + rm -rf cp_tests/{afile{1,2},symlink1,there1}; + +# This test doesn't work any more; gnu cp now _does_ copy a directory +# to a subdirectory of itself. What's worse, that "feature" has no +# (documented) way to be disabled with command line switches. +# It's not obvious that busybox cp should mimic this behavior. +# For now, this test is removed from the cp_tests list, above. +check_cp_dir_to_dir_wo_a: + # Copy a directory to another directory, without the -a switch. + @echo; echo Copy a directory to another directory, without the -a switch. + @echo ------------------------------; + @echo There should be an error message about cannot cp a dir to a subdir of itself. + cd cp_tests; \ + touch a b c; \ + mkdir adir; \ + ls -lR . > ../cp_a_star_adir.gnu; \ + ${GCP} -a * adir; \ + ls -lR . >> ../cp_a_star_adir.gnu; + + @echo + @echo There should be an error message about cannot cp a dir to a subdir of itself. + cd cp_tests; \ + rm -rf adir; \ + mkdir adir; \ + ls -lR . > ../cp_a_star_adir.bb; \ + ${BCP} -a * adir; \ + ls -lR . >> ../cp_a_star_adir.bb; + + @echo; + diff -u cp_a_star_adir.gnu cp_a_star_adir.bb; + + # Done + @echo; + rm -rf cp_tests; + @echo; echo Done. + + +check_cp_dir_to_dir_w_a: + @echo; echo Copy a directory into another directory with the -a switch. + @echo ------------------------------; + cd cp_tests; \ + mkdir dir{a,b}; \ + echo A file > dira/afile; \ + echo A file in dirb > dirb/afileindirb; \ + ln -s dira/afile dira/alinktoafile; \ + mkdir dira/subdir1; \ + echo Another file > dira/subdir1/anotherfile; \ + ls -lR . > ../cp_a_dira_dirb.gnu; \ + ${GCP} -a dira dirb; \ + ls -lR . >> ../cp_a_dira_dirb.gnu; + + @echo; + rm -rf cp_tests/dir{a,b}; + + @echo; + cd cp_tests; \ + mkdir dir{a,b}; \ + echo A file > dira/afile; \ + echo A file in dirb > dirb/afileindirb; \ + ln -s dira/afile dira/alinktoafile; \ + mkdir dira/subdir1; \ + echo Another file > dira/subdir1/anotherfile; \ + ls -lR . > ../cp_a_dira_dirb.bb; \ + ${BCP} -a dira dirb; \ + ls -lR . >> ../cp_a_dira_dirb.bb; + + @echo; + diff -u cp_a_dira_dirb.gnu cp_a_dira_dirb.bb; + + @echo; + rm -rf cp_tests/dir{a,b}; + + +check_cp_dir_to_dir_w_a_take_two: + @echo; echo Copy a directory into another directory with the -a switch; + @echo ------------------------------; + mkdir -p cp_tests/gnu; \ + mkdir -p cp_tests/bb; \ + cd cp_tests; \ + mkdir here there; \ + echo A file > here/afile; \ + mkdir here/adir; \ + touch here/adir/afileinadir; \ + ln -s $$(pwd) here/alink; + + @echo; + cd cp_tests/gnu; \ + ls -lR . > ../../cp_a_dir_dir.gnu; \ + ${GCP} -a here/ there/; \ + ls -lR . >> ../../cp_a_dir_dir.gnu; + + @echo; + rm -rf cp_tests/there/*; + + sleep 1; + + @echo; + cd cp_tests/bb; \ + ls -lR . > ../../cp_a_dir_dir.bb; \ + ${BCP} -a here/ there/; \ + ls -lR . >> ../../cp_a_dir_dir.bb; + + @echo; + echo "Erik 1" + diff -u cp_a_dir_dir.gnu cp_a_dir_dir.bb; + echo "Erik 2" + + @echo; + echo "Erik 3" + rm -rf cp_tests/*; + + diff --git a/busybox/tests/ln_tests.mk b/busybox/tests/ln_tests.mk new file mode 100644 index 000000000..3110f8199 --- /dev/null +++ b/busybox/tests/ln_tests.mk @@ -0,0 +1,71 @@ +# ln_tests.mk - Set of tests for busybox ln +# ------------- +# Copyright (C) 2000 Karl M. Hegbloom GPL +# + +# GNU `ln' +GLN = /bin/ln +# BusyBox `ln' +BLN = $(shell pwd)/ln + +all:: ln_tests +clean:: ln_clean + +ln_clean: + rm -rf ln_tests ln_*.{gnu,bb} ln + +ln_tests: ln_clean ln + @echo; + @echo "No output from diff means busybox ln is functioning properly."; + + @echo; + ${BLN} || true; + + @echo; + mkdir ln_tests; + + @echo; + cd ln_tests; \ + echo A file > afile; \ + ls -l afile > ../ln_afile_newname.gnu; \ + ${GLN} afile newname; \ + ls -l afile newname >> ../ln_afile_newname.gnu; + + @echo; + rm -f ln_tests/{afile,newname}; + + @echo; + cd ln_tests; \ + echo A file > afile; \ + ls -l afile > ../ln_afile_newname.bb; \ + ${BLN} afile newname; \ + ls -l afile newname >> ../ln_afile_newname.bb; + + @echo; + diff -u ln_afile_newname.gnu ln_afile_newname.bb + + @echo; + rm -f ln_tests/{afile,newname}; + + @echo; + cd ln_tests; \ + echo A file > afile; \ + ls -l afile > ../ln_s_afile_newname.gnu; \ + ${GLN} -s afile newname; \ + ls -l afile newname >> ../ln_s_afile_newname.gnu; + + @echo; + rm -f ln_tests/{afile,newname}; + + @echo; + cd ln_tests; \ + echo A file > afile; \ + ls -l afile > ../ln_s_afile_newname.bb; \ + ${BLN} -s afile newname; \ + ls -l afile newname >> ../ln_s_afile_newname.bb; + + @echo; + diff -u ln_s_afile_newname.gnu ln_s_afile_newname.bb + + @echo; + rm -f ln_tests/{afile,newname}; diff --git a/busybox/tests/multibuild.pl b/busybox/tests/multibuild.pl new file mode 100755 index 000000000..94930bd95 --- /dev/null +++ b/busybox/tests/multibuild.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl + +# multibuild.pl +# Tests BusyBox-0.48 (at least) to see if each applet builds +# properly on its own. The most likely problems this will +# flush out are those involving preprocessor instructions in +# utility.c. +# +# TODO: some time it might be nice to list absolute and +# differential object sizes for each option... +# + +$logfile = "multibuild.log"; + +# How to handle all the BB_FEATURE_FOO lines +if ($ARGV[0] eq "-all" ) { shift(@ARGV); $choice="all"; } +if ($ARGV[0] eq "-none") { shift(@ARGV); $choice="none"; } +# neither means, leave that part of Config.h alone + +# Support building from pristine source +$make_opt = "-f $ARGV[0]/Makefile BB_SRC_DIR=$ARGV[0]" if ($ARGV[0] ne ""); + +# Move the config file to a safe place +-e "Config.h.orig" || 0==system("mv -f Config.h Config.h.orig") || die; + +# Clear previous log file, if any +unlink($logfile); + +# Parse the config file +open(C,") { + if ($in_trailer) { + if (!$in_olympus) { + s/^\/\/#/#/ if ($choice eq "all" && !/USE_DEVPS_PATCH/); + s/^#/\/\/#/ if ($choice eq "none"); + } + $in_olympus=1 if /End of Features List/; + $trailer .= $_; + } else { + $in_trailer=1 if /End of Applications List/; + if (/^\/*#define BB_([A-Z0-9_]*)/) { + push @apps, $1; + } + } +} +close C; + +# Do the real work ... +$failed_tests=0; +for $a (@apps) { + # print "Testing build of applet $a ...\n"; + open (O, ">Config.h") || die; + print O "#define BB_$a\n", $trailer; + close O; + system("echo -e '\n***\n$a\n***' >>$logfile"); + # With a fast computer and 1-second resolution on file timestamps, this + # process pushes beyond the limits of what unix make can understand. + # That's why need to weed out obsolete files before restarting make. + $result{$a} = system("rm -f *.o applet_source_list; make $make_opt busybox >>$logfile 2>&1"); + $flag = $result{$a} ? "FAILED!!!" : "ok"; + printf("Applet %-20s: %s\n", $a, $flag); + $total_tests++; + $failed_tests++ if $flag eq "FAILED!!!"; + # pause long enough to let user stop us with a ^C + select(undef, undef, undef, 0.03); +} + +# Clean up our mess +system("mv -f Config.h.orig Config.h"); + +print "$total_tests applets tested, $failed_tests failures\n"; +print "See $logfile for details.\n"; + diff --git a/busybox/tests/multifeat.pl b/busybox/tests/multifeat.pl new file mode 100755 index 000000000..adcb30bbd --- /dev/null +++ b/busybox/tests/multifeat.pl @@ -0,0 +1,83 @@ +#!/usr/bin/perl +# +# multifeat.pl +# +# Turns on all applets, then tests turning on one feature at a time through +# iterative compilations. Tests if any features depend on each other in any +# weird ways or such-like problems. +# +# Hacked by Mark Whitley, but based *heavily* on multibuild.pl which was +# written by Larry Doolittle. + +$logfile = "multifeat.log"; + +# How to handle all the BB_APPLET lines +# (most thorough testing occurs when you call it with the -all switch) +if ($ARGV[0] eq "-all" ) { shift(@ARGV); $choice="all"; } +if ($ARGV[0] eq "-none") { shift(@ARGV); $choice="none"; } +# neither means, leave that part of Config.h alone + +# Support building from pristine source +$make_opt = "-f $ARGV[0]/Makefile BB_SRC_DIR=$ARGV[0]" if ($ARGV[0] ne ""); + +# Move the config file to a safe place +-e "Config.h.orig" || 0==system("mv -f Config.h Config.h.orig") || die; + +# Clear previous log file, if any +unlink($logfile); + +# Parse the config file +open(C,") { + if ($in_applist) { + s/^\/\/#/#/ if ($choice eq "all"); + s/^#/\/\/#/ if ($choice eq "none"); + $header .= $_; + if (/End of Applications List/) { + $in_applist=0; + $in_features=1 + } + } + elsif ($in_features) { + if (/^\/*#define BB_FEATURE_([A-Z0-9_]*)/) { + push @features, $1; + } + if (/End of Features List/) { + $in_features=0; + $in_olympus=1 + } + } elsif ($in_olympus) { + $trailer .= $_; + } +} +close C; + +# Do the real work ... +$failed_tests=0; +for $f (@features) { + # print "Testing build with feature $f ...\n"; + open (O, ">Config.h") || die; + print O $header, "#define BB_FEATURE_$f\n", $trailer; + close O; + system("echo -e '\n***\n$f\n***' >>$logfile"); + # With a fast computer and 1-second resolution on file timestamps, this + # process pushes beyond the limits of what unix make can understand. + # That's why need to weed out obsolete files before restarting make. + $result{$f} = system("rm -f *.o applet_source_list; make $make_opt busybox >>$logfile 2>&1"); + $flag = $result{$f} ? "FAILED!!!" : "ok"; + printf("Feature %-20s: %s\n", $f, $flag); + $total_tests++; + $failed_tests++ if $flag eq "FAILED!!!"; + # pause long enough to let user stop us with a ^C + select(undef, undef, undef, 0.03); +} + +# Clean up our mess +system("mv -f Config.h.orig Config.h"); + +print "$total_tests applets tested, $failed_tests failures\n"; +print "See $logfile for details.\n"; + diff --git a/busybox/tests/mv_tests.mk b/busybox/tests/mv_tests.mk new file mode 100644 index 000000000..f03e08a73 --- /dev/null +++ b/busybox/tests/mv_tests.mk @@ -0,0 +1,167 @@ +# mv_tests.mk - Set of tests cases for busybox mv +# ------------- +# Copyright (C) 2000 Karl M. Hegbloom GPL +# + +# GNU `mv' +GMV = /bin/mv +# BusyBox `mv' +BMV = $(shell pwd)/mv + +all:: mv_tests +clean:: mv_clean + +mv_clean: + rm -rf mv_tests mv_*.{gnu,bb} mv + +mv_tests: mv_clean mv + @echo; + @echo "No output from diff means busybox mv is functioning properly."; + @echo; + @echo "No such file or directory is good; it means the old file got removed."; + @echo; + ${BMV} || true; + + @echo; + mkdir mv_tests; + + @echo; + cd mv_tests; \ + echo A file > afile; \ + ls -l afile > ../mv_afile_newname.gnu; \ + ${GMV} afile newname; \ + ls -l newname >> ../mv_afile_newname.gnu; + -ls -l mv_tests/afile; + + @echo; + rm -f mv_tests/{afile,newname}; + + @echo; + cd mv_tests; \ + echo A file > afile; \ + ls -l afile > ../mv_afile_newname.bb; \ + ${BMV} afile newname; \ + ls -l newname >> ../mv_afile_newname.bb; + -ls -l mv_tests/afile; + + @echo; + diff -u mv_afile_newname.gnu mv_afile_newname.bb; + + @echo; + rm -f mv_tests/{afile,newname}; + + @echo; echo ------------------------------; + cd mv_tests; \ + echo A file > afile; \ + ln -s afile symlink; \ + ls -l afile symlink > ../mv_symlink_newname.gnu; \ + ${GMV} symlink newname; \ + ls -l afile newname >> ../mv_symlink_newname.gnu; + -ls -l mv_tests/symlink; + + @echo; + rm -f mv_tests/{afile,newname}; + + @echo; + cd mv_tests; \ + echo A file > afile; \ + ln -s afile symlink; \ + ls -l afile symlink > ../mv_symlink_newname.bb;\ + ${BMV} symlink newname; \ + ls -l afile newname >> ../mv_symlink_newname.bb; + -ls -l mv_tests/symlink; + + @echo; + diff -u mv_symlink_newname.gnu mv_symlink_newname.bb; + + @echo; + rm -rf mv_tests/*; + + @echo; echo ------------------------------; + cd mv_tests; \ + echo A file > afile; \ + ln -s afile symlink; \ + mkdir newdir; \ + ls -lR > ../mv_file_symlink_dir.gnu; \ + ${GMV} symlink afile newdir; \ + ls -lR >> ../mv_file_symlink_dir.gnu; + -ls -l mv_tests/{symlink,afile}; + + @echo; + rm -rf mv_tests/* + + @echo; echo ------------------------------; + cd mv_tests; \ + echo A file > afile; \ + ln -s afile symlink; \ + mkdir newdir; \ + ls -lR > ../mv_file_symlink_dir.bb; \ + ${BMV} symlink afile newdir; \ + ls -lR >> ../mv_file_symlink_dir.bb; + -ls -l mv_tests/{symlink,afile}; + + @echo; + diff -u mv_file_symlink_dir.gnu mv_file_symlink_dir.bb; + + @echo; + rm -rf mv_tests/*; + + @echo; echo ------------------------------; + cd mv_tests; \ + mkdir dir{a,b}; \ + echo A file > dira/afile; \ + echo A file in dirb > dirb/afileindirb; \ + ln -s dira/afile dira/alinktoafile; \ + mkdir dira/subdir1; \ + echo Another file > dira/subdir1/anotherfile; \ + ls -lR . > ../mv_dira_dirb.gnu; \ + ${GMV} dira dirb; \ + ls -lR . >> ../mv_dira_dirb.gnu; + + # false; + @echo; + rm -rf mv_tests/dir{a,b}; + + @echo; + cd mv_tests; \ + mkdir dir{a,b}; \ + echo A file > dira/afile; \ + echo A file in dirb > dirb/afileindirb; \ + ln -s dira/afile dira/alinktoafile; \ + mkdir dira/subdir1; \ + echo Another file > dira/subdir1/anotherfile; \ + ls -lR . > ../mv_dira_dirb.bb; \ + ${BMV} dira dirb; \ + ls -lR . >> ../mv_dira_dirb.bb; + + @echo; + diff -u mv_dira_dirb.gnu mv_dira_dirb.bb; + + # false; + @echo; + rm -rf mv_tests/dir{a,b}; + + @echo; echo ------------------------------; + @echo There should be an error message about cannot mv a dir to a subdir of itself. + cd mv_tests; \ + mkdir adir; \ + touch -r . a b c adir; \ + ls -lR . > ../mv_a_star_adir.gnu; \ + ${GMV} * adir; \ + ls -lR . >> ../mv_a_star_adir.gnu; + + @echo + @echo There should be an error message about cannot mv a dir to a subdir of itself. + cd mv_tests; \ + rm -rf a b c adir; \ + mkdir adir; \ + touch -r . a b c adir; \ + ls -lR . > ../mv_a_star_adir.bb; \ + ${BMV} * adir; \ + ls -lR . >> ../mv_a_star_adir.bb; + + @echo; + diff -u mv_a_star_adir.gnu mv_a_star_adir.bb; + + @echo; + rm -rf mv_test/*; diff --git a/busybox/tests/sh.testcases b/busybox/tests/sh.testcases new file mode 100644 index 000000000..e2a75873e --- /dev/null +++ b/busybox/tests/sh.testcases @@ -0,0 +1,89 @@ +# try running this with bash, ksh, ash, and hush. + +# simple quoting rules. +echo a b +echo "a b" +echo a "" b +echo a '' b +echo hello? +echo "hello?" +echo t* hello +echo t\* hello + +# quick and painless exit for lash +if false; then true; exit; fi + +# fairly simple command substitution +echo `echo -e foo\\\necho bar` + +echo THIS IS A TEST >foo +cat $(echo FOO | tr 'A-Z' 'a-z') +cat foo | tr 'A-Z' 'a-z' +cat $(echo FOO | tr 'A-Z' 'a-z') | tr 'A-Z' 'a-z' + +cat foo | if true; then tr 'A-Z' 'a-z'; else echo bar1; fi +cat foo | if false; then tr 'A-Z' 'a-z'; else echo bar2; fi +if true; then tr 'A-Z' 'a-z'; else echo bar3; fi foo 2>&1 +cat foo +cat doesnt_exist >foo 2>&1 +tr 'a-z' 'A-Z' $TMP +ls fish + +# ash, lash, and hush do not create fish; bash and ksh do. +# Thanks to Tapani Tarvainen for this stress test. +unset TMP +rm -f fish +TMP=fish >$TMP +ls fish + +# The following example shows that hush's parser is +# not _really_ Bourne compatible +echo "echo Hello World" >"a=b" +unset a +chmod a+x "a=b" +PATH=$PATH:. +"a=b" +echo $a + +# assuming the shell wasn't too buggy, clean up the mess +rm -f a=b fish foo diff --git a/busybox/tests/syslog_test.c b/busybox/tests/syslog_test.c new file mode 100644 index 000000000..fb4c691b1 --- /dev/null +++ b/busybox/tests/syslog_test.c @@ -0,0 +1,19 @@ +#include + +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); +} diff --git a/busybox/tests/testcases b/busybox/tests/testcases new file mode 100644 index 000000000..4708e54e2 --- /dev/null +++ b/busybox/tests/testcases @@ -0,0 +1,406 @@ +# testcases +# +# This file should be filled with test cases to test applets that: +# +# - can somehow produce output (we can't test sync or sleep) +# - have a GNU (or other) counterpart +# - are not interactive (don't require a ^C or anything) +# - don't require extensive setup or cleanup (a litte setup is fine) +# - don't have huge and possibly damaging effects (fsck, swapoff) +# +# If possible, a test case should be made that tests each option the applet +# supports. When a new option is added, a new test case should be written for +# it. When somebody reports a bug with a testcase, that testcase should be +# added here as well. +# +# Some other guidelines to follow: +# +# - please try to keep applets alphabetized, it will make life easier +# - use the file tester.sh or testcases when you need to do a non-destructive +# test on a file (i.e., cat, md5sum) +# - try to make the applet you're testing the first thing on the line (this +# not always possible) +# - (???) if you have to create a temporary file, call it TMPFILE +# - pipe symbols that represent real pipes need a space in front of them +# (so the test script can find them and add the "../busybox" after it). +# - pipe symbols that are not used for pipes need to be shell-escaped, +# with a double \. See the expr test cases. + + +# ar + +# basename +basename `pwd` + +# cat +cat tester.sh +echo hello there | cat tester.sh - + +# chmod +# chown +# chgrp +# chroot +# chvt - can't be tested here +# clear - can't be tested here +# cmp +# cp + +# cut +echo "1234" | cut -c1 +echo "1234" | cut -c 1 +echo "1234567890" | cut -c2-7 +echo "1234567890" | cut -c 2-7 +echo "f1 f2" | cut -f2 +echo "f1 f2" | cut -f 2 +echo "f1 f2 f3 f4 f5" | cut -f2-4 +echo "f1 f2 f3 f4 f5" | cut -f 2-4 + +# date +date +date -R +date -u +date +%d/%m/%y + +# dc - needs an input file + +# dd +# BUG: record count line goes to stdout instead of stderr +dd if=/dev/urandom of=O bs=1k count=1 ; ls -l O ; rm O + +# deallocvt + +# df +# XXX: minor formatting differences +df +df . +df -k +df -h +df -m + +# dirname +dirname `pwd` + +# dmesg (XXX: change the silly cmd business in the source) +dmesg +dmesg -n 8 +dmesg -s 512 +# I really don't want to do this next one +#dmesg -c + +# dos2unix - needs an input file +# dpkg +# dpkg_deb + +# du +# BUG: rounding behavior differs from GNU du +du +du -s +du -l +du -k +du -h +du -m + +# dumpkmap - no counterprt? +# dutmp - no counterprt? + +# echo +echo "foo bar baz" +echo -n "no newline" + + +# expr +expr 1 \\| 1 +expr 1 \\| 0 +expr 0 \\| 1 +expr 0 \\| 0 + +expr 1 \\& 1 +expr 1 \\& 0 +expr 0 \\& 1 +expr 0 \\& 0 + +expr 0 \\< 1 +expr 1 \\< 0 + +expr 1 \\> 0 +expr 0 \\> 1 + +expr 0 \\<= 1 +expr 1 \\<= 0 +expr 1 \\<= 1 + +expr 1 \\>= 0 +expr 0 \\>= 1 +expr 1 \\>= 1 + +expr 1 + 2 +expr 2 - 1 +expr 2 \\* 3 +expr 12 / 2 +expr 12 % 5 + +# somebody else can do all the string stuff + + +# fbset - can't be tested here +# fdflush +# find +find . + +# free +# XXX: minor formatting differences +free + +# freeramdisk +# fsck.minix - won't test +# getopt + +# grep +grep -l strdup ../*.c +grep -c strdup ../*.c +grep -lc strdup ../*.c +grep -cv strdup ../*.c +grep -i null ../grep.c +grep -e strdup -e regcomp -e atexit ../grep.c + +# gunzip + +# gzip +# XXX: compressed output differs from gzip-1.2.4, but decompresses fine +echo testing 1 2 3 >tmpfile1; gzip tmpfile1; echo tmpfile*; md5sum tmpfile1.gz; rm tmpfile1.gz +echo testing 1 2 3 | gzip >tmpfile1.gz; md5sum tmpfile1.gz; rm tmpfile1.gz + + +# halt - won't test, dangerous + +# head +head tester.sh +head -n 2 tester.sh + +# hostid +hostid + +# hostname +# XXX: minor formatting differences +hostname +hostname -s +hostname -i +hostname -d +# not going to do this next one +#hostname -F + +# id +# BUG: Busybox id doesn't print supplemental groups +id +id -u +id -g +id -ur +id -un + + +# ifconfig +# requires BB_FEATURE_IFCONFIG_STATUS +ifconfig +#ifconfig -a +#ifconfig eth0 +#ifconfig lo + +# init - won't test +# insmod - won't test + +# kill +#kill -l +# not going to do any more + +# length +# ln - see ln_tests.mk +# loadacm +# loadfont +# loadkmap +# logger +# logname + +# ls +# XXX: minor formatting differences +ls ../e* +ls -l ../e* +ls -s ../e* +ls -h ../e* +ls -1 ../e* + +# lsmod +lsmod + +# makedevs + +# md5sum +md5sum tester.sh + +# mkdir +mkdir D ; ls -ld D ; rmdir D + +# mkfifo +# +# we will test making one. actually testing pushing data through it requires +# more interaction than we can manage here. +# (these lines turn up an existing ls bug) +mkfifo F ; ls -l F ; rm F +mkfifo -m 0600 F ; ls -l F ; rm F + +# mkfs.minix - won't test +# mknod +# mkswap - won't test +# mktemp +# more - can't test: interactive + +# mount +# BUG: proc line starts with /proc instead of proc +mount +# not going to test mount with any args, can't be done safely or sanely + +# mt +# mv - see mv_tests.mk +# nc +# nfsmount +# nslookup +# ping +ping -c 3 yahoo.com +# pivot_root +# poweroff - won't test +# printf +# ps - there's lotsa differences between busybox ps and any other ps + +# pwd +pwd + +# rdate - won't test + +# readlink +ln -sf tester.sh L ; readlink L ; rm -f L + +# reboot - won't test +# renice - won't test +# reset - can't test: no output + +# rm +touch F ; rm F + +# rmdir +# rmmod - won't test: dangerous + +# route +# XXX: doesn't DNS resolve +route + +# rpm2cpio + +# rpmunpack + +# sed - we can do some one-liners here, some testing is a little +# difficult to do in just this space (like a,i,c cmds). + +# test ^$ matching +echo foo | sed -ne '/^$/p' +echo -e "foo\\n\\nbar" | sed -ne '/^$/p' + +sed -e '/test$/d' testcases +sed -e '/^echo/d' testcases +sed -e '/test/s/dangerous/PELIGROSO/' testcases +sed -ne '1,/getopt/p' ../pwd.c +sed -e '/getopt/r ../pwd.c' ../sed.c + + +# setkeycodes + +# sh - note that we cannot test the shell interactively here +sh -c "echo a b c" +sh -c ">" +sh -c "a" +sh sh.testcases + + +# sleep - can't test: produces no output + +# sort +sort tester.sh +sort -n tester.sh +sort -r tester.sh + +# stty +# swapon - won't test: dangerous +# swapoff - won't test: dangerous +# sync - can't test: no output +# syslogd - won't test: too involved + +# tail +tail tester.sh +tail -n 2 tester.sh + +# tar + +# tee +echo "please tee me!" | tee A B C ; cat A B C +echo "please tee me!" | tee A B C ; echo "tee me too!" | tee -a A B C ; cat A B C ; rm A B C + +# telnet - can't test: interactive + +# test +# tftp + +# touch +touch tmpfile1; ls tmpfile1; rm -f tmpfile1 +touch -c tmpfile1; ls tmpfile1; rm -f tmpfile1 + +# tr +# BUG: Busybox tr range handling minix style [a-z] instead of GNU # style a-z +echo "cbaab" | tr abc zyx +echo "TESTING A B C" | tr [A-Z] [a-z] +# not GNU compatible +echo fdhrnzvfu bffvsentr | tr [a-z] [n-z][a-m] +echo abc[] | tr a[b AXB +echo testing | tr -d aeiou + +# true +true ; echo $? + +# false +false ; echo $? + +# tty +# umount +# uname +# uniq +# unix2dos +# update + +# uptime +# BUG: doesn't print number of users +uptime + +# usleep +# uudecode +# uuencode +# watchdog + +# wc +wc tester.sh +wc -c tester.sh +wc -w tester.sh +wc -l tester.sh +wc -L tester.sh + +# wget + +# which +which ls + +# whoami +whoami + +# xargs +# XXX: Busygox xargs divides filenames with '\n' instead of ' ' +ls -1 ../e* | xargs +ls -1 ../e* | xargs md5sum + +# yes - can't test: interactive (needs ^C) + diff --git a/busybox/tests/tester.sh b/busybox/tests/tester.sh new file mode 100755 index 000000000..a767c6c7f --- /dev/null +++ b/busybox/tests/tester.sh @@ -0,0 +1,158 @@ +#!/bin/bash +# +# tester.sh - reads testcases from file and tests busybox applets vs GNU +# counterparts +# +# This should be run from within the tests/ directory. Before you run it, you +# should compile up a busybox that has all applets and all features turned on. + +# set up defaults (can be changed with cmd-line options) +BUSYBOX=../busybox +TESTCASES=testcases +LOGFILE=tester.log +BB_OUT=bb.out +GNU_OUT=gnu.out +SETUP="" +CLEANUP="" +KEEPTMPFILES="no" +DEBUG=2 + + +#while getopts 'p:t:l:b:g:s:c:kd:' opt +while getopts 'p:t:l:s:c:kd:' opt +do + case $opt in + p) BUSYBOX=$OPTARG; ;; + t) TESTCASES=$OPTARG; ;; + l) LOGFILE=$OPTARG; ;; +# b) BB_OUT=$OPTARG; ;; +# g) GNU_OUT=$OPTARG; ;; + s) SETUP=$OPTARG; ;; + c) CLEANUP=$OPTARG; ;; + k) KEEPTMPFILES="yes"; ;; + d) DEBUG=$OPTARG; ;; + *) + echo "usage: $0 [-ptlbgsc]" + echo " -p PATH path to busybox executable (default=$BUSYBOX)" + echo " -t FILE run testcases in FILE (default=$TESTCASES)" + echo " -l FILE log test results in FILE (default=$LOGFILE)" +# echo " -b FILE store temporary busybox output in FILE" +# echo " -g FILE store temporary GNU output in FILE" + echo " -s FILE (setup) run commands in FILE before testcases" + echo " -c FILE (cleanup) run commands in FILE after testcases" + echo " -k keep temporary output files (don't delete them)" + echo " -d NUM set level of debugging output" + echo " 0 = no output" + echo " 1 = output failures / whoops lines only" + echo " 2 = (default) output setup / cleanup msgs and testcase lines" + echo " 3+= other debug noise (internal stuff)" + exit 1 + ;; + esac +done +#shift `expr $OPTIND - 1` + + +# maybe print some debug output +if [ $DEBUG -ge 3 ] +then + echo "BUSYBOX=$BUSYBOX" + echo "TESTCASES=$TESTCASES" + echo "LOGFILE=$LOGFILE" + echo "BB_OUT=$BB_OUT" + echo "GNU_OUT=$GNU_OUT" + echo "SETUP=$SETUP" + echo "CLEANUP=$CLEANUP" + echo "DEBUG=$DEBUG" +fi + + +# do sanity checks +if [ ! -e $BUSYBOX ] +then + echo "Busybox executable: $BUSYBOX not found!" + exit 1 +fi + +if [ ! -e $TESTCASES ] +then + echo "Testcases file: $TESTCASES not found!" + exit 1 +fi + + +# do normal setup +[ -e $LOGFILE ] && rm $LOGFILE +unalias -a # gets rid of aliases that might create different output + + +# do extra setup (if any) +if [ ! -z "$SETUP" ] +then + [ $DEBUG -ge 2 ] && echo "running setup commands in $SETUP" + source $SETUP +fi + + +# go through each line in the testcase file +cat $TESTCASES | while read line +do + #echo $line + # only process non-blank lines and non-comment lines + if [ "$line" ] + then + if [ `echo "$line" | cut -c1` != "#" ] + then + + # test if the applet was compiled into busybox + # (this only tests the applet at the beginning of the line) + #applet=`echo $line | cut -d' ' -f1` + applet=`echo $line | sed 's/\(^[^ ;]*\)[ ;].*/\1/'` + $BUSYBOX 2>&1 | grep -qw $applet + if [ $? -eq 1 ] + then + echo "WHOOPS: $applet not compiled into busybox" | tee -a $LOGFILE + else + + # execute line using gnu / system programs + [ $DEBUG -ge 2 ] && echo "testing: $line" | tee -a $LOGFILE + sh -c "$line" > $GNU_OUT + + # change line to include "busybox" before every statement + line="$BUSYBOX $line" + # is this a bash-2-ism? + # line=${line//;/; $BUSYBOX } + # line=${line//|/| $BUSYBOX } + # assume $BUSYBOX has no commas + line=`echo "$line" | sed -e 's,;,; '$BUSYBOX, \ + -e 's, |, | '$BUSYBOX,` + + # execute line using busybox programs + [ $DEBUG -ge 2 ] && echo "testing: $line" | tee -a $LOGFILE + sh -c "$line" > $BB_OUT + + # see if they match + diff -q $BB_OUT $GNU_OUT > /dev/null + if [ $? -eq 1 ] + then + [ $DEBUG -ge 1 ] && echo "FAILED: $line" | tee -a $LOGFILE + diff -u $BB_OUT $GNU_OUT >> $LOGFILE + fi + fi + fi + fi +done + +[ $DEBUG -gt 0 ] && echo "Finished. Results are in $LOGFILE" + + +# do normal cleanup +[ "$KEEPTMPFILES" = "no" ] && rm -f $BB_OUT $GNU_OUT + + +# do extra cleanup (if any) +if [ ! -z "$CLEANUP" ] +then + [ $DEBUG -ge 2 ] && echo "running cleanup commands in $CLEANUP" + source $CLEANUP +fi diff --git a/busybox/tests/tst-syslogd.c b/busybox/tests/tst-syslogd.c new file mode 100644 index 000000000..bae10afdf --- /dev/null +++ b/busybox/tests/tst-syslogd.c @@ -0,0 +1,44 @@ +/* + * tst-syslogd.c - tests concurrent threads calling syslog + * + * build with: gcc -Wall tst-syslogd.c -lpthread + */ + +#include +#include +#include +#include + +void *log_func(void *arg) +{ + int i; + int thrid = (int)arg; + + openlog(NULL, LOG_PERROR | LOG_PID, LOG_USER); + for (i = 0; i < 10; i++) { + syslog(LOG_DEBUG, "thread %i iter %i\n", thrid, i); + sleep(thrid); /* this mixes things up a bit */ + } + closelog(); + + return NULL; +} + +int main(int argc, char **argv) +{ + pthread_t thr1, thr2, thr3; + int id1 = 1; + int id2 = 2; + int id3 = 3; + + pthread_create(&thr1, NULL, log_func, (void *)id1); + pthread_create(&thr2, NULL, log_func, (void *)id2); + pthread_create(&thr3, NULL, log_func, (void *)id3); + + pthread_join(thr1, NULL); + pthread_join(thr2, NULL); + pthread_join(thr3, NULL); + + return 0; +} + diff --git a/busybox/tftp.c b/busybox/tftp.c new file mode 100644 index 000000000..bb75c88ec --- /dev/null +++ b/busybox/tftp.c @@ -0,0 +1,414 @@ +/* ------------------------------------------------------------------------- */ +/* tftp.c */ +/* */ +/* A simple tftp client for busybox. */ +/* Tries to follow RFC1350. */ +/* Only "octet" mode and 512-byte data blocks are supported. */ +/* */ +/* Copyright (C) 2001 Magnus Damm */ +/* */ +/* Parts of the code based on: */ +/* */ +/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre */ +/* and Remi Lefebvre */ +/* */ +/* utftp: Copyright (C) 1999 Uwe Ohse */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +//#define BB_FEATURE_TFTP_DEBUG + +/* we don't need #ifdefs with these constants and optimization... */ + +#ifdef BB_FEATURE_TFTP_GET +#define BB_TFTP_GET (1 << 0) +#else +#define BB_TFTP_GET 0 +#endif + +#ifdef BB_FEATURE_TFTP_PUT +#define BB_TFTP_PUT (1 << 1) +#else +#define BB_TFTP_PUT 0 +#endif + +#ifdef BB_FEATURE_TFTP_DEBUG +#define BB_TFTP_DEBUG 1 +#else +#define BB_TFTP_DEBUG 0 +#endif + +#define BB_TFTP_NO_RETRIES 5 +#define BB_TFTP_TIMEOUT 5 /* seconds */ + +#define RRQ 1 /* read request */ +#define WRQ 2 /* write request */ +#define DATA 3 /* data packet */ +#define ACK 4 /* acknowledgement */ +#define ERROR 5 /* error code */ + +#define BUFSIZE (512+4) + +static const char *tftp_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" +}; + +static inline int tftp(int cmd, struct hostent *host, + char *serverfile, int localfd, int port) +{ + struct sockaddr_in sa; + int socketfd; + struct timeval tv; + fd_set rfds; + struct sockaddr_in from; + socklen_t fromlen; + char *cp; + unsigned short tmp; + int len, opcode, finished; + int timeout, block_nr; + + RESERVE_BB_BUFFER(buf, BUFSIZE); + + opcode = finished = timeout = 0; + block_nr = 1; + + if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + 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 = htons(port); + memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, + sizeof(sa.sin_addr)); + + /* build opcode */ + + if (cmd & BB_TFTP_GET) { + opcode = RRQ; + } + + if (cmd & BB_TFTP_PUT) { + opcode = WRQ; + } + + while (1) { + + + /* build packet of type "opcode" */ + + + cp = buf; + + *((unsigned short *) cp) = htons(opcode); + + cp += 2; + + /* add filename and mode */ + + if ((BB_TFTP_GET && (opcode == RRQ)) || + (BB_TFTP_PUT && (opcode == WRQ))) { + + while (cp != &buf[BUFSIZE - 1]) { + if ((*cp = *serverfile++) == '\0') + break; + cp++; + } + + if ((*cp != '\0') || (&buf[BUFSIZE - 1] - cp) < 7) { + error_msg("too long server-filename"); + break; + } + + memcpy(cp + 1, "octet", 6); + cp += 7; + } + + /* add ack and data */ + + if ((BB_TFTP_GET && (opcode == ACK)) || + (BB_TFTP_PUT && (opcode == DATA))) { + + *((unsigned short *) cp) = htons(block_nr); + + cp += 2; + + block_nr++; + + if (BB_TFTP_PUT && (opcode == DATA)) { + len = read(localfd, cp, BUFSIZE - 4); + + if (len < 0) { + perror_msg("read"); + break; + } + + if (len != (BUFSIZE - 4)) { + finished++; + } + + cp += len; + } else if (finished) { + break; + } + } + + + /* send packet */ + + + do { + + len = cp - buf; + + if (BB_TFTP_DEBUG) { + printf("sending %u bytes\n", len); + + for (cp = buf; cp < &buf[len]; cp++) + printf("%02x ", *cp); + printf("\n"); + } + + if (sendto(socketfd, buf, len, 0, + (struct sockaddr *) &sa, sizeof(sa)) < 0) { + perror_msg("send"); + len = -1; + break; + } + + + /* receive packet */ + + + memset(&from, 0, sizeof(from)); + fromlen = sizeof(from); + + tv.tv_sec = BB_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, + BUFSIZE, 0, + (struct sockaddr *) &from, &fromlen); + + if (len < 0) { + perror_msg("recvfrom"); + break; + } + + timeout = 0; + + if (sa.sin_port == htons(port)) { + sa.sin_port = from.sin_port; + break; + } + + if (sa.sin_port == from.sin_port) { + break; + } + + /* fall-through for bad packets! */ + /* discard the packet - treat as timeout */ + + case 0: + error_msg("timeout"); + + if (!timeout) { + timeout = BB_TFTP_NO_RETRIES; + } else { + timeout--; + } + + if (!timeout) { + len = -1; + error_msg("last timeout"); + } + break; + + default: + perror_msg("select"); + len = -1; + } + + } while (timeout && (len >= 0)); + + if (len < 0) { + break; + } + + /* process received packet */ + + + opcode = ntohs(*((unsigned short *) buf)); + tmp = ntohs(*((unsigned short *) &buf[2])); + + if (BB_TFTP_DEBUG) { + printf("received %d bytes: %04x %04x\n", len, opcode, tmp); + } + + if (BB_TFTP_GET && (opcode == DATA)) { + + if (tmp == block_nr) { + len = write(localfd, &buf[4], len - 4); + + if (len < 0) { + perror_msg("write"); + break; + } + + if (len != (BUFSIZE - 4)) { + finished++; + } + + opcode = ACK; + continue; + } + } + + if (BB_TFTP_PUT && (opcode == ACK)) { + + if (tmp == (block_nr - 1)) { + if (finished) { + break; + } + + opcode = DATA; + continue; + } + } + + if (opcode == ERROR) { + char *msg = NULL; + + if (buf[4] != '\0') { + msg = &buf[4]; + buf[BUFSIZE - 1] = '\0'; + } else if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) { + msg = (char *) tftp_error_msg[tmp]; + } + + if (msg) { + error_msg("server says: %s", msg); + } + + break; + } + } + + close(socketfd); + + return finished ? EXIT_SUCCESS : EXIT_FAILURE; +} + +int tftp_main(int argc, char **argv) +{ + char *cp, *s; + char *serverstr; + struct hostent *host; + char *serverfile; + char *localfile; + int cmd, flags, fd, bad; + + host = (void *) serverstr = serverfile = localfile = NULL; + flags = cmd = 0; + bad = 1; + + if (argc > 3) { + if (BB_TFTP_GET && (strcmp(argv[1], "get") == 0)) { + cmd = BB_TFTP_GET; + flags = O_WRONLY | O_CREAT; + serverstr = argv[2]; + localfile = argv[3]; + } + + if (BB_TFTP_PUT && (strcmp(argv[1], "put") == 0)) { + cmd = BB_TFTP_PUT; + flags = O_RDONLY; + localfile = argv[2]; + serverstr = argv[3]; + } + + } + + if (!(cmd & (BB_TFTP_GET | BB_TFTP_PUT))) { + show_usage(); + } + + for (cp = serverstr; *cp != '\0'; cp++) + if (*cp == ':') + break; + + if (*cp == ':') { + + serverfile = cp + 1; + + s = xstrdup(serverstr); + s[cp - serverstr] = '\0'; + + host = xgethostbyname(s); + + free(s); + } + + if (BB_TFTP_DEBUG) { + printf("using server \"%s\", serverfile \"%s\"," + "localfile \"%s\".\n", + inet_ntoa(*((struct in_addr *) host->h_addr)), + serverfile, localfile); + } + + if ((fd = open(localfile, flags, 0644)) < 0) { + perror_msg_and_die("local file"); + } + + flags = tftp(cmd, host, serverfile, fd, 69); + + close(fd); + + return flags; +} diff --git a/busybox/touch.c b/busybox/touch.c new file mode 100644 index 000000000..1718da71e --- /dev/null +++ b/busybox/touch.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini touch implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int touch_main(int argc, char **argv) +{ + int fd; + int create = TRUE; + + /* Parse options */ + while (--argc > 0 && **(++argv) == '-') { + while (*(++(*argv))) { + switch (**argv) { + case 'c': + create = FALSE; + break; + default: + show_usage(); + } + } + } + + if (argc < 1) { + show_usage(); + } + + while (argc > 0) { + fd = open(*argv, (create == FALSE) ? O_RDWR : O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd < 0) { + if (create == FALSE && errno == ENOENT) + return EXIT_SUCCESS; + else { + perror_msg_and_die("%s", *argv); + } + } + close(fd); + if (utime(*argv, NULL)) { + perror_msg_and_die("%s", *argv); + } + argc--; + argv++; + } + + return EXIT_SUCCESS; +} diff --git a/busybox/tr.c b/busybox/tr.c new file mode 100644 index 000000000..5b7b8d091 --- /dev/null +++ b/busybox/tr.c @@ -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 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 +#include +#include +#include +#include +#include "busybox.h" + +/* This must be a #define, since when DODEBUG 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() +{ + 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) + error_msg("%s", 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) + error_msg_and_die("%s", 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++ = 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_BB_BUFFER(output, BUFSIZ); + RESERVE_BB_BUFFER(input, BUFSIZ); + RESERVE_BB_UBUFFER(vector, ASCII+1); + RESERVE_BB_BUFFER(invec, ASCII+1); + RESERVE_BB_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: + 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') + 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[(int)input[i]] = TRUE; + for (i = 0; i < output_length; i++) + outvec[(int)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/traceroute.c b/busybox/traceroute.c new file mode 100644 index 000000000..a3abd0a00 --- /dev/null +++ b/busybox/traceroute.c @@ -0,0 +1,652 @@ +/*- + * 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 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * 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 BB_FEATURE_TRACEROUTE_VERBOSE +//#define BB_FEATURE_TRACEROUTE_VERBOSE +#undef BB_FEATURE_TRACEROUTE_SO_DEBUG /* not in documentation man */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + /* It turns out that libc5 doesn't have proper icmp support + * built into it header files, so we have to supplement it */ +#if __GNU_LIBRARY__ < 5 +static const int ICMP_MINLEN = 8; /* abs minimum */ + +struct icmp_ra_addr +{ + u_int32_t ira_addr; + u_int32_t ira_preference; +}; + + +struct icmp +{ + u_int8_t icmp_type; /* type of message, see below */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement checksum of struct */ + union + { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* gateway address */ + struct ih_idseq /* echo datagram */ + { + u_int16_t icd_id; + u_int16_t icd_seq; + } ih_idseq; + u_int32_t ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu + { + u_int16_t ipm_void; + u_int16_t ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv + { + u_int8_t irt_num_addrs; + u_int8_t irt_wpa; + u_int16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union + { + struct + { + u_int32_t its_otime; + u_int32_t its_rtime; + u_int32_t its_ttime; + } id_ts; + struct + { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + struct icmp_ra_addr id_radv; + u_int32_t id_mask; + u_int8_t id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +}; + +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_UNREACH 3 /* dest unreachable, codes: */ +#define ICMP_TIMXCEED 11 /* time exceeded, code: */ +#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ +#define ICMP_UNREACH_NET 0 /* bad net */ +#define ICMP_UNREACH_HOST 1 /* bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ +#define ICMP_UNREACH_PORT 3 /* bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ +#endif + + +#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 BB_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; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + const char *ina; + + if (first && !nflag) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = strchr(domain, '.'))) + (void) strcpy(domain, cp + 1); + else + domain[0] = 0; + } + cp = 0; + if (!nflag && from->sin_addr.s_addr != INADDR_ANY) { + hp = gethostbyaddr((char *)&(from->sin_addr), sizeof (from->sin_addr), AF_INET); + if (hp) { + if ((cp = strchr(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = (char *)hp->h_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 BB_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 BB_FEATURE_TRACEROUTE_VERBOSE +/* + * Convert an ICMP "type" field to a printable string. + */ +static inline const char * +pr_type(t) + 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 BB_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 BB_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 BB_TRACEROUTE +main(argc, argv) +#else +traceroute_main(argc, argv) +#endif + int argc; + char *argv[]; +{ + 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 BB_FEATURE_TRACEROUTE_SO_DEBUG + options |= SO_DEBUG; +#endif + break; + case 'm': + max_ttl = atoi(optarg); + if (max_ttl <= 1) + error_msg_and_die("max ttl must be >1."); + break; + case 'n': + nflag++; + break; + case 'p': + port = atoi(optarg); + if (port < 1) + error_msg_and_die("port must be >0."); + break; + case 'q': + nprobes = atoi(optarg); + if (nprobes < 1) + 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) + error_msg_and_die("tos must be 0 to 255."); + break; + case 'v': +#ifdef BB_FEATURE_TRACEROUTE_VERBOSE + verbose++; +#endif + break; + case 'w': + waittime = atoi(optarg); + if (waittime <= 1) + error_msg_and_die("wait must be >1 sec."); + break; + default: + show_usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + 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)) + 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) + perror_msg_and_die(can_not_create_raw_socket); + + s = create_icmp_socket(); + +#ifdef BB_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) + perror_msg_and_die("SO_SNDBUF"); +#endif SO_SNDBUF +#ifdef IP_HDRINCL + if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on, + sizeof(on)) < 0) + perror_msg_and_die("IP_HDRINCL"); +#endif IP_HDRINCL +#ifdef BB_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) + 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) + perror_msg_and_die("bind"); +#endif IP_HDRINCL + } + + 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) + exit(0); + } + + return 0; +} diff --git a/busybox/true_false.c b/busybox/true_false.c new file mode 100644 index 000000000..76183431c --- /dev/null +++ b/busybox/true_false.c @@ -0,0 +1,39 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini true/false implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ + +#include +#include "busybox.h" + + +extern int true_main(int argc, char **argv) +{ + return EXIT_SUCCESS; +} + +extern int false_main(int argc, char **argv) +{ + return EXIT_FAILURE; +} diff --git a/busybox/tty.c b/busybox/tty.c new file mode 100644 index 000000000..4510c2996 --- /dev/null +++ b/busybox/tty.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tty implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +extern int tty_main(int argc, char **argv) +{ + char *tty; + + if (argc > 1) { + if (argv[1][0] != '-' || argv[1][1] != 's') + show_usage(); + } else { + tty = ttyname(0); + if (tty) + puts(tty); + else + puts("not a tty"); + } + return(isatty(0) ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/busybox/umount.c b/busybox/umount.c new file mode 100644 index 000000000..74638d21c --- /dev/null +++ b/busybox/umount.c @@ -0,0 +1,298 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini umount implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#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 BB_FEATURE_MOUNT_FORCE +static int doForce = FALSE; +#endif +#if defined BB_FEATURE_MOUNT_LOOP +static int freeLoop = TRUE; +#endif +#if defined BB_FEATURE_MTAB_SUPPORT +static int useMtab = TRUE; +#endif +static int umountAll = FALSE; +static int doRemount = FALSE; +extern const char mtab_file[]; /* Defined in utility.c */ + + + +/* 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(mtab_file, "r")) == NULL) { + error_msg("Cannot open %s", 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 BB_FEATURE_MTAB_SUPPORT + 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(cur->device); + } +#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 BB_FEATURE_CLEAN_UP +static void mtab_free(void) +{ + struct _mtab_entry_t *this, *next; + + this = mtab_cache; + while (this) { + next = this->next; + if (this->device) + free(this->device); + if (this->mountpt) + 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 BB_FEATURE_MOUNT_LOOP + if (freeLoop == TRUE && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9)) + /* this was a loop device, delete it */ + del_loop(blockDevice); +#endif +#if defined BB_FEATURE_MOUNT_FORCE + if (status != 0 && doForce == TRUE) { + status = umount2(blockDevice, MNT_FORCE); + if (status != 0) { + error_msg_and_die("forced umount of %s failed!", blockDevice); + } + } +#endif + if (status != 0 && doRemount == TRUE && errno == EBUSY) { + status = mount(blockDevice, name, NULL, + MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); + if (status == 0) { + error_msg("%s busy - remounted read-only", blockDevice); + } else { + error_msg("Cannot remount %s read-only", blockDevice); + } + } + if (status == 0) { +#if defined BB_FEATURE_MTAB_SUPPORT + if (useMtab == TRUE) + 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) { + 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]; + + if (argc < 2) { + show_usage(); + } +#ifdef BB_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 BB_FEATURE_MOUNT_LOOP + case 'l': + freeLoop = FALSE; + break; +#endif +#ifdef BB_FEATURE_MTAB_SUPPORT + case 'n': + useMtab = FALSE; + break; +#endif +#ifdef BB_FEATURE_MOUNT_FORCE + case 'f': + doForce = TRUE; + break; +#endif + case 'r': + doRemount = TRUE; + break; + case 'v': + break; /* ignore -v */ + default: + show_usage(); + } + } + + mtab_read(); + if (umountAll == TRUE) { + if (umount_all() == TRUE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } + if (realpath(*argv, path) == NULL) + perror_msg_and_die("%s", path); + if (do_umount(path) == TRUE) + return EXIT_SUCCESS; + perror_msg_and_die("%s", *argv); +} + diff --git a/busybox/uname.c b/busybox/uname.c new file mode 100644 index 000000000..f7e2291a8 --- /dev/null +++ b/busybox/uname.c @@ -0,0 +1,156 @@ +/* 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. */ + +/* 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 */ + +/* Busyboxed by Erik Andersen */ + +#include +#include +#include +#include +#include + +#if defined (HAVE_SYSINFO) && defined (HAVE_SYS_SYSTEMINFO_H) +# include +#endif +#include "busybox.h" + +static void print_element(unsigned int mask, char *element); + +/* Values that are bitwise or'd into `toprint'. */ +/* Operating system name. */ +static const int PRINT_SYSNAME = 1; + +/* Node name on a communications network. */ +static const int PRINT_NODENAME = 2; + +/* Operating system release. */ +static const int PRINT_RELEASE = 4; + +/* Operating system version. */ +static const int PRINT_VERSION = 8; + +/* Machine hardware name. */ +static const int PRINT_MACHINE = 16; + + /* Host processor type. */ +static const int PRINT_PROCESSOR = 32; + +/* Mask indicating which elements of the name to print. */ +static unsigned char toprint; + + +int uname_main(int argc, char **argv) +{ + struct utsname name; + char processor[256]; + +#if defined(__sparc__) && defined(__linux__) + char *fake_sparc = getenv("FAKE_SPARC"); +#endif + + toprint = 0; + + /* Parse any options */ + //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv); + while (--argc > 0 && **(++argv) == '-') { + while (*(++(*argv))) { + switch (**argv) { + case 's': + toprint |= PRINT_SYSNAME; + break; + case 'n': + toprint |= PRINT_NODENAME; + break; + case 'r': + toprint |= PRINT_RELEASE; + break; + case 'v': + toprint |= PRINT_VERSION; + break; + case 'm': + toprint |= PRINT_MACHINE; + break; + case 'p': + toprint |= PRINT_PROCESSOR; + break; + case 'a': + toprint = (PRINT_SYSNAME | PRINT_NODENAME | PRINT_RELEASE | + PRINT_PROCESSOR | PRINT_VERSION | + PRINT_MACHINE); + break; + default: + show_usage(); + } + } + } + + if (toprint == 0) + toprint = PRINT_SYSNAME; + + if (uname(&name) == -1) + perror_msg("cannot get system name"); + +#if defined (HAVE_SYSINFO) && defined (SI_ARCHITECTURE) + if (sysinfo(SI_ARCHITECTURE, processor, sizeof(processor)) == -1) + perror_msg("cannot get processor type"); +} + +#else + strcpy(processor, "unknown"); +#endif + +#if defined(__sparc__) && defined(__linux__) + if (fake_sparc != NULL + && (fake_sparc[0] == 'y' + || fake_sparc[0] == 'Y')) strcpy(name.machine, "sparc"); +#endif + + print_element(PRINT_SYSNAME, name.sysname); + print_element(PRINT_NODENAME, name.nodename); + print_element(PRINT_RELEASE, name.release); + print_element(PRINT_VERSION, name.version); + print_element(PRINT_MACHINE, name.machine); + print_element(PRINT_PROCESSOR, processor); + + return EXIT_SUCCESS; +} + +/* If the name element set in MASK is selected for printing in `toprint', + print ELEMENT; then print a space unless it is the last element to + be printed, in which case print a newline. */ + +static void print_element(unsigned int mask, char *element) +{ + if (toprint & mask) { + toprint &= ~mask; + printf("%s%c", element, toprint ? ' ' : '\n'); + } +} diff --git a/busybox/uniq.c b/busybox/uniq.c new file mode 100644 index 000000000..53e3c64f2 --- /dev/null +++ b/busybox/uniq.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini uniq implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by John Beppu + * Rewritten by 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 +#include +#include +#include +#include +#include "busybox.h" + +static int print_count; +static int print_uniq = 1; +static int print_duplicates = 1; + +static void print_line(char *line, int count, FILE *fp) +{ + if ((print_duplicates && count > 1) || (print_uniq && count == 1)) { + if (print_count) + fprintf(fp, "%7d\t%s", count, line); + else + fputs(line, fp); + } +} + +int uniq_main(int argc, char **argv) +{ + FILE *in = stdin, *out = stdout; + char *lastline = NULL, *input; + int opt, count = 0; + + /* parse argv[] */ + while ((opt = getopt(argc, argv, "cdu")) > 0) { + switch (opt) { + case 'c': + print_count = 1; + break; + case 'd': + print_duplicates = 1; + print_uniq = 0; + break; + case 'u': + print_duplicates = 0; + print_uniq = 1; + break; + } + } + + if (argv[optind] != NULL) { + in = xfopen(argv[optind], "r"); + if (argv[optind+1] != NULL) + out = xfopen(argv[optind+1], "w"); + } + + while ((input = get_line_from_file(in)) != NULL) { + if (lastline == NULL || strcmp(input, lastline) != 0) { + print_line(lastline, count, out); + free(lastline); + lastline = input; + count = 0; + } + count++; + } + print_line(lastline, count, out); + free(lastline); + + return EXIT_SUCCESS; +} diff --git a/busybox/update.c b/busybox/update.c new file mode 100644 index 000000000..27a04ddee --- /dev/null +++ b/busybox/update.c @@ -0,0 +1,112 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini update implementation for busybox; much pasted from update-2.11 + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * Copyright (c) 1996, 1997, 1999 Torsten Poulin. + * Copyright (c) 2000 by Karl M. Hegbloom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Note: This program is only necessary if you are running a 2.0.x (or + * earlier) kernel. 2.2.x and higher flush filesystem buffers automatically. + */ + +#include +#include +#include /* for getopt() */ +#include + +#if __GNU_LIBRARY__ > 5 + #include +#else + extern int bdflush (int func, long int data); +#endif + +#include "busybox.h" + +static unsigned int sync_duration = 30; +static unsigned int flush_duration = 5; +static int use_sync = 0; + +extern int update_main(int argc, char **argv) +{ + int pid; + int opt; + + while ((opt = getopt(argc, argv, "Ss:f:")) > 0) { + switch (opt) { + case 'S': + use_sync = 1; + break; + case 's': + sync_duration = atoi(optarg); + break; + case 'f': + flush_duration = atoi(optarg); + break; + default: + show_usage(); + } + } + + if (daemon(0, 1) < 0) + perror_msg_and_die("daemon"); + +#ifdef OPEN_MAX + for (pid = 0; pid < OPEN_MAX; pid++) close(pid); +#else + /* glibc 2.1.92 requires using sysconf(_SC_OPEN_MAX) */ + for (pid = 0; pid < sysconf(_SC_OPEN_MAX); pid++) close(pid); +#endif + + /* This is no longer necessary since 1.3.5x, but it will harmlessly + * exit if that is the case. + */ + + /* set the program name that will show up in a 'ps' listing */ + argv[0] = "bdflush (update)"; + argv[1] = NULL; + argv[2] = NULL; + for (;;) { + if (use_sync) { + sleep(sync_duration); + sync(); + } else { + sleep(flush_duration); + if (bdflush(1, 0) < 0) { + openlog("update", LOG_CONS, LOG_DAEMON); + syslog(LOG_INFO, + "This kernel does not need update(8). Exiting."); + closelog(); + return EXIT_SUCCESS; + } + } + } + + return EXIT_SUCCESS; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/uptime.c b/busybox/uptime.c new file mode 100644 index 000000000..6758d959e --- /dev/null +++ b/busybox/uptime.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini uptime implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* 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 BB_FEATURE_UTMP_SUPPORT #define + */ + +/* getopt not needed */ + + +#include +#include +#include +#include +#include "busybox.h" + +static const int FSHIFT = 16; /* nr of bits of precision */ +#define FIXED_1 (1<> 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(¤t_secs); + current_time = localtime(¤t_secs); + + sysinfo(&info); + + printf(" %2d:%02d%s up ", + current_time->tm_hour%12 ? current_time->tm_hour%12 : 12, + current_time->tm_min, current_time->tm_hour > 11 ? "pm" : "am"); + 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/usage.c b/busybox/usage.c new file mode 100644 index 000000000..dfea1f96b --- /dev/null +++ b/busybox/usage.c @@ -0,0 +1,10 @@ +#include "busybox.h" + +const char usage_messages[] = + +#define MAKE_USAGE +#include "usage.h" + +#include "applets.h" + +; diff --git a/busybox/usage.h b/busybox/usage.h new file mode 100644 index 000000000..13759d23f --- /dev/null +++ b/busybox/usage.h @@ -0,0 +1,1829 @@ +#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\n" + +#define ar_trivial_usage \ + "-[ov][ptx] 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\n" + +#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 cat_trivial_usage \ + "[FILE]..." +#define cat_full_usage \ + "Concatenates FILE(s) and prints them to stdout." +#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 \ + "FILE1 [FILE2]" +#define cmp_full_usage \ + "\t-s\tquiet mode - do not print\n" \ + "Compare files." + +#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-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" \ + "\tu\t\tunconditional overwrite\t" \ + "\tF\t\tinput from file\t" + +#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" + +#define date_trivial_usage \ + "[OPTION]... [+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\tdisplay time described by STRING, not `now'\n" \ + "\t-s\t\tSets time described by STRING\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: +, -, /, *, and, or, not, eor.\n" \ + "i.e., 'dc 2 2 add' -> 4, and 'dc 8 8 \\* 2 2 + /' -> 16" +#define dc_example_usage \ + "$ dc 2 2 +\n" \ + "4\n" \ + "$ dc 8 8 \* 2 2 + /\n" \ + "16\n" \ + "$ dc 0 1 and\n" \ + "0\n" \ + "$ dc 0 1 or\n" \ + "1\n" \ + "$ echo 72 9 div 8 mul | dc\n" \ + "64\n" + +#define dd_trivial_usage \ + "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \ + "\t [seek=N] [conv=notrunc|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=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" + + +#ifdef BB_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 \ + "-i package_file\n" + "[-CPru] package_name" +#define dpkg_full_usage \ + "\t-i\tInstall the package\n" \ + "\t-C\tConfigure an unpackaged package\n" \ + "\t-P\tPurge all files of a package\n" \ + "\t-r\tRemove all but the configuration files for a package\n" \ + "\t-u\tUnpack a package, but dont configure it\n" + +#define dpkg_deb_trivial_usage \ + "[-cefItxX] 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-I\tDisplay the control filenamed [argument]\n" \ + "\t-t\tExtract filesystem tree to stdout in tar format\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" + +#define du_trivial_usage \ + "[-ls" USAGE_HUMAN_READABLE("hm") USAGE_NOT_HUMAN_READABLE("") "k] [FILE]..." +#define du_full_usage \ + "Summarizes disk space used for each FILE and/or directory.\n" \ + "Disk space is printed in units of 1024 bytes.\n\n" \ + "Options:\n" \ + "\t-l\tcount sizes many times if hard linked\n" \ + "\t-s\tdisplay only a total for each argument" \ + 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 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 dutmp_trivial_usage \ + "[FILE]" +#define dutmp_full_usage \ + "Dump utmp file format (pipe delimited) from FILE\n" \ + "or stdin to stdout. (i.e., 'dutmp /var/run/utmp')" +#define dutmp_example_usage \ + "$ dutmp /var/run/utmp\n" \ + "8|7||si|||0|0|0|955637625|760097|0\n" \ + "2|0|~|~~|reboot||0|0|0|955637625|782235|0\n" \ + "1|20020|~|~~|runlevel||0|0|0|955637625|800089|0\n" \ + "8|125||l4|||0|0|0|955637629|998367|0\n" \ + "6|245|tty1|1|LOGIN||0|0|0|955637630|998974|0\n" \ + "6|246|tty2|2|LOGIN||0|0|0|955637630|999498|0\n" \ + "7|336|pts/0|vt00andersen|andersen|:0.0|0|0|0|955637763|0|0\n" + +#define echo_trivial_usage \ + "[-neE] [ARG ...]" +#define echo_full_usage \ + "Prints the specified ARGs to stdout\n\n" \ + "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" \ + "$ 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\n" + +#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" + +#ifdef BB_FEATURE_FIND_TYPE + #define USAGE_FIND_TYPE(a) a +#else + #define USAGE_FIND_TYPE(a) +#endif +#ifdef BB_FEATURE_FIND_PERM + #define USAGE_FIND_PERM(a) a +#else + #define USAGE_FIND_PERM(a) +#endif +#ifdef BB_FEATURE_FIND_MTIME + #define USAGE_FIND_MTIME(a) a +#else + #define USAGE_FIND_MTIME(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") +#define find_example_usage \ + "$ find / -name /etc/passwd\n" \ + "/etc/passwd\n" + +#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 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, --unqote 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 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-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 with maximum compression.\n" \ + "When FILE is '-', reads standard input. Implies -c.\n\n" \ + "Options:\n" \ + "\t-c\tWrite output to standard output instead of FILE.gz\n" \ + "\t-d\tdecompress" +#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 \ + "" +#define halt_full_usage \ + "Halt the system." + +#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" +#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 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\t\tShort\n" \ + "\t-i\t\tAddresses for the hostname\n" \ + "\t-d\t\tDNS domain name\n" \ + "\t-F, --file FILE\tUse the contents of FILE to specify the hostname" +#define hostname_example_usage \ + "$ hostname\n" \ + "sage \n" + +#define id_trivial_usage \ + "[OPTIONS]... [USERNAME]" +#define id_full_usage \ + "Print information for USERNAME or the current user\n\n" \ + "Options:\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 (with for -ug)\n" \ + "\t-r\tprints the real user ID instead of the effective ID (with -ug)" +#define id_example_usage \ + "$ id\n" \ + "uid=1000(andersen) gid=1000(andersen)\n" + +#ifdef BB_FEATURE_IFCONFIG_SLIP + #define USAGE_SIOCSKEEPALIVE(a) a +#else + #define USAGE_SIOCSKEEPALIVE(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + #define USAGE_IFCONFIG_MII(a) a +#else + #define USAGE_IFCONFIG_MII(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_HW + #define USAGE_IFCONFIG_HW(a) a +#else + #define USAGE_IFCONFIG_HW(a) +#endif +#ifdef BB_FEATURE_IFCONFIG_STATUS + #define USAGE_IFCONFIG_OPT_A(a) a +#else + #define USAGE_IFCONFIG_OPT_A(a) +#endif + +#define ifconfig_trivial_usage \ + USAGE_IFCONFIG_OPT_A("[-a]") " [
]" +#define ifconfig_full_usage \ + "configure a network interface\n\n" \ + "Options:\n" \ + "\t[[-]broadcast [
]] [[-]pointopoint [
]]\n" \ + "\t[netmask
] [dstaddr
]\n" \ + USAGE_SIOCSKEEPALIVE("\t[outfill ] [keepalive ]\n") \ + "\t" USAGE_IFCONFIG_HW("[hw ether
] ") \ + "[metric ] [mtu ]\n" \ + "\t[[-]trailers] [[-]arp] [[-]allmulti]\n" \ + "\t[multicast] [[-]promisc] [txqueuelen ] [[-]dynamic]\n" \ + USAGE_IFCONFIG_MII("\t[mem_start ] [io_addr ] [irq ]\n") \ + "\t[up|down] ..." + +#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" \ +"\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" \ +" :::\n" \ +"\n" \ +" : \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" \ +" : \n" \ +"\n" \ +" The runlevels field is completely ignored.\n" \ +"\n" \ +" : \n" \ +"\n" \ +" Valid actions include: sysinit, respawn, askfirst, wait, \n" \ +" once, 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. 'ctrlaltdel'\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 reverse order from how they appear in /etc/inittab.\n" \ +"\n" \ +" : \n" \ +"\n" \ +" Specifies the process to be executed and it's 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 tty5\n" \ +" tty5::respawn:/sbin/getty 38400 tty6\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 before rebooting\n" \ +" ::ctrlaltdel:/sbin/reboot\n" \ +" ::shutdown:/bin/umount -a -r\n" \ +" ::shutdown:/sbin/swapoff -a\n" + +#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-L\tLock to prevent simultaneous loads of a module\n" \ + "\t-x\tdo not export externs" + +#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 \ + "[-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." +#define killall_example_usage \ + "$ killall apache\n" + +#define klogd_trivial_usage \ + "-n" +#define klogd_full_usage \ + "Kernel logger.\n"\ + "Options:\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 loadacm_trivial_usage \ + "< mapfile" +#define loadacm_full_usage \ + "Loads an acm from standard input." +#define loadacm_example_usage \ + "$ loadacm < /etc/i18n/acmname\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\tLog using the specified tag (defaults to user name).\n" \ + "\t-p\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 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 \ + "" + +#define logread_full_usage \ + "Shows the messages from syslogd (using circular buffer)." + +#ifdef BB_FEATURE_LS_TIMESTAMPS + #define USAGE_LS_TIMESTAMPS(a) a +#else + #define USAGE_LS_TIMESTAMPS(a) +#endif +#ifdef BB_FEATURE_LS_FILETYPES + #define USAGE_LS_FILETYPES(a) a +#else + #define USAGE_LS_FILETYPES(a) +#endif +#ifdef BB_FEATURE_LS_FOLLOWLINKS + #define USAGE_LS_FOLLOWLINKS(a) a +#else + #define USAGE_LS_FOLLOWLINKS(a) +#endif +#ifdef BB_FEATURE_LS_RECURSIVE + #define USAGE_LS_RECURSIVE(a) a +#else + #define USAGE_LS_RECURSIVE(a) +#endif +#ifdef BB_FEATURE_LS_SORTFILES + #define USAGE_LS_SORTFILES(a) a +#else + #define USAGE_LS_SORTFILES(a) +#endif +#ifdef BB_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] [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" \ + "\t-k\tprint sizes in kilobytes(default)") USAGE_NOT_HUMAN_READABLE( \ + "\t-k\tprint sizes in kilobytes(compatibility)") + +#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" + +#define md5sum_trivial_usage \ + "[OPTION] [FILE]...\n" \ + "or: md5sum [OPTION] -c [FILE]" +#define md5sum_full_usage \ + "Print or check MD5 checksums.\n\n" \ + "Options:\n" \ + "With no FILE, or when FILE is -, read standard input.\n\n" \ + "\t-b\tread files in binary mode\n" \ + "\t-c\tcheck MD5 sums against given list\n" \ + "\t-t\tread files in text mode (default)\n" \ + "\t-g\tread a string\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 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 \ + "[-q] 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)." +#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 \ + "[FILE ...]" +#define modprobe_full_usage \ + "Used for hight level module loading and unloading." +#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 BB_FEATURE_MOUNT_LOOP + #define USAGE_MOUNT_LOOP(a) a +#else + #define USAGE_MOUNT_LOOP(a) +#endif +#ifdef BB_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\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 rew rewoffline seek setblk setdensity\n" \ + "setpart tell unload unlock weof wset" + +#define mv_trivial_usage \ + "SOURCE DEST\n" \ + "or: mv SOURCE... DIRECTORY" +#define mv_full_usage \ + "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY." +#define mv_example_usage \ + "$ mv /tmp/foo /bin/bar\n" + +#define nc_trivial_usage \ + "[IP] [port]" +#define nc_full_usage \ + "Netcat opens a pipe to IP:port" +#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 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 pidof_trivial_usage \ + "process-name [process-name ...]" +#define pidof_full_usage \ + "Lists the PIDs of all processes with names that match the names on the command line" +#define pidof_example_usage \ + "$ pidof init\n" \ + "1\n" + +#ifndef BB_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" + +#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 \ + "" +#define poweroff_full_usage \ + "Halt the system and request that the kernel shut off the power." + +#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" + +#define ps_trivial_usage \ + "" +#define ps_full_usage \ + "Report process status\n" \ + "\nThis version of ps accepts no options." +#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 \ + "[OPTION] 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 \ + "Read a symbolic link." + +#define reboot_trivial_usage \ + "" +#define reboot_full_usage \ + "Reboot the system." + +#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" \ + "\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\tTry to remove all unused kernel modules." +#define rmmod_example_usage \ + "$ rmmod tulip\n" + +#define route_trivial_usage \ + "[{add|del|flush}]" +#define route_full_usage \ + "Edit the kernel's routing tables" + +#define rpm2cpio_trivial_usage \ + "package.rpm" +#define rpm2cpio_full_usage \ + "Outputs a cpio archive of the rpm file." + +#define rpmunpack_trivial_usage \ + "< package.rpm | gunzip | cpio -idmuv" +#define rpmunpack_full_usage \ + "Extracts an rpm archive." + +#define sed_trivial_usage \ + "[-nef] pattern [files...]" +#define sed_full_usage \ + "Options:\n" \ + "\t-n\t\tsuppress automatic printing of pattern space\n" \ + "\t-e script\tadd the script to the commands to be executed\n" \ + "\t-f scriptfile\tadd the contents of script-file to the commands to be executed\n" \ + "\n" \ + "If no -e or -f is given, the first non-option argument is taken as the\n" \ + "sed script to interpret. All remaining arguments are names of input\n" \ + "files; if no input files are specified, then the standard input is read." +#define sed_example_usage \ + "$ echo "foo" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \ + "bar\n" + +#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 \ + "lash: 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 sleep_trivial_usage \ + "N" +#define sleep_full_usage \ + "Pause for N seconds." +#define sleep_example_usage \ + "$ sleep 2\n" \ + "[2 second delay results]\n" + + +#ifdef BB_FEATURE_SORT_UNIQUE + #define USAGE_SORT_UNIQUE(a) a +#else + #define USAGE_SORT_UNIQUE(a) +#endif +#ifdef BB_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 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 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 BB_FEATURE_REMOTE_LOG + #define USAGE_REMOTE_LOG(a) a +#else + #define USAGE_REMOTE_LOG(a) +#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 NUM\t\tInterval between MARK lines (default=20min, 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)" \ + 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)") +#define syslogd_example_usage \ + "$ syslogd -R masterlog:514\n" \ + "$ syslogd -R 192.168.1.1:601\n" + + +#ifndef BB_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 BB_FEATURE_TAR_CREATE + #define USAGE_TAR_CREATE(a) a +#else + #define USAGE_TAR_CREATE(a) +#endif +#ifdef BB_FEATURE_TAR_EXCLUDE + #define USAGE_TAR_EXCLUDE(a) a +#else + #define USAGE_TAR_EXCLUDE(a) +#endif +#define tar_trivial_usage \ + "-[" USAGE_TAR_CREATE("c") "xtvO] " \ + USAGE_TAR_EXCLUDE("[--exclude FILE] [-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" \ + "\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" +#define tee_example_usage \ + "$ echo "Hello" | tee /tmp/foo\n" \ + "$ cat /tmp/foo\n" \ + "Hello\n" + +#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." + +#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 BB_FEATURE_TFTP_GET + #define USAGE_TFTP_GET(a) a +#else + #define USAGE_TFTP_GET(a) +#endif +#ifdef BB_FEATURE_TFTP_PUT + #define USAGE_TFTP_PUT(a) a +#else + #define USAGE_TFTP_PUT(a) +#endif + +#define tftp_trivial_usage \ + "command SOURCE DEST" +#define tftp_full_usage \ + "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ + "Commands:\n" \ + USAGE_TFTP_GET( \ + "\tget\tGet file from server SOURCE and store to local DEST.\n" \ + ) \ + USAGE_TFTP_PUT( \ + "\tput\tPut local file SOURCE to server DEST.\n" \ + ) \ + "\nWhen naming a server, use the syntax \"server:file\"." + +#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\ + [-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" + +#ifdef BB_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.2.15pre13 #5 Tue Mar 14 16:03:50 MST 2000 i686 unknown\n" + +#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" +#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 update_trivial_usage \ + "[options]" +#define update_full_usage \ + "Periodically flushes filesystem buffers.\n\n" \ + "Options:\n" \ + "\t-S\tforce use of sync(2) instead of flushing\n" \ + "\t-s SECS\tcall sync this often (default 30)\n" \ + "\t-f SECS\tflush some buffers this often (default 5)" + +#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" \ + "\n" \ + "$ uudecode busybox busybox > busybox.uu\n" \ + "$\n" + +#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 watchdog_trivial_usage \ + "DEV" +#define watchdog_full_usage \ + "Periodically write to watchdog device DEV" + +#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[--header 'header: value'] [-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)" + +#define which_trivial_usage \ + "[COMMAND ...]" +#define which_full_usage \ + "Locates a COMMAND." +#define which_example_usage \ + "$ which login\n" \ + "/bin/login\n" + +#define whoami_trivial_usage \ + "" +#define whoami_full_usage \ + "Prints the user name associated with the current effective user id." + +#define xargs_trivial_usage \ + "[COMMAND] [ARGS...]" +#define xargs_full_usage \ + "Executes COMMAND on every item given by standard input." +#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." diff --git a/busybox/usleep.c b/busybox/usleep.c new file mode 100644 index 000000000..6023bf430 --- /dev/null +++ b/busybox/usleep.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini usleep implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "busybox.h" + +extern int usleep_main(int argc, char **argv) +{ + if ((argc < 2) || (**(argv + 1) == '-')) { + show_usage(); + } + + usleep(atoi(*(++argv))); /* return void */ + return EXIT_SUCCESS; +} diff --git a/busybox/util-linux/dmesg.c b/busybox/util-linux/dmesg.c new file mode 100644 index 000000000..73de6d1ae --- /dev/null +++ b/busybox/util-linux/dmesg.c @@ -0,0 +1,95 @@ +/* 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 + * - added Native Language Support + * + * from util-linux -- adapted for busybox by + * Erik Andersen . I ripped out Native Language + * Support, replaced getopt, added some gotos for redundant stuff. + */ + +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +# ifdef __alpha__ +# define klogctl syslog +# endif +#else +# include +#endif + +#include "busybox.h" + +int dmesg_main(int argc, char **argv) +{ + char *buf; + int c; + int bufsize = 8196; + int i; + int n; + int level = 0; + int lastc; + int cmd = 3; + + while ((c = getopt(argc, argv, "cn:s:")) != EOF) { + switch (c) { + case 'c': + cmd = 4; + break; + case 'n': + cmd = 8; + if (optarg == NULL) + show_usage(); + level = atoi(optarg); + break; + case 's': + if (optarg == NULL) + show_usage(); + bufsize = atoi(optarg); + break; + default: + show_usage(); + } + } + + if (optind < argc) { + show_usage(); + } + + if (cmd == 8) { + if (klogctl(cmd, NULL, level) < 0) + perror_msg_and_die("klogctl"); + return EXIT_SUCCESS; + } + + if (bufsize < 4096) + bufsize = 4096; + buf = (char *) xmalloc(bufsize); + if ((n = klogctl(cmd, buf, bufsize)) < 0) + perror_msg_and_die("klogctl"); + + 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'); + return EXIT_SUCCESS; +} diff --git a/busybox/util-linux/fbset.c b/busybox/util-linux/fbset.c new file mode 100644 index 000000000..41c7f9796 --- /dev/null +++ b/busybox/util-linux/fbset.c @@ -0,0 +1,424 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fbset implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define DEFAULTFBDEV "/dev/fb0" +#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 BB_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; +#define __u32 unsigned int +struct fb_bitfield { + __u32 offset; /* beginning of bitfield */ + __u32 length; /* length of bitfield */ + __u32 msb_right; /* != 0 : Most significant bit is */ + /* right */ +}; +struct fb_var_screeninfo { + __u32 xres; /* visible resolution */ + __u32 yres; + __u32 xres_virtual; /* virtual resolution */ + __u32 yres_virtual; + __u32 xoffset; /* offset from virtual to visible */ + __u32 yoffset; /* resolution */ + + __u32 bits_per_pixel; /* guess what */ + __u32 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 */ + + __u32 nonstd; /* != 0 Non standard pixel format */ + + __u32 activate; /* see FB_ACTIVATE_* */ + + __u32 height; /* height of picture in mm */ + __u32 width; /* width of picture in mm */ + + __u32 accel_flags; /* acceleration flags (hints) */ + + /* Timing: All values in pixclocks, except pixclock (of course) */ + __u32 pixclock; /* pixel clock in ps (pico seconds) */ + __u32 left_margin; /* time from sync to picture */ + __u32 right_margin; /* time from picture to sync */ + __u32 upper_margin; /* time from sync to picture */ + __u32 lower_margin; + __u32 hsync_len; /* length of horizontal sync */ + __u32 vsync_len; /* length of vertical sync */ + __u32 sync; /* see FB_SYNC_* */ + __u32 vmode; /* see FB_VMODE_* */ + __u32 reserved[6]; /* Reserved for future compatibility */ +}; + + +static struct cmdoptions_t { + char *name; + unsigned char param_count; + 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 BB_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 BB_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 BB_FEATURE_FBSET_READMODE + FILE *f; + char buf[256]; + char *p = buf; + + f = 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 + error_msg( "mode reading not compiled in"); +#endif + return 0; +} + +static 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 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", v->xres, v->yres, (int) (vrate + 0.5)); +#ifdef BB_FEATURE_FBSET_FANCY + printf("\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n", drate / 1e6, + hrate / 1e3, vrate); +#endif + printf("\tgeometry %u %u %u %u %u\n", v->xres, v->yres, + v->xres_virtual, v->yres_virtual, v->bits_per_pixel); + printf("\ttimings %u %u %u %u %u %u %u\n", v->pixclock, v->left_margin, + v->right_margin, v->upper_margin, v->lower_margin, v->hsync_len, + v->vsync_len); + printf("\taccel %s\n", (v->accel_flags > 0 ? "true" : "false")); + printf("\trgba %u/%u,%u/%u,%u/%u,%u/%u\n", v->red.length, + v->red.offset, v->green.length, v->green.offset, v->blue.length, + v->blue.offset, v->transp.length, v->transp.offset); + printf("endmode\n\n"); +} + +#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) + 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 BB_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; +#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 { + show_usage(); + } + } + } + + if ((fh = open(fbdev, O_RDONLY)) < 0) + perror_msg_and_die("fbset(open)"); + if (ioctl(fh, FBIOGET_VSCREENINFO, &var)) + perror_msg_and_die("fbset(ioctl)"); + if (g_options & OPT_READMODE) { + if (!readmode(&var, modefile, mode)) { + error_msg("Unknown video mode `%s'", mode); + return EXIT_FAILURE; + } + } + + setmode(&var, &varset); + if (g_options & OPT_CHANGE) + if (ioctl(fh, FBIOPUT_VSCREENINFO, &var)) + 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 index 000000000..28f5cb68a --- /dev/null +++ b/busybox/util-linux/fdflush.c @@ -0,0 +1,47 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fdflush implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +/* From */ +#define FDFLUSH _IO(2,0x4b) + +extern int fdflush_main(int argc, char **argv) +{ + int fd; + + if (argc <= 1 || **(++argv) == '-') + show_usage(); + + if ((fd = open(*argv, 0)) < 0) + perror_msg_and_die("%s", *argv); + + if (ioctl(fd, FDFLUSH, 0)) + perror_msg_and_die("%s", *argv); + + return EXIT_SUCCESS; +} diff --git a/busybox/util-linux/freeramdisk.c b/busybox/util-linux/freeramdisk.c new file mode 100644 index 000000000..cf25fae6a --- /dev/null +++ b/busybox/util-linux/freeramdisk.c @@ -0,0 +1,65 @@ +/* vi: set sw=4 ts=4: */ +/* + * freeramdisk implementation for busybox + * + * Copyright (C) 2000 and written by Emanuele Caratti + * Adjusted a bit by 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 +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +/* From linux/fs.h */ +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ + +extern int +freeramdisk_main(int argc, char **argv) +{ + int f; + + if (argc != 2 || *argv[1] == '-') { + show_usage(); + } + + if ((f = open(argv[1], O_RDWR)) == -1) { + perror_msg_and_die("cannot open %s", argv[1]); + } + if (ioctl(f, BLKFLSBUF) < 0) { + perror_msg_and_die("failed ioctl on %s", argv[1]); + } + /* Don't bother closing. Exit does + * that, so we can save a few bytes */ + /* close(f); */ + 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 index 000000000..a2421fc34 --- /dev/null +++ b/busybox/util-linux/fsck_minix.c @@ -0,0 +1,1484 @@ +/* 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 rudimetary 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 systemindependent. + * (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 . + * + * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by + * Andreas Schwab. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz + * - 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). + * + * Usuage: 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + + typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + + +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 { + u16 i_mode; + u16 i_uid; + u32 i_size; + u32 i_time; + u8 i_gid; + u8 i_nlinks; + u16 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 { + u16 i_mode; + u16 i_nlinks; + u16 i_uid; + u16 i_gid; + u32 i_size; + u32 i_atime; + u32 i_mtime; + u32 i_ctime; + u32 i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + u16 s_ninodes; + u16 s_nzones; + u16 s_imap_blocks; + u16 s_zmap_blocks; + u16 s_firstdatazone; + u16 s_log_zone_size; + u32 s_max_size; + u16 s_magic; + u16 s_state; + u32 s_zones; +}; + +struct minix_dir_entry { + u16 inode; + char name[0]; +}; + +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<> 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) +{ + 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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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 BB_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++) { + if (name_list[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 BB_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 BB_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) + 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: + show_usage(); + } + } + if (!device_name) + 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", 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 BB_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 index 000000000..95ecba6e6 --- /dev/null +++ b/busybox/util-linux/getopt.c @@ -0,0 +1,402 @@ +/* + * getopt.c - Enhanced implementation of BSD getopt(1) + * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 Mikiewicz + * ) + * Ported to Busybox - Alfred M. Szmidt + * Removed --version/-V and --help/-h in + * Removed prase_error(), using error_msg() from Busybox instead + * Replaced our_malloc with xmalloc and our_realloc with xrealloc + * + */ + +#include +#include +#include +#include +#include +#include + +#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; + + if (BUFFER != NULL) + free(BUFFER); + + if (!quote) { /* Just copy arg */ + BUFFER=xmalloc(strlen(arg)+1); + + strcpy(BUFFER,arg); + return BUFFER; + } + + /* Each character in arg may take upto 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 \ */ + *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) +{ + char *tmp; + 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; + tmp = xmalloc(strlen(name)+1); + strcpy(tmp,name); + long_options[long_options_nr-1].name=tmp; + } + 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) + 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 + error_msg("unknown shell after -s or --shell argument"); +} + + +/* Exit codes: + * 0) No errors, succesful 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[]) +{ + char *optstr=NULL; + 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"); + exit(0); + } else + 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]; + exit(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': + if (optstr) + free(optstr); + optstr=xmalloc(strlen(optarg)+1); + strcpy(optstr,optarg); + break; + case 'l': + add_long_options(optarg); + break; + case 'n': + if (name) + free(name); + name=xmalloc(strlen(optarg)+1); + strcpy(name,optarg); + break; + case 'q': + quiet_errors=1; + break; + case 'Q': + quiet_output=1; + break; + case 's': + set_shell(optarg); + break; + case 'T': + exit(4); + case 'u': + quote=0; + break; + default: + show_usage(); + } + + if (!optstr) { + if (optind >= argc) + error_msg_and_die("missing optstring argument"); + else { + optstr=xmalloc(strlen(argv[optind])+1); + strcpy(optstr,argv[optind]); + optind++; + } + } + if (name) + argv[optind-1]=name; + else + argv[optind-1]=argv[0]; + exit(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/mkfs_minix.c b/busybox/util-linux/mkfs_minix.c new file mode 100644 index 000000000..70374eae9 --- /dev/null +++ b/busybox/util-linux/mkfs_minix.c @@ -0,0 +1,853 @@ +/* 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 readablility 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 -- + * removed getopt based parser and added a hand rolled one. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + + +#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 { + u16 i_mode; + u16 i_uid; + u32 i_size; + u32 i_time; + u8 i_gid; + u8 i_nlinks; + u16 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 { + u16 i_mode; + u16 i_nlinks; + u16 i_uid; + u16 i_gid; + u32 i_size; + u32 i_atime; + u32 i_mtime; + u32 i_ctime; + u32 i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + u16 s_ninodes; + u16 s_nzones; + u16 s_imap_blocks; + u16 s_zmap_blocks; + u16 s_firstdatazone; + u16 s_log_zone_size; + u32 s_max_size; + u16 s_magic; + u16 s_state; + u32 s_zones; +}; + +struct minix_dir_entry { + u16 inode; + char name[0]; +}; + +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<> 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 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; + + 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 int count_blocks(int fd) +{ + int high, low; + + low = 0; + for (high = 1; valid_offset(fd, high); high *= 2) + low = high; + while (low < high - 1) { + const int mid = (low + high) / 2; + + if (valid_offset(fd, mid)) + low = mid; + else + high = mid; + } + valid_offset(fd, 0); + return (low + 1); +} + +static int get_size(const char *file) +{ + int fd; + long size; + + if ((fd = open(file, O_RDWR)) < 0) + 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 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)) + error_msg_and_die("seek to boot block failed in write_tables"); + if (512 != write(DEV, boot_block_buffer, 512)) + error_msg_and_die("unable to clear boot sector"); + if (BLOCK_SIZE != lseek(DEV, BLOCK_SIZE, SEEK_SET)) + error_msg_and_die("seek failed in write_tables"); + if (BLOCK_SIZE != write(DEV, super_block_buffer, BLOCK_SIZE)) + error_msg_and_die("unable to write super-block"); + if (IMAPS * BLOCK_SIZE != write(DEV, inode_map, IMAPS * BLOCK_SIZE)) + error_msg_and_die("unable to write inode map"); + if (ZMAPS * BLOCK_SIZE != write(DEV, zone_map, ZMAPS * BLOCK_SIZE)) + error_msg_and_die("unable to write zone map"); + if (INODE_BUFFER_SIZE != write(DEV, inode_buffer, INODE_BUFFER_SIZE)) + 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)) + error_msg_and_die("seek failed in write_block"); + if (BLOCK_SIZE != write(DEV, buffer, BLOCK_SIZE)) + 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) + 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) + error_msg_and_die("not enough good blocks"); + good_blocks_table[used_good_blocks] = blk; + used_good_blocks++; + return blk; +} + +static 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 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; + } + } + 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 BB_FEATURE_MINIX2 +static 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 */ + 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 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 BB_FEATURE_MINIX2 +static 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 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; + ZONES = 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 BB_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) { + 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", 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\n", MAXSIZE); +} + +/* + * Perform a test of a block; return the number of + * blocks readable/writeable. + */ +static 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) { + 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) + 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) + 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(filename) +char *filename; + +{ + FILE *listfile; + unsigned long blockno; + + listfile = 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) + error_msg_and_die("bad inode size"); +#ifdef BB_FEATURE_MINIX2 + if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) + 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) + 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) + show_usage(); + if (i == 14) + magic = MINIX_SUPER_MAGIC; + else if (i == 30) + magic = MINIX_SUPER_MAGIC2; + else + show_usage(); + namelen = i; + dirsize = i + 2; + stopIt=TRUE; + break; + } + case 'v': +#ifdef BB_FEATURE_MINIX2 + version2 = 1; +#else + error_msg("%s: not compiled with minix v2 support", + device_name); + exit(-1); +#endif + break; + case '-': + case 'h': + default: +goodbye: + 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) { + show_usage(); + } +#ifdef BB_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) + error_msg_and_die("unable to open %s", device_name); + if (fstat(DEV, &statbuf) < 0) + 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) + 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 BB_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 index 000000000..f72c7009a --- /dev/null +++ b/busybox/util-linux/mkswap.c @@ -0,0 +1,422 @@ +/* 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 + * - added Native Language Support + * + * from util-linux -- adapted for busybox by + * Erik Andersen . I ripped out Native Language + * Support, made some stuff smaller, and fitted for life in busybox. + * + */ + +#include +#include +#include +#include +#include +#include /* for _IO */ +#include +#include /* 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 void init_signature_page() +{ + pagesize = getpagesize(); + +#ifdef PAGE_SIZE + if (pagesize != PAGE_SIZE) + 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 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 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 void page_bad(int page) +{ + if (version == 0) + bit_test_and_clear(signature_page, page); + else { + if (badpages == MAX_BADPAGES) + 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) + 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) + 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) + 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: + show_usage(); + } + } + } + if (!device_name) { + error_msg("error: Nowhere to set up swap on?"); + show_usage(); + } + sz = get_size(device_name); + if (!PAGES) { + PAGES = sz; + } else if (PAGES > sz && !force) { + error_msg("error: size %ld is larger than device size %d", + PAGES * (pagesize / 1024), sz * (pagesize / 1024)); + return EXIT_FAILURE; + } + + if (version == -1) { + if (PAGES <= V0_MAX_PAGES) + version = 0; + else if (get_kernel_revision() < MAKE_VERSION(2, 1, 117)) + version = 0; + else if (pagesize < 2048) + version = 0; + else + version = 1; + } + if (version != 0 && version != 1) { + error_msg("error: unknown version %d", version); + show_usage(); + } + if (PAGES < 10) { + error_msg("error: swap area needs to be at least %ldkB", + (long) (10 * pagesize / 1024)); + 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; + error_msg("warning: truncating swap area to %ldkB", + PAGES * pagesize / 1024); + } + + DEV = open(device_name, O_RDWR); + if (DEV < 0 || fstat(DEV, &statbuf) < 0) + 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) + 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) + 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) { + 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)) + 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) + 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) + error_msg_and_die("unable to rewind swap-device"); + if (write(DEV, (char *) signature_page + offset, pagesize - offset) + != pagesize - offset) + 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)) + 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 index 000000000..780cddf66 --- /dev/null +++ b/busybox/util-linux/more.c @@ -0,0 +1,217 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini more implementation for busybox + * + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * Latest version blended together by Erik Andersen , + * based on the original more implementation by Bruce, and code from the + * Debian boot-floppies team. + * + * Termios corrects by Vladimir Oleynik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include "busybox.h" + +static FILE *cin; + +#ifdef BB_FEATURE_USE_TERMIOS +#include +#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(fileno(cin), &initial_settings); +} + +static void gotsig(int sig) +{ + putchar('\n'); + exit(EXIT_FAILURE); +} +#endif /* BB_FEATURE_USE_TERMIOS */ + + +static int terminal_width = 79; /* not 80 in case terminal has linefold bug */ +static int terminal_height = 24; + + +extern int more_main(int argc, char **argv) +{ + int c, lines, input = 0; + int please_display_more_prompt = -1; + struct stat st; + FILE *file; + int len, page_height; + +#if defined BB_FEATURE_AUTOWIDTH && defined BB_FEATURE_USE_TERMIOS + struct winsize win = { 0, 0, 0, 0 }; +#endif + + argc--; + argv++; + + + /* not use inputing from terminal if usage: more > outfile */ + if(isatty(fileno(stdout))) { + cin = fopen(CURRENT_TTY, "r"); + if (!cin) + cin = xfopen(CONSOLE_DEV, "r"); + please_display_more_prompt = 0; +#ifdef BB_FEATURE_USE_TERMIOS + getTermSettings(fileno(cin), &initial_settings); + new_settings = initial_settings; + new_settings.c_lflag &= ~ICANON; + new_settings.c_lflag &= ~ECHO; +#ifndef linux + /* Hmm, in linux c_cc[] not parsed if set ~ICANON */ + new_settings.c_cc[VMIN] = 1; + new_settings.c_cc[VTIME] = 0; +#endif + setTermSettings(fileno(cin), &new_settings); + atexit(set_tty_to_initial_mode); + (void) signal(SIGINT, gotsig); + (void) signal(SIGQUIT, gotsig); + (void) signal(SIGTERM, gotsig); +#endif + } + + do { + if (argc == 0) { + file = stdin; + } else + file = wfopen(*argv, "r"); + if(file==0) + goto loop; + + fstat(fileno(file), &st); + + if(please_display_more_prompt>0) + please_display_more_prompt = 0; + +#if defined BB_FEATURE_AUTOWIDTH && defined BB_FEATURE_USE_TERMIOS + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (win.ws_row > 4) + terminal_height = win.ws_row - 2; + if (win.ws_col > 0) + terminal_width = win.ws_col - 1; +#endif + len=0; + lines = 0; + page_height = terminal_height; + while ((c = getc(file)) != EOF) { + + if (please_display_more_prompt>0) { + len = printf("--More-- "); + if (file != stdin) { +#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 BB_FEATURE_USE_TERMIOS + printf("\033[A"); /* up cursor */ +#endif + /* Erase the "More" message */ + putc('\r', stdout); + while (--len >= 0) + putc(' ', stdout); + putc('\r', stdout); + fflush(stdout); + len=0; + lines = 0; + page_height = terminal_height; + please_display_more_prompt = 0; + + 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') + if(please_display_more_prompt==0) + 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) { + if(please_display_more_prompt==0) + 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 index 000000000..eb6091f30 --- /dev/null +++ b/busybox/util-linux/mount.c @@ -0,0 +1,472 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mount implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + * 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 , . + * 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 suppport is back - andersee + * + * 2000-01-12 Ben Collins , Borrowed utils-linux's + * mount to add loop support. + * + * 2000-04-30 Dave Cinege + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#if defined BB_FEATURE_USE_DEVPS_PATCH +# include /* For Erik's nifty devmtab device driver */ +#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 */ +}; + + +#if defined BB_FEATURE_MOUNT_LOOP +#include +#include +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); + +extern const char mtab_file[]; /* Defined in utility.c */ + +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}, + {"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}, + {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 BB_FEATURE_MOUNT_LOOP + char *lofile = NULL; +#endif + + if (fakeIt == FALSE) + { +#if defined BB_FEATURE_MOUNT_LOOP + if (use_loop==TRUE) { + int loro = flags & MS_RDONLY; + + lofile = specialfile; + + specialfile = find_unused_loop_device(); + if (specialfile == NULL) { + error_msg_and_die("Could not find a spare loop device"); + } + if (set_loop(specialfile, lofile, 0, &loro)) { + error_msg_and_die("Could not setup loop device"); + } + if (!(flags & MS_RDONLY) && loro) { /* loop is ro, but wanted rw */ + 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) { + 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 BB_FEATURE_MTAB_SUPPORT + if (useMtab == TRUE) { + 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 BB_FEATURE_MOUNT_LOOP + if (lofile != NULL) { + del_loop(specialfile); + } +#endif + + if (errno == EPERM) { + error_msg_and_die("permission denied. Are you root?"); + } + + return (FALSE); +} + + + +/* 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 BB_FEATURE_MOUNT_LOOP + if (gotone == FALSE && !strcasecmp("loop", options)) { /* loop device support */ + use_loop = TRUE; + gotone = TRUE; + } +#endif + if (*strflags && strflags != '\0' && gotone == FALSE) { + char *temp = strflags; + + temp += strlen(strflags); + *temp++ = ','; + *temp++ = '\0'; + } + if (gotone == FALSE) + strcat(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) { + static const char *noauto_array[] = { "tmpfs", "shm", "proc", "ramfs", "devpts", "devfs", 0 }; + const char **noauto_fstype; + const int num_of_filesystems = sysfs(3, 0, 0); + char buf[255]; + int i=0; + + filesystemType=buf; + + while(i < num_of_filesystems) { + sysfs(2, i++, filesystemType); + for (noauto_fstype = noauto_array; *noauto_fstype; noauto_fstype++) { + if (!strcmp(filesystemType, *noauto_fstype)) { + break; + } + } + if (!*noauto_fstype) { + status = do_mount(blockDevice, directory, filesystemType, + flags | MS_MGC_VAL, string_flags, + useMtab, fakeIt, mtab_opts, mount_all); + if (status == TRUE) + break; + } + } + } else { + status = do_mount(blockDevice, directory, filesystemType, + flags | MS_MGC_VAL, string_flags, useMtab, + fakeIt, mtab_opts, mount_all); + } + + if (status == FALSE) { + if (whineOnErrors == TRUE) { + perror_msg("Mounting %s on %s failed", blockDevice, directory); + } + return (FALSE); + } + return (TRUE); +} + +void show_mounts() +{ +#if defined BB_FEATURE_USE_DEVPS_PATCH + int fd, i, numfilesystems; + char device[] = "/dev/mtab"; + struct k_mntent *mntentlist; + + /* open device */ + fd = open(device, O_RDONLY); + if (fd < 0) + perror_msg_and_die("open failed for `%s'", device); + + /* How many mounted filesystems? We need to know to + * allocate enough space for later... */ + numfilesystems = ioctl (fd, DEVMTAB_COUNT_MOUNTS); + if (numfilesystems<0) + perror_msg_and_die( "\nDEVMTAB_COUNT_MOUNTS"); + mntentlist = (struct k_mntent *) xcalloc ( numfilesystems, sizeof(struct k_mntent)); + + /* Grab the list of mounted filesystems */ + if (ioctl (fd, DEVMTAB_GET_MOUNTS, mntentlist)<0) + perror_msg_and_die( "\nDEVMTAB_GET_MOUNTS"); + + for( i = 0 ; i < numfilesystems ; i++) { + printf( "%s %s %s %s %d %d\n", mntentlist[i].mnt_fsname, + mntentlist[i].mnt_dir, mntentlist[i].mnt_type, + mntentlist[i].mnt_opts, mntentlist[i].mnt_freq, + mntentlist[i].mnt_passno); + } +#ifdef BB_FEATURE_CLEAN_UP + /* Don't bother to close files or free memory. Exit + * does that automagically, so we can save a few bytes */ + free( mntentlist); + close(fd); +#endif + exit(EXIT_SUCCESS); +#else + FILE *mountTable = setmntent(mtab_file, "r"); + + if (mountTable) { + struct mntent *m; + + while ((m = getmntent(mountTable)) != 0) { + char *blockDevice = m->mnt_fsname; + if (strcmp(blockDevice, "/dev/root") == 0) { + blockDevice = find_real_root_device_name(blockDevice); + } + printf("%s on %s type %s (%s)\n", blockDevice, m->mnt_dir, + m->mnt_type, m->mnt_opts); +#ifdef BB_FEATURE_CLEAN_UP + if(blockDevice != m->mnt_fsname) + free(blockDevice); +#endif + } + endmntent(mountTable); + } else { + perror_msg_and_die("%s", mtab_file); + } + exit(EXIT_SUCCESS); +#endif +} + +extern int mount_main(int argc, char **argv) +{ + struct stat statbuf; + char string_flags_buf[1024] = ""; + char *string_flags = string_flags_buf; + char *extra_opts = string_flags_buf; + int flags = 0; + char *filesystemType = "auto"; + char *device = xmalloc(PATH_MAX); + char *directory = xmalloc(PATH_MAX); + int all = FALSE; + int fakeIt = FALSE; + int useMtab = TRUE; + int rc = EXIT_FAILURE; + int fstabmount = FALSE; + 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; + break; + case 'w': + flags &= ~MS_RDONLY; + break; + case 'a': + all = TRUE; + break; + case 'f': + fakeIt = TRUE; + break; +#ifdef BB_FEATURE_MTAB_SUPPORT + case 'n': + useMtab = FALSE; + break; +#endif + case 'v': + break; /* ignore -v */ + } + } + + if (!all && optind == argc) + show_mounts(); + + if (optind < argc) { + /* if device is a filename get its real path */ + if (stat(argv[optind], &statbuf) == 0) { + realpath(argv[optind], device); + } else { + safe_strncpy(device, argv[optind], PATH_MAX); + } + } + + if (optind + 1 < argc) { + if (realpath(argv[optind + 1], directory) == NULL) { + perror_msg_and_die("%s", directory); + } + } + + if (all == TRUE || optind + 1 == argc) { + struct mntent *m = NULL; + FILE *f = setmntent("/etc/fstab", "r"); + fstabmount = TRUE; + + if (f == NULL) + perror_msg_and_die( "\nCannot read /etc/fstab"); + + while ((m = getmntent(f)) != NULL) { + if (all == FALSE && optind + 1 == argc && ( + (strcmp(device, m->mnt_fsname) != 0) && + (strcmp(device, m->mnt_dir) != 0) ) ) { + continue; + } + + if (all == TRUE && ( // 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 or nfs, then mount it + (strstr(m->mnt_type, "nfs")) ) ) { + continue; + } + + if (all == TRUE || flags == 0) { // Allow single mount to override fstab flags + flags = 0; + *string_flags = '\0'; + parse_mount_options(m->mnt_opts, &flags, string_flags); + } + + strcpy(device, m->mnt_fsname); + strcpy(directory, m->mnt_dir); + filesystemType = strdup(m->mnt_type); +singlemount: + string_flags = strdup(string_flags); + rc = EXIT_SUCCESS; +#ifdef BB_NFSMOUNT + if (strchr(device, ':') != NULL) + filesystemType = "nfs"; + if (strcmp(filesystemType, "nfs") == 0) { + if (nfsmount (device, directory, &flags, &extra_opts, + &string_flags, 1)) { + 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 == FALSE) + break; + } + if (fstabmount == TRUE) + endmntent(f); + + if (all == FALSE && fstabmount == TRUE && 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 index 000000000..cd722acc3 --- /dev/null +++ b/busybox/util-linux/nfsmount.c @@ -0,0 +1,977 @@ +/* vi: set sw=4 ts=4: */ +/* + * nfsmount.c -- Linux NFS mount + * Copyright (C) 1993 Rick Sladkey + * + * This program is free software; you can redistribute 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 : + * Omit the call to connect() for Linux version 1.3.11 or later. + * + * Wed Oct 1 23:55:28 1997: Dick Streefland + * Implemented the "bg", "fg" and "retry" mount options for NFS. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz + * - 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#undef TRUE +#undef FALSE +#include +#include +#include +#include /* For the kernels nfs stuff */ +#include "nfsmount.h" + +#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; + static 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)) { + 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'; + error_msg("warning: multiple hostnames not supported"); + } + } else { + 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) { + herror_msg("%s", hostname); + goto fail; + } else { + if (hp->h_length > sizeof(struct in_addr)) { + 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)) { + error_msg("excessively long option argument"); + goto fail; + } + sprintf(new_opts, "%s%saddr=%s", + old_opts, *old_opts ? "," : "", s); + *extra_opts = 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=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) { + error_msg("NFSv%d not supported!", nfsvers); + return 0; + } + if (mountvers > MAX_NFSPROT) { + 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; + *mount_opts = (char *) &data; + + if (*flags & MS_REMOUNT) + return 0; + + /* + * 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) { + herror_msg("%s", mounthost); + goto fail; + } else { + if (hp->h_length > sizeof(struct in_addr)) { + 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 = 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) { + 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) { + 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); + 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 : change errno: + * "after #include 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, (u_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, (u_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 index 000000000..b3d5a51e6 --- /dev/null +++ b/busybox/util-linux/nfsmount.h @@ -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 + + +#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 +#define MOUNTPORT 635 +#define MNTPATHLEN 1024 +#define MNTNAMLEN 255 +#define FHSIZE 32 +#define FHSIZE3 64 + +typedef char fhandle[FHSIZE]; + +typedef struct { + u_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 { + u_int fhs_status; + union { + fhandle fhs_fhandle; + } fhstatus_u; +}; +typedef struct fhstatus fhstatus; + +struct mountres3_ok { + fhandle3 fhandle; + struct { + u_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 index 000000000..ba26b9c58 --- /dev/null +++ b/busybox/util-linux/pivot_root.c @@ -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 +#include +#include +#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) + show_usage(); + + if (pivot_root(argv[1],argv[2]) < 0) + 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 index 000000000..50be4de8c --- /dev/null +++ b/busybox/util-linux/rdate.c @@ -0,0 +1,116 @@ +/* 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +static const int RFC_868_BIAS = 2208988800UL; + +static time_t askremotedate(const char *host) +{ + struct hostent *h; + struct sockaddr_in s_in; + struct servent *tserv; + unsigned long int nett, localt; + int fd; + + h = xgethostbyname(host); /* get the IP addr */ + + if ((tserv = getservbyname("time", "tcp")) == NULL) /* find port # */ + perror_msg_and_die("time"); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) /* get net connection */ + perror_msg_and_die("socket"); + + memcpy(&s_in.sin_addr, h->h_addr, sizeof(s_in.sin_addr)); + s_in.sin_port= tserv->s_port; + s_in.sin_family = AF_INET; + + if (connect(fd, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) /* connect to time server */ + perror_msg_and_die("%s", host); + + if (read(fd, (void *)&nett, 4) != 4) /* read time from server */ + 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: + show_usage(); + } + } + + if (optind == argc) + show_usage(); + + remote_time = askremotedate(argv[optind]); + + if (setdate) { + if (stime(&remote_time) < 0) + 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 index 000000000..ce0e2c6cc --- /dev/null +++ b/busybox/util-linux/swaponoff.c @@ -0,0 +1,115 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini swapon/swapoff implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +/* libc5 doesn't have sys/swap.h, define these here. */ +extern int swapon (__const char *__path, int __flags); +extern int swapoff (__const char *__path); +#else +#include +#endif + +#include "busybox.h" + +static int whichApp; + +static const int SWAPON_APP = 1; +static const int SWAPOFF_APP = 2; + + +static void swap_enable_disable(char *device) +{ + int status; + + if (whichApp == SWAPON_APP) + status = swapon(device, 0); + else + status = swapoff(device); + + if (status != 0) + perror_msg_and_die(applet_name); +} + +static void do_em_all() +{ + struct mntent *m; + FILE *f = setmntent("/etc/fstab", "r"); + + if (f == NULL) + perror_msg_and_die("/etc/fstab"); + while ((m = getmntent(f)) != NULL) { + if (strcmp(m->mnt_type, MNTTYPE_SWAP)==0) { + swap_enable_disable(m->mnt_fsname); + } + } + endmntent(f); + exit(EXIT_SUCCESS); +} + + +extern int swap_on_off_main(int argc, char **argv) +{ + if (strcmp(applet_name, "swapon") == 0) { + whichApp = SWAPON_APP; + } else { + 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) + error_msg_and_die("/etc/fstab file missing"); + } + do_em_all(); + break; + default: + goto usage_and_exit; + } + } + swap_enable_disable(*argv); + return EXIT_SUCCESS; + + usage_and_exit: + show_usage(); +} diff --git a/busybox/util-linux/umount.c b/busybox/util-linux/umount.c new file mode 100644 index 000000000..74638d21c --- /dev/null +++ b/busybox/util-linux/umount.c @@ -0,0 +1,298 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini umount implementation for busybox + * + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 +#include +#include +#include +#include +#include +#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 BB_FEATURE_MOUNT_FORCE +static int doForce = FALSE; +#endif +#if defined BB_FEATURE_MOUNT_LOOP +static int freeLoop = TRUE; +#endif +#if defined BB_FEATURE_MTAB_SUPPORT +static int useMtab = TRUE; +#endif +static int umountAll = FALSE; +static int doRemount = FALSE; +extern const char mtab_file[]; /* Defined in utility.c */ + + + +/* 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(mtab_file, "r")) == NULL) { + error_msg("Cannot open %s", 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 BB_FEATURE_MTAB_SUPPORT + 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(cur->device); + } +#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 BB_FEATURE_CLEAN_UP +static void mtab_free(void) +{ + struct _mtab_entry_t *this, *next; + + this = mtab_cache; + while (this) { + next = this->next; + if (this->device) + free(this->device); + if (this->mountpt) + 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 BB_FEATURE_MOUNT_LOOP + if (freeLoop == TRUE && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9)) + /* this was a loop device, delete it */ + del_loop(blockDevice); +#endif +#if defined BB_FEATURE_MOUNT_FORCE + if (status != 0 && doForce == TRUE) { + status = umount2(blockDevice, MNT_FORCE); + if (status != 0) { + error_msg_and_die("forced umount of %s failed!", blockDevice); + } + } +#endif + if (status != 0 && doRemount == TRUE && errno == EBUSY) { + status = mount(blockDevice, name, NULL, + MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); + if (status == 0) { + error_msg("%s busy - remounted read-only", blockDevice); + } else { + error_msg("Cannot remount %s read-only", blockDevice); + } + } + if (status == 0) { +#if defined BB_FEATURE_MTAB_SUPPORT + if (useMtab == TRUE) + 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) { + 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]; + + if (argc < 2) { + show_usage(); + } +#ifdef BB_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 BB_FEATURE_MOUNT_LOOP + case 'l': + freeLoop = FALSE; + break; +#endif +#ifdef BB_FEATURE_MTAB_SUPPORT + case 'n': + useMtab = FALSE; + break; +#endif +#ifdef BB_FEATURE_MOUNT_FORCE + case 'f': + doForce = TRUE; + break; +#endif + case 'r': + doRemount = TRUE; + break; + case 'v': + break; /* ignore -v */ + default: + show_usage(); + } + } + + mtab_read(); + if (umountAll == TRUE) { + if (umount_all() == TRUE) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } + if (realpath(*argv, path) == NULL) + perror_msg_and_die("%s", path); + if (do_umount(path) == TRUE) + return EXIT_SUCCESS; + perror_msg_and_die("%s", *argv); +} + diff --git a/busybox/uudecode.c b/busybox/uudecode.c new file mode 100644 index 000000000..a4059ddfe --- /dev/null +++ b/busybox/uudecode.c @@ -0,0 +1,353 @@ +/* uudecode.c -- uudecode utility. + * Copyright (C) 1994, 1995 Free Software Foundation, Inc. + * + * This product is free software; you can redistribute 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 product is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this product; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Reworked to GNU style by Ian Lance Taylor, ian@airs.com, August 93. + * + * Original copyright notice is retained at the end of this file. + */ + + + +#include +#include +#include +#include +#include +#include "busybox.h" +#include "pwd_grp/pwd.h" +#include "pwd_grp/grp.h" + +/*struct passwd *getpwnam();*/ + +/* Single character decode. */ +#define DEC(Char) (((Char) - ' ') & 077) + +static int read_stduu (const char *inname) +{ + char buf[2 * BUFSIZ]; + + while (1) { + int n; + char *p; + + if (fgets (buf, sizeof(buf), stdin) == NULL) { + error_msg("%s: Short file", inname); + return FALSE; + } + p = buf; + + /* N is used to avoid writing out all the characters at the end of + the file. */ + n = DEC (*p); + if (n <= 0) + break; + for (++p; n > 0; p += 4, n -= 3) { + char ch; + + if (n >= 3) { + ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4; + putchar (ch); + ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2; + putchar (ch); + ch = DEC (p[2]) << 6 | DEC (p[3]); + putchar (ch); + } else { + if (n >= 1) { + ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4; + putchar (ch); + } + if (n >= 2) { + ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2; + putchar (ch); + } + } + } + } + + if (fgets (buf, sizeof(buf), stdin) == NULL + || strcmp (buf, "end\n")) { + error_msg("%s: No `end' line", inname); + return FALSE; + } + + return TRUE; +} + +static int read_base64 (const char *inname) +{ + static const char b64_tab[256] = { + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*000-007*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*010-017*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*020-027*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*030-037*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*040-047*/ + '\177', '\177', '\177', '\76', '\177', '\177', '\177', '\77', /*050-057*/ + '\64', '\65', '\66', '\67', '\70', '\71', '\72', '\73', /*060-067*/ + '\74', '\75', '\177', '\177', '\177', '\100', '\177', '\177', /*070-077*/ + '\177', '\0', '\1', '\2', '\3', '\4', '\5', '\6', /*100-107*/ + '\7', '\10', '\11', '\12', '\13', '\14', '\15', '\16', /*110-117*/ + '\17', '\20', '\21', '\22', '\23', '\24', '\25', '\26', /*120-127*/ + '\27', '\30', '\31', '\177', '\177', '\177', '\177', '\177', /*130-137*/ + '\177', '\32', '\33', '\34', '\35', '\36', '\37', '\40', /*140-147*/ + '\41', '\42', '\43', '\44', '\45', '\46', '\47', '\50', /*150-157*/ + '\51', '\52', '\53', '\54', '\55', '\56', '\57', '\60', /*160-167*/ + '\61', '\62', '\63', '\177', '\177', '\177', '\177', '\177', /*170-177*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*200-207*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*210-217*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*220-227*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*230-237*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*240-247*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*250-257*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*260-267*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*270-277*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*300-307*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*310-317*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*320-327*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*330-337*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*340-347*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*350-357*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*360-367*/ + '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /*370-377*/ + }; + unsigned char buf[2 * BUFSIZ]; + + while (1) { + int last_data = 0; + unsigned char *p; + + if (fgets (buf, sizeof(buf), stdin) == NULL) { + error_msg("%s: Short file", inname); + return FALSE; + } + p = buf; + + if (memcmp (buf, "====", 4) == 0) + break; + if (last_data != 0) { + error_msg("%s: data following `=' padding character", inname); + return FALSE; + } + + /* The following implementation of the base64 decoding might look + a bit clumsy but I only try to follow the POSIX standard: + ``All line breaks or other characters not found in the table + [with base64 characters] shall be ignored by decoding + software.'' */ + while (*p != '\n') { + char c1, c2, c3; + + while ((b64_tab[*p] & '\100') != 0) + if (*p == '\n' || *p++ == '=') + break; + if (*p == '\n') + /* This leaves the loop. */ + continue; + c1 = b64_tab[*p++]; + + while ((b64_tab[*p] & '\100') != 0) + if (*p == '\n' || *p++ == '=') { + error_msg("%s: illegal line", inname); + return FALSE; + } + c2 = b64_tab[*p++]; + + while (b64_tab[*p] == '\177') + if (*p++ == '\n') { + error_msg("%s: illegal line", inname); + return FALSE; + } + if (*p == '=') { + putchar (c1 << 2 | c2 >> 4); + last_data = 1; + break; + } + c3 = b64_tab[*p++]; + + while (b64_tab[*p] == '\177') + if (*p++ == '\n') { + error_msg("%s: illegal line", inname); + return FALSE; + } + putchar (c1 << 2 | c2 >> 4); + putchar (c2 << 4 | c3 >> 2); + if (*p == '=') { + last_data = 1; + break; + } + else + putchar (c3 << 6 | b64_tab[*p++]); + } + } + + return TRUE; +} + +static int decode (const char *inname, + const char *forced_outname) +{ + struct passwd *pw; + register char *p; + int mode; + char buf[2 * BUFSIZ]; + char *outname; + int do_base64 = 0; + int res; + int dofre; + + /* Search for header line. */ + + while (1) { + if (fgets (buf, sizeof (buf), stdin) == NULL) { + error_msg("%s: No `begin' line", inname); + return FALSE; + } + + if (strncmp (buf, "begin", 5) == 0) { + if (sscanf (buf, "begin-base64 %o %s", &mode, buf) == 2) { + do_base64 = 1; + break; + } else if (sscanf (buf, "begin %o %s", &mode, buf) == 2) + break; + } + } + + /* If the output file name is given on the command line this rules. */ + dofre = FALSE; + if (forced_outname != NULL) + outname = (char *) forced_outname; + else { + /* Handle ~user/file format. */ + if (buf[0] != '~') + outname = buf; + else { + p = buf + 1; + while (*p != '/') + ++p; + if (*p == '\0') { + error_msg("%s: Illegal ~user", inname); + return FALSE; + } + *p++ = '\0'; + pw = getpwnam (buf + 1); + if (pw == NULL) { + error_msg("%s: No user `%s'", inname, buf + 1); + return FALSE; + } + outname = concat_path_file(pw->pw_dir, p); + dofre = TRUE; + } + } + + /* Create output file and set mode. */ + if (strcmp (outname, "/dev/stdout") != 0 && strcmp (outname, "-") != 0 + && (freopen (outname, "w", stdout) == NULL + || chmod (outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) + )) { + perror_msg("%s", outname); /* */ + if (dofre) + free(outname); + return FALSE; + } + + /* We differenciate decoding standard UU encoding and base64. A + common function would only slow down the program. */ + + /* For each input line: */ + if (do_base64) + res = read_base64 (inname); + else + res = read_stduu (inname); + if (dofre) + free(outname); + return res; +} + +int uudecode_main (int argc, + char **argv) +{ + int opt; + int exit_status; + const char *outname; + outname = NULL; + + while ((opt = getopt(argc, argv, "o:")) != EOF) { + switch (opt) { + case 0: + break; + + case 'o': + outname = optarg; + break; + + default: + show_usage(); + } + } + + if (optind == argc) + exit_status = decode ("stdin", outname) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + else { + exit_status = EXIT_SUCCESS; + do { + if (freopen (argv[optind], "r", stdin) != NULL) { + if (decode (argv[optind], outname) != 0) + exit_status = FALSE; + } else { + perror_msg("%s", argv[optind]); + exit_status = EXIT_FAILURE; + } + optind++; + } + while (optind < argc); + } + return(exit_status); +} + +/* Copyright (c) 1983 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. + * + * 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/uuencode.c b/busybox/uuencode.c new file mode 100644 index 000000000..fc037403a --- /dev/null +++ b/busybox/uuencode.c @@ -0,0 +1,180 @@ +/* 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 +#include +#include +#include +#include +#include +#include "busybox.h" + +/* Conversion table. for base 64 */ +static char tbl_base64[64] = { + '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', '+', '/' +}; + +static char tbl_std[64] = { + '`', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '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', '[', '\\', ']', '^', '_' +}; + +/* + * 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 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) = '='; + } + else if (i == length + 2) { + *(p - 1) = *(p - 2) = '='; + } + /* ...and zero-terminate it. */ + *p = '\0'; +} + +int uuencode_main(int argc, char **argv) +{ + const int src_buf_size = 60; // This *MUST* be a multiple of 3 + const int dst_buf_size = 4 * ((src_buf_size + 2) / 3); + RESERVE_BB_BUFFER(src_buf, src_buf_size + 1); + RESERVE_BB_BUFFER(dst_buf, dst_buf_size + 1); + struct stat stat_buf; + FILE *src_stream = stdin; + char *tbl = tbl_std; + size_t size; + mode_t mode; + int opt; + int column = 0; + int write_size = 0; + int remaining; + int buffer_offset = 0; + + while ((opt = getopt(argc, argv, "m")) != -1) { + switch (opt) { + case 'm': + tbl = tbl_base64; + break; + default: + show_usage(); + } + } + + switch (argc - optind) { + case 2: + src_stream = xfopen(argv[optind], "r"); + stat(argv[optind], &stat_buf); + mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + if (src_stream == stdout) { + printf("NULL\n"); + } + break; + case 1: + mode = 0666 & ~umask(0666); + break; + default: + show_usage(); + } + + 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) { + /* Encode the buffer we just read in */ + uuencode(src_buf, dst_buf, size, tbl); + + /* Write the buffer to stdout, wrapping at 60 chars. + * This looks overly complex, but it gets tricky as + * the line has to continue to wrap correctly if we + * have to refill the buffer + * + * Improvments most welcome + */ + + /* Initialise values for the new buffer */ + remaining = 4 * ((size + 2) / 3); + buffer_offset = 0; + + /* Write the buffer to stdout, wrapping at 60 chars + * starting from the column the last buffer ran out + */ + do { + if (remaining > (60 - column)) { + write_size = 60 - column; + } + else if (remaining < 60) { + write_size = remaining; + } else { + write_size = 60; + } + + /* Setup a new row if required */ + if (column == 0) { + putchar('\n'); + if (tbl == tbl_std) { + putchar('M'); + } + } + /* Write to the 60th column */ + if (fwrite(&dst_buf[buffer_offset], 1, write_size, stdout) != write_size) { + perror("Couldnt finish writing"); + } + /* Update variables based on last write */ + buffer_offset += write_size; + remaining -= write_size; + column += write_size; + if (column % 60 == 0) { + column = 0; + } + } while (remaining > 0); + } + printf(tbl == tbl_std ? "\n`\nend\n" : "\n====\n"); + + return(EXIT_SUCCESS); +} diff --git a/busybox/vi.c b/busybox/vi.c new file mode 100644 index 000000000..8d7506d0f --- /dev/null +++ b/busybox/vi.c @@ -0,0 +1,3947 @@ +/* vi: set sw=8 ts=8: */ +/* + * tiny vi.c: A small 'vi' clone + * Copyright (C) 2000, 2001 Sterling Huxley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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.15 2001/08/02 05:26:41 andersen Exp $"; + +/* + * To compile for standalone use: + * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c + * or + * gcc -Wall -Os -s -DSTANDALONE -DBB_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 immplement +#ifdef STANDALONE +#define vi_main main +#define BB_FEATURE_VI_COLON // 4288 +#define BB_FEATURE_VI_YANKMARK // 1408 +#define BB_FEATURE_VI_SEARCH // 1088 +#define BB_FEATURE_VI_USE_SIGNALS // 1056 +#define BB_FEATURE_VI_DOT_CMD // 576 +#define BB_FEATURE_VI_READONLY // 128 +#define BB_FEATURE_VI_SETOPTS // 576 +#define BB_FEATURE_VI_SET // 224 +#define BB_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 BB_FEATURE_VI_CRASHME // randomly pick commands to execute +#endif /* STANDALONE */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef STANDALONE +#include "busybox.h" +#endif /* STANDALONE */ + +#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 + +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 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 err_method; // indicate error with beep or flash +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 char erase_char; // the users erase character +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 char *SOs, *SOn; // terminal standout start/normal ESC sequence +static char *bell; // terminal bell sequence +static char *Ceol, *Ceos; // Clear-end-of-line and Clear-end-of-screen ESC sequence +static char *CMrc; // Cursor motion arbitrary destination ESC sequence +static char *CMup, *CMdown; // Cursor motion up and down ESC sequence +static Byte *status_buffer; // mesages to the user +static Byte last_input_char; // last char read from user +static Byte last_forward_char; // last char searched for with 'f' +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 + +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR +static int last_row; // where the cursor was last moved to +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ +#ifdef BB_FEATURE_VI_USE_SIGNALS +static jmp_buf restart; // catch_sig() +#endif /* BB_FEATURE_VI_USE_SIGNALS */ +#ifdef BB_FEATURE_VI_WIN_RESIZE +static struct winsize winsize; // remember the window size +#endif /* BB_FEATURE_VI_WIN_RESIZE */ +#ifdef BB_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 /* BB_FEATURE_VI_DOT_CMD */ +#if defined(BB_FEATURE_VI_DOT_CMD) || defined(BB_FEATURE_VI_YANKMARK) +static Byte *modifying_cmds; // cmds that modify text[] +#endif /* BB_FEATURE_VI_DOT_CMD || BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_READONLY +static int vi_readonly, readonly; +#endif /* BB_FEATURE_VI_READONLY */ +#ifdef BB_FEATURE_VI_SETOPTS +static int autoindent; +static int showmatch; +static int ignorecase; +#endif /* BB_FEATURE_VI_SETOPTS */ +#ifdef BB_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 /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_SEARCH +static Byte *last_search_pattern; // last pattern from a '/' or '?' search +#endif /* BB_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 *dollar_line(Byte *); // return pointer to just before NL +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 print_literal(Byte *, Byte *); // copy s to buf, convert unprintable +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(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(); +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 beep(void); // beep the terminal +static void indicate_error(char); // use flash or beep to indicate error +static void show_status_line(void); // put a message on the bottom line +static void psb(char *, ...); // Print Status Buf +static void psbs(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[] + +#ifdef BB_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 /* BB_FEATURE_VI_SEARCH */ +#ifdef BB_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 /* BB_FEATURE_VI_COLON */ +static Byte *get_input_line(Byte *); // get input line- use "status line" +#ifdef BB_FEATURE_VI_USE_SIGNALS +static void winch_sig(int); // catch window size changes +static void suspend_sig(int); // catch ctrl-Z +static void alarm_sig(int); // catch alarm time-outs +static void catch_sig(int); // catch ctrl-C +static void core_sig(int); // catch a core dump signal +#endif /* BB_FEATURE_VI_USE_SIGNALS */ +#ifdef BB_FEATURE_VI_DOT_CMD +static void start_new_cmd_q(Byte); // new queue for command +static void end_cmd_q(); // stop saving input chars +#else /* BB_FEATURE_VI_DOT_CMD */ +#define end_cmd_q() +#endif /* BB_FEATURE_VI_DOT_CMD */ +#ifdef BB_FEATURE_VI_WIN_RESIZE +static void window_size_get(int); // find out what size the window is +#endif /* BB_FEATURE_VI_WIN_RESIZE */ +#ifdef BB_FEATURE_VI_SETOPTS +static void showmatching(Byte *); // show the matching pair () [] {} +#endif /* BB_FEATURE_VI_SETOPTS */ +#if defined(BB_FEATURE_VI_YANKMARK) || defined(BB_FEATURE_VI_COLON) || defined(BB_FEATURE_VI_CRASHME) +static Byte *string_insert(Byte *, Byte *); // insert the string at 'p' +#endif /* BB_FEATURE_VI_YANKMARK || BB_FEATURE_VI_COLON || BB_FEATURE_VI_CRASHME */ +#ifdef BB_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 +static Byte *swap_context(Byte *); // goto new context for '' command +#endif /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_CRASHME +static void crash_dummy(); +static void crash_test(); +static int crashme = 0; +#endif /* BB_FEATURE_VI_CRASHME */ + + +extern int vi_main(int argc, char **argv) +{ + int c; + +#ifdef BB_FEATURE_VI_YANKMARK + int i; +#endif /* BB_FEATURE_VI_YANKMARK */ + + CMrc= "\033[%d;%dH"; // Terminal Crusor motion ESC sequence + CMup= "\033[A"; // move cursor up one line, same col + CMdown="\n"; // move cursor down one line, same col + Ceol= "\033[0K"; // Clear from cursor to end of line + Ceos= "\033[0J"; // Clear from cursor to end of screen + SOs = "\033[7m"; // Terminal standout mode on + SOn = "\033[0m"; // Terminal standout mode off + bell= "\007"; // Terminal bell sequence +#ifdef BB_FEATURE_VI_CRASHME + (void) srand((long) getpid()); +#endif /* BB_FEATURE_VI_CRASHME */ + status_buffer = (Byte *) malloc(200); // hold messages to user +#ifdef BB_FEATURE_VI_READONLY + vi_readonly = readonly = FALSE; + if (strncmp(argv[0], "view", 4) == 0) { + readonly = TRUE; + vi_readonly = TRUE; + } +#endif /* BB_FEATURE_VI_READONLY */ +#ifdef BB_FEATURE_VI_SETOPTS + autoindent = 1; + ignorecase = 1; + showmatch = 1; +#endif /* BB_FEATURE_VI_SETOPTS */ +#ifdef BB_FEATURE_VI_YANKMARK + for (i = 0; i < 28; i++) { + reg[i] = 0; + } // init the yank regs +#endif /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_DOT_CMD + modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[] +#endif /* BB_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 BB_FEATURE_VI_CRASHME + case 'C': + crashme = 1; + break; +#endif /* BB_FEATURE_VI_CRASHME */ +#ifdef BB_FEATURE_VI_READONLY + case 'R': // Read-only flag + readonly = TRUE; + break; +#endif /* BB_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 + if (cfn != 0) + free(cfn); + cfn = (Byte *) strdup(argv[optind]); + edit_file(cfn); + } + } + //----------------------------------------------------------- + + return (0); +} + +static void edit_file(Byte * fn) +{ + char c; + int cnt, size, ch; + +#ifdef BB_FEATURE_VI_USE_SIGNALS + char *msg; + int sig; +#endif /* BB_FEATURE_VI_USE_SIGNALS */ +#ifdef BB_FEATURE_VI_YANKMARK + static Byte *cur_line; +#endif /* BB_FEATURE_VI_YANKMARK */ + + rawmode(); + rows = 24; + columns = 80; + ch= -1; +#ifdef BB_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* BB_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 BB_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 /* BB_FEATURE_VI_YANKMARK */ + + err_method = 1; // flash + last_forward_char = last_input_char = '\0'; + crow = 0; + ccol = 0; + edit_status(); + +#ifdef BB_FEATURE_VI_USE_SIGNALS + signal(SIGHUP, catch_sig); + signal(SIGINT, catch_sig); + signal(SIGALRM, alarm_sig); + signal(SIGTERM, catch_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 + signal(SIGWINCH, winch_sig); + signal(SIGTSTP, suspend_sig); + sig = setjmp(restart); + if (sig != 0) { + 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 /* BB_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 BB_FEATURE_VI_DOT_CMD + if (last_modifying_cmd != 0) + free(last_modifying_cmd); + if (ioq_start != NULL) + free(ioq_start); + ioq = ioq_start = last_modifying_cmd = 0; + adding2q = 0; +#endif /* BB_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 BB_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 /* BB_FEATURE_VI_CRASHME */ + last_input_char = c = get_one_char(); // get a cmd from user +#ifdef BB_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 /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_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 /* BB_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 BB_FEATURE_VI_CRASHME + if (crashme > 0) + crash_test(); // test editor variables +#endif /* BB_FEATURE_VI_CRASHME */ + } + //------------------------------------------------------------------- + + place_cursor(rows, 0, FALSE); // go to bottom of screen + clear_to_eol(); // Erase to end of line + cookmode(); +} + +static Byte readbuffer[BUFSIZ]; + +#ifdef BB_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 (strlen((char *) readbuffer) > 0) + goto cd1; + cd0: + startrbi = rbi = 0; + sleeptime = 0; // how long to pause between commands + memset(readbuffer, '\0', BUFSIZ - 1); // 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"); + } + 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], buf[BUFSIZ], msg[BUFSIZ]; + + msg[0] = '\0'; + if (end < text) { + strcat((char *) msg, "end textend) { + strcat((char *) msg, "end>textend "); + } + if (dot < text) { + strcat((char *) msg, "dot end) { + strcat((char *) msg, "dot>end "); + } + if (screenbegin < text) { + strcat((char *) msg, "screenbegin end - 1) { + strcat((char *) msg, "screenbegin>end-1 "); + } + + if (strlen(msg) > 0) { + alarm(0); + sprintf(buf, "\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s", + totalcmds, last_input_char, msg, SOs, SOn); + write(1, buf, strlen(buf)); + 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 /* BB_FEATURE_VI_CRASHME */ + +//--------------------------------------------------------------------- +//----- 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 (cmd_mode == 2) { + // 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 && c <= 127) { // only ASCII chars + 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 && c <= 127) { + dot = char_insert(dot, c); // only ASCII chars + } + goto dc1; + } + + 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 BB_FEATURE_VI_CRASHME + case 0x14: // dc4 ctrl-T + crashme = (crashme == 0) ? 1 : 0; + break; +#endif /* BB_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 BB_FEATURE_VI_USE_SIGNALS + case 0x03: // ctrl-C interrupt + longjmp(restart, 1); + break; + case 26: // ctrl-Z suspend + suspend_sig(SIGTSTP); + break; +#endif /* BB_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 BB_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 /* BB_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 + 1); + 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 seperate 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 BB_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 *) strdup((char *) last_modifying_cmd); + } + break; +#endif /* BB_FEATURE_VI_DOT_CMD */ +#ifdef BB_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 + if (last_search_pattern != 0) { + free(last_search_pattern); + } + last_search_pattern = (Byte *) strdup((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 /* BB_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 BB_FEATURE_VI_COLON + colon(p); // execute the command +#else /* BB_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 == TRUE && 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) { + cnt = file_write(cfn, text, end - 1); + file_modified = FALSE; + psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt); + if (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 /* BB_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 BB_FEATURE_VI_DOT_CMD + if (c == 'D') + end_cmd_q(); // stop adding to q +#endif /* BB_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 == TRUE +#ifdef BB_FEATURE_VI_READONLY + && vi_readonly == FALSE + && readonly == FALSE +#endif /* BB_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 BB_FEATURE_VI_YANKMARK + case 'y': // y- yank something + case 'Y': // Y- Yank a line +#endif /* BB_FEATURE_VI_YANKMARK */ + yf = YANKDEL; // assume either "c" or "d" +#ifdef BB_FEATURE_VI_YANKMARK + if (c == 'y' || c == 'Y') + yf = YANKONLY; +#endif /* BB_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 BB_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 /* BB_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 BB_FEATURE_VI_YANKMARK + check_context(c); // update the current context +#endif /* BB_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--; +} + +//----- The Colon commands ------------------------------------- +#ifdef BB_FEATURE_VI_COLON +static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present +{ + int st; + Byte *q; + +#ifdef BB_FEATURE_VI_YANKMARK + Byte c; +#endif /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_SEARCH + Byte *pat, buf[BUFSIZ]; +#endif /* BB_FEATURE_VI_SEARCH */ + + *addr = -1; // assume no addr + if (*p == '.') { // the current line + p++; + q = begin_line(dot); + *addr = count_lines(text, q); +#ifdef BB_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 /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_SEARCH + } else if (*p == '/') { // a search pattern + q = buf; + for (p++; *p; p++) { + if (*p == '/') + break; + *q++ = *p; + *q = '\0'; + } + pat = (Byte *) strdup((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 /* BB_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 seperator + 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); +} + +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, forced; + 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" + // :! // run then return + // + if (strlen((char *) buf) <= 0) + goto vc1; + if (*buf == ':') + buf++; // move past the ':' + + forced = useforce = FALSE; + 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 + (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 == TRUE && useforce != TRUE) { + 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 *) strdup((char *) fn); // save the cfn + if (cfn != 0) + 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 BB_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 /* BB_FEATURE_VI_YANKMARK */ + // how many lines in text[]? + li = count_lines(text, end - 1); + psb("\"%s\"%s" +#ifdef BB_FEATURE_VI_READONLY + "%s" +#endif /* BB_FEATURE_VI_READONLY */ + " %dL, %dC", cfn, + (sr < 0 ? " [New file]" : ""), +#ifdef BB_FEATURE_VI_READONLY + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), +#endif /* BB_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 + if (cfn != NULL) + free(cfn); + cfn = (Byte *) strdup((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 + write(1, "\r\n", 2); + for (; q <= r; q++) { + c = *q; + if (c > '~') + standout_start(); + if (c == '\n') { + write(1, "$\r", 2); + } else if (*q < ' ') { + write(1, "^", 1); + c += '@'; + } + write(1, &c, 1); + if (c > '~') + standout_end(); + } +#ifdef BB_FEATURE_VI_SET + vc2: +#endif /* BB_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 == TRUE) { + // 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 == TRUE) { + 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 BB_FEATURE_VI_READONLY + l= readonly; // remember current files' status +#endif + ch = file_insert(fn, q, file_size(fn)); +#ifdef BB_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 BB_FEATURE_VI_READONLY + "%s" +#endif /* BB_FEATURE_VI_READONLY */ + " %dL, %dC", fn, +#ifdef BB_FEATURE_VI_READONLY + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), +#endif /* BB_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 == TRUE && useforce != TRUE) { + psbs("No write since last change (:rewind! overrides)"); + } else { + // reset the filenames to edit + optind = fn_start - 1; + editing = 0; + } +#ifdef BB_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 BB_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 /* BB_FEATURE_VI_SETOPTS */ + printf("\r\n"); + goto vc2; + } + if (strncasecmp((char *) args, "no", 2) == 0) + i = 2; // ":set noautoindent" +#ifdef BB_FEATURE_VI_SETOPTS + if (strncasecmp((char *) args + i, "autoindent", 10) == 0 || + strncasecmp((char *) args + i, "ai", 2) == 0) { + autoindent = (i == 2) ? 0 : 1; + } + if (strncasecmp((char *) args + i, "flash", 5) == 0 || + strncasecmp((char *) args + i, "fl", 2) == 0) { + err_method = (i == 2) ? 0 : 1; + } + if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 || + strncasecmp((char *) args + i, "ic", 2) == 0) { + ignorecase = (i == 2) ? 0 : 1; + } + if (strncasecmp((char *) args + i, "showmatch", 9) == 0 || + strncasecmp((char *) args + i, "sm", 2) == 0) { + showmatch = (i == 2) ? 0 : 1; + } + if (strncasecmp((char *) args + i, "tabstop", 7) == 0) { + sscanf(strchr((char *) args + i, '='), "=%d", &ch); + if (ch > 0 && ch < columns - 1) + tabstop = ch; + } +#endif /* BB_FEATURE_VI_SETOPTS */ +#endif /* BB_FEATURE_VI_SET */ +#ifdef BB_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 /* BB_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)) { // write text to file + // is there a file name to write to? + if (strlen((char *) args) > 0) { + fn = args; + } +#ifdef BB_FEATURE_VI_READONLY + if ((vi_readonly == TRUE || readonly == TRUE) && useforce == FALSE) { + psbs("\"%s\" File is read only", fn); + goto vc3; + } +#endif /* BB_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 == TRUE) { + // 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 == TRUE && forced == TRUE) { + // 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[1] == 'q' && l == ch) { + editing = 0; + } +#ifdef BB_FEATURE_VI_READONLY + vc3:; +#endif /* BB_FEATURE_VI_READONLY */ +#ifdef BB_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 /* BB_FEATURE_VI_YANKMARK */ + } else { + // cmd unknown + ni((Byte *) cmd); + } + vc1: + dot = bound_dot(dot); // make sure "dot" is valid + return; +#ifdef BB_FEATURE_VI_SEARCH +colon_s_fail: + psb(":s expression missing delimiters"); + return; +#endif + +} + +static void Hit_Return(void) +{ + char c; + + standout_start(); // start reverse video + write(1, "[Hit return to continue]", 24); + standout_end(); // end reverse video + while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */ + ; + redraw(TRUE); // force redraw all +} +#endif /* BB_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 < ' ') { + 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 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 < ' ') { + 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; + + if (screen != 0) + free(screen); + screensize = ro * co + 8; + screen = (Byte *) malloc(screensize); + // initialize the new screen. assume this will be a empty file. + screen_erase(); + // non-existant 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 + if (text != 0) { + //text -= 4; + free(text); + } + text = (Byte *) malloc(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 BB_FEATURE_VI_SEARCH +static int mycmp(Byte * s1, Byte * s2, int len) +{ + int i; + + i = strncmp((char *) s1, (char *) s2, len); +#ifdef BB_FEATURE_VI_SETOPTS + if (ignorecase) { + i = strncasecmp((char *) s1, (char *) s2, len); + } +#endif /* BB_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 /* BB_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 BB_FEATURE_VI_DOT_CMD + // also rmove char from last_modifying_cmd + if (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 /* BB_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 BB_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 /* BB_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 (dot > text) + 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 BB_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 /* BB_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 BB_FEATURE_VI_YANKMARK + text_yank(start, stop, YDreg); +#endif /* BB_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 BB_FEATURE_VI_SEARCH + "\n\tPattern searches with / and ?" +#endif /* BB_FEATURE_VI_SEARCH */ +#ifdef BB_FEATURE_VI_DOT_CMD + "\n\tLast command repeat with \'.\'" +#endif /* BB_FEATURE_VI_DOT_CMD */ +#ifdef BB_FEATURE_VI_YANKMARK + "\n\tLine marking with 'x" + "\n\tNamed buffers with \"x" +#endif /* BB_FEATURE_VI_YANKMARK */ +#ifdef BB_FEATURE_VI_READONLY + "\n\tReadonly if vi is called as \"view\"" + "\n\tReadonly with -R command line arg" +#endif /* BB_FEATURE_VI_READONLY */ +#ifdef BB_FEATURE_VI_SET + "\n\tSome colon mode commands with \':\'" +#endif /* BB_FEATURE_VI_SET */ +#ifdef BB_FEATURE_VI_SETOPTS + "\n\tSettable options with \":set\"" +#endif /* BB_FEATURE_VI_SETOPTS */ +#ifdef BB_FEATURE_VI_USE_SIGNALS + "\n\tSignal catching- ^C" + "\n\tJob suspend and resume with ^Z" +#endif /* BB_FEATURE_VI_USE_SIGNALS */ +#ifdef BB_FEATURE_VI_WIN_RESIZE + "\n\tAdapt to window re-sizes" +#endif /* BB_FEATURE_VI_WIN_RESIZE */ + ); +} + +static 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++) { + c = *s; + if (*s > '~') { + strcat((char *) buf, SOs); + c = *s - 128; + } + if (*s < ' ') { + strcat((char *) buf, "^"); + c += '@'; + } + b[0] = c; + strcat((char *) buf, (char *) b); + if (*s > '~') + strcat((char *) buf, SOn); + if (*s == '\n') { + strcat((char *) buf, "$"); + } + } +} + +#ifdef BB_FEATURE_VI_DOT_CMD +static void start_new_cmd_q(Byte c) +{ + // release old cmd + if (last_modifying_cmd != 0) + free(last_modifying_cmd); + // get buffer for new cmd + last_modifying_cmd = (Byte *) malloc(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() +{ +#ifdef BB_FEATURE_VI_YANKMARK + YDreg = 26; // go back to default Yank/Delete reg +#endif /* BB_FEATURE_VI_YANKMARK */ + adding2q = 0; + return; +} +#endif /* BB_FEATURE_VI_DOT_CMD */ + +#if defined(BB_FEATURE_VI_YANKMARK) || defined(BB_FEATURE_VI_COLON) || defined(BB_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 BB_FEATURE_VI_YANKMARK + psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg()); +#endif /* BB_FEATURE_VI_YANKMARK */ + return (p); +} +#endif /* BB_FEATURE_VI_YANKMARK || BB_FEATURE_VI_COLON || BB_FEATURE_VI_CRASHME */ + +#ifdef BB_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]; + if (t != 0) { // if already a yank register + free(t); // free it + } + t = (Byte *) malloc(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 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 /* BB_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); +#ifndef linux + term_vi.c_cc[VMIN] = 1; + term_vi.c_cc[VTIME] = 0; +#endif + erase_char = term_vi.c_cc[VERASE]; + tcsetattr(0, TCSANOW, &term_vi); +} + +static void cookmode(void) +{ + tcsetattr(0, TCSANOW, &term_orig); +} + +#ifdef BB_FEATURE_VI_WIN_RESIZE +//----- See what the window size currently is -------------------- +static void window_size_get(int sig) +{ + int i; + + i = ioctl(0, TIOCGWINSZ, &winsize); + if (i != 0) { + // force 24x80 + winsize.ws_row = 24; + winsize.ws_col = 80; + } + if (winsize.ws_row <= 1) { + winsize.ws_row = 24; + } + if (winsize.ws_col <= 1) { + winsize.ws_col = 80; + } + rows = (int) winsize.ws_row; + columns = (int) winsize.ws_col; +} +#endif /* BB_FEATURE_VI_WIN_RESIZE */ + +//----- Come here when we get a window resize signal --------- +#ifdef BB_FEATURE_VI_USE_SIGNALS +static void winch_sig(int sig) +{ + signal(SIGWINCH, winch_sig); +#ifdef BB_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* BB_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(getpid(), 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(getpid(), 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); + longjmp(restart, sig); +} + +static void alarm_sig(int sig) +{ + signal(SIGALRM, catch_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 + + dot = bound_dot(dot); // make sure "dot" is valid + + longjmp(restart, sig); +} +#endif /* BB_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 + 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)); +} + +//----- IO Routines -------------------------------------------- +static Byte readit(void) // read (maybe cursor) key from stdin +{ + Byte c; + int i, bufsiz, cnt, cmdindex; + struct esc_cmds { + Byte *seq; + Byte val; + }; + + static struct esc_cmds esccmds[] = { + {(Byte *) "OA", (Byte) VI_K_UP}, // cursor key Up + {(Byte *) "OB", (Byte) VI_K_DOWN}, // cursor key Down + {(Byte *) "OC", (Byte) VI_K_RIGHT}, // Cursor Key Right + {(Byte *) "OD", (Byte) VI_K_LEFT}, // cursor key Left + {(Byte *) "OH", (Byte) VI_K_HOME}, // Cursor Key Home + {(Byte *) "OF", (Byte) VI_K_END}, // Cursor Key End + {(Byte *) "", (Byte) VI_K_UP}, // cursor key Up + {(Byte *) "", (Byte) VI_K_DOWN}, // cursor key Down + {(Byte *) "", (Byte) VI_K_RIGHT}, // Cursor Key Right + {(Byte *) "", (Byte) VI_K_LEFT}, // cursor key Left + {(Byte *) "", (Byte) VI_K_HOME}, // Cursor Key Home + {(Byte *) "", (Byte) VI_K_END}, // Cursor Key End + {(Byte *) "[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert + {(Byte *) "[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up + {(Byte *) "[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down + {(Byte *) "OP", (Byte) VI_K_FUN1}, // Function Key F1 + {(Byte *) "OQ", (Byte) VI_K_FUN2}, // Function Key F2 + {(Byte *) "OR", (Byte) VI_K_FUN3}, // Function Key F3 + {(Byte *) "OS", (Byte) VI_K_FUN4}, // Function Key F4 + {(Byte *) "[15~", (Byte) VI_K_FUN5}, // Function Key F5 + {(Byte *) "[17~", (Byte) VI_K_FUN6}, // Function Key F6 + {(Byte *) "[18~", (Byte) VI_K_FUN7}, // Function Key F7 + {(Byte *) "[19~", (Byte) VI_K_FUN8}, // Function Key F8 + {(Byte *) "[20~", (Byte) VI_K_FUN9}, // Function Key F9 + {(Byte *) "[21~", (Byte) VI_K_FUN10}, // Function Key F10 + {(Byte *) "[23~", (Byte) VI_K_FUN11}, // Function Key F11 + {(Byte *) "[24~", (Byte) VI_K_FUN12}, // Function Key F12 + {(Byte *) "[11~", (Byte) VI_K_FUN1}, // Function Key F1 + {(Byte *) "[12~", (Byte) VI_K_FUN2}, // Function Key F2 + {(Byte *) "[13~", (Byte) VI_K_FUN3}, // Function Key F3 + {(Byte *) "[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 + // get input from User- are there already input chars in Q? + bufsiz = strlen((char *) readbuffer); + if (bufsiz <= 0) { + ri0: + // the Q is empty, wait for a typed char + bufsiz = read(0, readbuffer, BUFSIZ - 1); + if (bufsiz < 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; + bufsiz = 0; + } + readbuffer[bufsiz] = '\0'; + } + // return char if it is not part of ESC sequence + if (readbuffer[0] != 27) + goto ri1; + + // 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 && bufsiz <= (BUFSIZ - 5)) { + // read the rest of the ESC string + i = read(0, (void *) (readbuffer + bufsiz), BUFSIZ - bufsiz); + if (i > 0) { + bufsiz += i; + readbuffer[bufsiz] = '\0'; // Terminate the string + } + } + // Maybe cursor or function key? + for (cmdindex = 0; cmdindex < ESCCMDS_COUNT; cmdindex++) { + cnt = strlen((char *) esccmds[cmdindex].seq); + i = strncmp((char *) esccmds[cmdindex].seq, (char *) readbuffer, cnt); + if (i == 0) { + // is a Cursor key- put derived value back into Q + readbuffer[0] = esccmds[cmdindex].val; + // squeeze out the ESC sequence + for (i = 1; i < cnt; i++) { + memmove(readbuffer + 1, readbuffer + 2, BUFSIZ - 2); + readbuffer[BUFSIZ - 1] = '\0'; + } + break; + } + } + ri1: + c = readbuffer[0]; + // remove one char from Q + memmove(readbuffer, readbuffer + 1, BUFSIZ - 1); + readbuffer[BUFSIZ - 1] = '\0'; + (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 BB_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) { + // add new char to q + last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c; + } + } +#else /* BB_FEATURE_VI_DOT_CMD */ + c = readit(); // get the users input +#endif /* BB_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 + write(1, prompt, strlen((char *) 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 + write(1, " ", 3); // 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 + write(1, buf + i, 1); // echo the char back to user + i++; + } + } + refresh(FALSE); + if (obufp != NULL) + free(obufp); + obufp = (Byte *) strdup((char *) buf); + return (obufp); +} + +static int file_size(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 BB_FEATURE_VI_READONLY + readonly = FALSE; +#endif /* BB_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 BB_FEATURE_VI_READONLY + if (vi_readonly == TRUE) 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 BB_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 BB_FEATURE_VI_READONLY + // got the file- read-only + readonly = TRUE; +#endif /* BB_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. +// classicly 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; + int l; +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + char cm2[BUFSIZ]; + Byte *screenp; + // char cm3[BUFSIZ]; + int Rrow= last_row; +#endif /* BB_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 == FALSE) goto pc0; + +#ifdef BB_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 /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + pc0: + l= strlen(cm); + if (l) write(1, cm, l); // move the cursor +} + +//----- Erase from cursor to end of line ----------------------- +static void clear_to_eol() +{ + write(1, Ceol, strlen(Ceol)); // Erase from cursor to end of line +} + +//----- Erase from cursor to end of screen ----------------------- +static void clear_to_eos() +{ + write(1, Ceos, strlen(Ceos)); // Erase from cursor to end of screen +} + +//----- Start standout mode ------------------------------------ +static void standout_start() // send "start reverse video" sequence +{ + write(1, SOs, strlen(SOs)); // Start reverse video mode +} + +//----- End standout mode -------------------------------------- +static void standout_end() // send "end reverse video" sequence +{ + write(1, SOn, strlen(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 beep() +{ + write(1, bell, strlen(bell)); // send out a bell character +} + +static void indicate_error(char c) +{ +#ifdef BB_FEATURE_VI_CRASHME + if (crashme > 0) + return; // generate a random command +#endif /* BB_FEATURE_VI_CRASHME */ + if (err_method == 0) { + beep(); + } 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; + + 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 + write(1, status_buffer, cnt); + clear_to_eol(); + place_cursor(crow, ccol, FALSE); // put cursor back in correct place + } +} + +//----- format the status buffer, the bottom line of screen ------ +// print status buffer, with STANDOUT mode +static void psbs(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(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 BB_FEATURE_VI_READONLY + "%s" +#endif /* BB_FEATURE_VI_READONLY */ + "%s line %d of %d --%d%%--", + (cfn != 0 ? (char *) cfn : "No file"), +#ifdef BB_FEATURE_VI_READONLY + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), +#endif /* BB_FEATURE_VI_READONLY */ + (file_modified == TRUE ? " [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 < ' ' || c > '~') { + if (c == '\t') { + c = ' '; + // co % 8 != 7 + for (; (co % tabstop) != (tabstop - 1); co++) { + dest[co] = c; + } + } else { + dest[co++] = '^'; + c |= '@'; // make it visible + c &= 0x7f; // get rid of hi bit + } + } + // 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 BB_FEATURE_VI_OPTIMIZE_CURSOR + int last_li= -2; // last line that changed- for optimizing cursor movement +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + +#ifdef BB_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* BB_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 == TRUE) { + // 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 == TRUE) { + // 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 BB_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 /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + place_cursor(li, cs, FALSE); // use standard ESC sequence +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + } + + // write line out to terminal + write(1, sp+cs, ce-cs+1); +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + last_row = li; +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + } + } + +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE); + last_row = crow; +#else + place_cursor(crow, ccol, FALSE); +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + + if (offset != old_offset) + old_offset = offset; +} diff --git a/busybox/watchdog.c b/busybox/watchdog.c new file mode 100644 index 000000000..f0b0ebd0e --- /dev/null +++ b/busybox/watchdog.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini watchdog implementation for busybox + * + * Copyright (C) 2000 spoon . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +extern int watchdog_main(int argc, char **argv) +{ + int fd; + + if (argc != 2) { + show_usage(); + } + + if ((fd=open(argv[1], O_WRONLY)) == -1) { + perror_msg_and_die(argv[1]); + } + + while (1) { + sleep(30); + write(fd, "\0", 1); + } + + return EXIT_FAILURE; +} diff --git a/busybox/wc.c b/busybox/wc.c new file mode 100644 index 000000000..695e7e7d4 --- /dev/null +++ b/busybox/wc.c @@ -0,0 +1,156 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini wc implementation for busybox + * + * Copyright (C) 2000 Edward Betts + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include "busybox.h" + +static int total_lines, total_words, total_chars, max_length; +static int print_lines, print_words, print_chars, print_length; + +static void print_counts(int lines, int words, int chars, int length, + const char *name) +{ + char const *space = ""; + + if (print_lines) { + printf("%7d", lines); + space = " "; + } + if (print_words) { + printf("%s%7d", space, words); + space = " "; + } + if (print_chars) { + printf("%s%7d", space, chars); + space = " "; + } + if (print_length) + printf("%s%7d", space, length); + if (*name) + printf(" %s", name); + putchar('\n'); +} + +static void wc_file(FILE * file, const char *name) +{ + int lines, words, chars, length; + int in_word = 0, linepos = 0; + int c; + + lines = words = chars = length = 0; + while ((c = getc(file)) != EOF) { + chars++; + switch (c) { + case '\n': + lines++; + case '\r': + case '\f': + if (linepos > length) + length = linepos; + linepos = 0; + goto word_separator; + case '\t': + linepos += 8 - (linepos % 8); + goto word_separator; + case ' ': + linepos++; + case '\v': + word_separator: + if (in_word) { + in_word = 0; + words++; + } + break; + default: + linepos++; + in_word = 1; + break; + } + } + if (linepos > length) + length = linepos; + if (in_word) + words++; + print_counts(lines, words, chars, length, name); + total_lines += lines; + total_words += words; + total_chars += chars; + if (length > max_length) + max_length = length; + fclose(file); + fflush(stdout); +} + +int wc_main(int argc, char **argv) +{ + FILE *file; + unsigned int num_files_counted = 0; + int opt, status = EXIT_SUCCESS; + + total_lines = total_words = total_chars = max_length = 0; + print_lines = print_words = print_chars = print_length = 0; + + while ((opt = getopt(argc, argv, "clLw")) > 0) { + switch (opt) { + case 'c': + print_chars = 1; + break; + case 'l': + print_lines = 1; + break; + case 'L': + print_length = 1; + break; + case 'w': + print_words = 1; + break; + default: + show_usage(); + } + } + + if (!print_lines && !print_words && !print_chars && !print_length) + print_lines = print_words = print_chars = 1; + + if (argv[optind] == NULL || strcmp(argv[optind], "-") == 0) { + wc_file(stdin, ""); + return EXIT_SUCCESS; + } else { + while (optind < argc) { + if ((file = wfopen(argv[optind], "r")) != NULL) + wc_file(file, argv[optind]); + else + status = EXIT_FAILURE; + num_files_counted++; + optind++; + } + } + + if (num_files_counted > 1) + print_counts(total_lines, total_words, total_chars, + max_length, "total"); + + return status; +} diff --git a/busybox/wget.c b/busybox/wget.c new file mode 100644 index 000000000..59373d1d9 --- /dev/null +++ b/busybox/wget.c @@ -0,0 +1,834 @@ +/* vi: set sw=4 ts=4: */ +/* + * wget - retrieve a file using HTTP or FTP + * + * Chip Rosenthal Covad Communications + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include + +#include "busybox.h" + +/* Stupid libc5 doesn't define this... */ +#ifndef timersub +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +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(char *host, int port); +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 BB_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); \ + error_msg_and_die(s); } + + +#ifdef BB_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(char *p, char *buf, int len) { + + char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + char *s = buf; + + while(*p) { + if (s >= buf+len-4) + 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 + +int wget_main(int argc, char **argv) +{ + int n, try=5, status; + int port; + char *proxy; + 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); + int which_long_opt = 0, option_index = -1; + struct host_info server, target; + + 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... */ + +#define LONG_HEADER 1 + struct option long_options[] = { + { "continue", 0, NULL, 'c' }, + { "quiet", 0, NULL, 'q' }, + { "output-document", 1, NULL, 'O' }, + { "header", 1, &which_long_opt, LONG_HEADER }, + { 0, 0, 0, 0 } + }; + /* + * Crack command line. + */ + while ((n = getopt_long(argc, argv, "cqO:P:", long_options, &option_index)) != EOF) { + switch (n) { + case 'c': + ++do_continue; + break; + case 'P': + dir_prefix = optarg; + break; + case 'q': + quiet_flag = TRUE; + break; + case 'O': + /* can't set fname_out to NULL if outputting to stdout, because + * this gets interpreted as the auto-gen output filename + * case below - tausq@debian.org + */ + fname_out = optarg; + break; + case 0: + switch (which_long_opt) { + case LONG_HEADER: { + int arglen = strlen(optarg); + if(extra_headers_left - arglen - 2 <= 0) + error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen); + strcpy(extra_headers_ptr, optarg); + extra_headers_ptr += arglen; + extra_headers_left -= ( arglen + 2 ); + *extra_headers_ptr++ = '\r'; + *extra_headers_ptr++ = '\n'; + *(extra_headers_ptr + 1) = 0; + break; + } + } + break; + default: + show_usage(); + } + } + + if (argc - optind != 1) + show_usage(); + + parse_url(argv[optind], &target); + server.host = target.host; + server.port = target.port; + + /* + * Use the proxy if necessary. + */ + proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); + if (proxy) + parse_url(xstrdup(proxy), &server); + + /* Guess an output filename */ + if (!fname_out) { + fname_out = +#ifdef BB_FEATURE_WGET_STATUSBAR + curfile = +#endif + get_last_path_component(target.path); + if (fname_out==NULL || strlen(fname_out)<1) { + fname_out = +#ifdef BB_FEATURE_WGET_STATUSBAR + curfile = +#endif + "index.html"; + } + if (dir_prefix != NULL) + fname_out = concat_path_file(dir_prefix, fname_out); +#ifdef BB_FEATURE_WGET_STATUSBAR + } else { + curfile = get_last_path_component(fname_out); +#endif + } + if (do_continue && !fname_out) + 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 = xfopen(fname_out, (do_continue ? "a" : "w")); + } + + /* + * Determine where to start transfer. + */ + if (do_continue) { + if (fstat(fileno(output), &sbuf) < 0) + perror_msg_and_die("fstat()"); + if (sbuf.st_size > 0) + beg_range = sbuf.st_size; + else + do_continue = 0; + } + + if (proxy || !target.is_ftp) { + /* + * HTTP session + */ + do { + if (! --try) + close_delete_and_die("too many redirections"); + + /* + * Open socket to http server + */ + if (sfp) fclose(sfp); + sfp = open_socket(server.host, server.port); + + /* + * Send HTTP request. + */ + if (proxy) { + fprintf(sfp, "GET %stp://%s:%d/%s HTTP/1.1\r\n", + target.is_ftp ? "f" : "ht", target.host, + 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 BB_FEATURE_WGET_AUTHENTICATION + if (target.user) { + fprintf(sfp, "Authorization: Basic %s\r\n", + base64enc(target.user, buf, sizeof(buf))); + } + if (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) { + filesize = atol(s); + 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 = xstrdup(s+1); + else { + parse_url(xstrdup(s), &target); + if (!proxy) { + server.host = target.host; + server.port = target.port; + } + } + } + } + } while(status >= 300); + + dfp = sfp; + } + else + { + /* + * FTP session + */ + if (! target.user) + target.user = xstrdup("anonymous:busybox@"); + + sfp = open_socket(server.host, server.port); + 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) { + filesize = atol(buf+4); + 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; + dfp = open_socket(server.host, port); + + 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 BB_FEATURE_WGET_STATUSBAR + if (quiet_flag==FALSE) + progressmeter(-1); +#endif + do { + while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, chunked ? (filesize > sizeof(buf) ? sizeof(buf) : filesize) : sizeof(buf), dfp)) > 0) { + safe_fwrite(buf, 1, n, output); +#ifdef BB_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)) + perror_msg_and_die("network read error"); + } while (chunked); +#ifdef BB_FEATURE_WGET_STATUSBAR + if (quiet_flag==FALSE) + progressmeter(1); +#endif + if (!proxy && target.is_ftp) { + fclose(dfp); + if (ftpcmd(NULL, NULL, sfp, buf) != 226) + 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; + + if (strncmp(url, "http://", 7) == 0) { + h->port = 80; + h->host = url + 7; + h->is_ftp = 0; + } else if (strncmp(url, "ftp://", 6) == 0) { + h->port = 21; + h->host = url + 6; + h->is_ftp = 1; + } else + error_msg_and_die("not an http or ftp url: %s", url); + + sp = strchr(h->host, '/'); + if (sp != NULL) { + *sp++ = '\0'; + h->path = sp; + } else + h->path = ""; + + up = strrchr(h->host, '@'); + if (up != NULL) { + h->user = h->host; + *up++ = '\0'; + h->host = up; + } else + h->user = NULL; + + cp = strchr(h->host, ':'); + if (cp != NULL) { + *cp++ = '\0'; + h->port = atoi(cp); + } + +} + + +FILE *open_socket(char *host, int port) +{ + struct sockaddr_in s_in; + struct hostent *hp; + int fd; + FILE *fp; + + memset(&s_in, 0, sizeof(s_in)); + s_in.sin_family = AF_INET; + hp = xgethostbyname(host); + memcpy(&s_in.sin_addr, hp->h_addr_list[0], hp->h_length); + s_in.sin_port = htons(port); + + /* + * Get the server onto a stdio stream. + */ + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + perror_msg_and_die("socket()"); + if (connect(fd, (struct sockaddr *) &s_in, sizeof(s_in)) < 0) + perror_msg_and_die("connect(%s)", host); + if ((fp = fdopen(fd, "r+")) == NULL) + 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 != ':') + 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) +{ + char *p; + + if (s1) { + if (!s2) s2=""; + fprintf(fp, "%s%s\n", s1, s2); + fflush(fp); + } + + do { + p = fgets(buf, 510, fp); + if (!p) + perror_msg_and_die("fgets()"); + } while (! isdigit(buf[0]) || buf[3] != ' '); + + return atoi(buf); +} + +#ifdef BB_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) +{ + struct winsize winsize; + + if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) + return (winsize.ws_col ? winsize.ws_col : 80); + else + return (80); +} + +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(fileno(stderr), 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 BB_FEATURE_WGET_STATUSBAR stuff, + * much of which was blatently 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. + * + * 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.45 2001/07/19 22:28:01 andersen Exp $ + */ + + + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + + + diff --git a/busybox/which.c b/busybox/which.c new file mode 100644 index 000000000..c460ffdd1 --- /dev/null +++ b/busybox/which.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Which implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by 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 + * + */ + +/* getopt not needed */ +#include +#include +#include +#include "busybox.h" + +extern int which_main(int argc, char **argv) +{ + char *path_list, *path_n; + struct stat filestat; + int i, count=1, found, status = EXIT_SUCCESS; + + if (argc <= 1 || **(argv + 1) == '-') + show_usage(); + argc--; + + path_list = getenv("PATH"); + if (!path_list) + path_list = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"; + + /* Replace colons with zeros in path_parsed and count them */ + for(i=strlen(path_list); i > 0; i--) + if (path_list[i]==':') { + path_list[i]=0; + count++; + } + + while(argc-- > 0) { + path_n = path_list; + argv++; + found = 0; + for (i = 0; i < count; i++) { + char *buf; + buf = concat_path_file(path_n, *argv); + if (stat (buf, &filestat) == 0 + && filestat.st_mode & S_IXUSR) + { + puts(buf); + found = 1; + break; + } + free(buf); + path_n += (strlen(path_n) + 1); + } + if (!found) + status = EXIT_FAILURE; + } + return status; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/whoami.c b/busybox/whoami.c new file mode 100644 index 000000000..c3b1140e6 --- /dev/null +++ b/busybox/whoami.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini whoami implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +extern int whoami_main(int argc, char **argv) +{ + char user[9]; + uid_t uid = geteuid(); + + if (argc > 1) + show_usage(); + + my_getpwuid(user, uid); + if (*user) { + puts(user); + return EXIT_SUCCESS; + } + error_msg_and_die("cannot find username for UID %u", (unsigned) uid); +} diff --git a/busybox/xargs.c b/busybox/xargs.c new file mode 100644 index 000000000..48adae90a --- /dev/null +++ b/busybox/xargs.c @@ -0,0 +1,105 @@ +/* + * Mini xargs implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Erik Andersen , + * Remixed by Mark Whitley , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include "busybox.h" + +int xargs_main(int argc, char **argv) +{ + char *cmd_to_be_executed = NULL; + char *file_to_act_on = NULL; + + /* + * No options are supported in this version of xargs; no getopt. + * + * Re: The missing -t flag: Most programs that produce output also print + * the filename, so xargs doesn't really need to do it again. Supporting + * the -t flag =greatly= bloats up the size of this app and the memory it + * uses because you have to buffer all the input file strings in memory. If + * you really want to see the filenames that xargs will act on, just run it + * once with no args and xargs will echo the filename. Simple. + */ + + /* Store the command to be executed (taken from the command line) */ + if (argc == 1) { + /* default behavior is to echo all the filenames */ + cmd_to_be_executed = xstrdup("/bin/echo "); + } else { + /* concatenate all the arguments passed to xargs together */ + int i; + int len = 1; /* for the '\0' */ + for (i = 1; i < argc; i++) { + len += strlen(argv[i]); + len += 1; /* for the space between the args */ + cmd_to_be_executed = xrealloc(cmd_to_be_executed, len); + strcat(cmd_to_be_executed, argv[i]); + strcat(cmd_to_be_executed, " "); + } + } + + /* Now, read in one line at a time from stdin, and store this + * line to be used later as an argument to the command */ + while ((file_to_act_on = get_line_from_file(stdin)) !=NULL) { + + FILE *cmd_output = NULL; + char *output_line = NULL; + char *execstr = NULL; + + /* eat the newline off the filename. */ + chomp(file_to_act_on); + + /* eat blank lines */ + if (strlen(file_to_act_on) == 0) + continue; + + /* assemble the command and execute it */ + execstr = xcalloc(strlen(cmd_to_be_executed) + + strlen(file_to_act_on) + 1, sizeof(char)); + strcat(execstr, cmd_to_be_executed); + strcat(execstr, file_to_act_on); + cmd_output = popen(execstr, "r"); + if (cmd_output == NULL) + perror_msg_and_die("popen"); + + /* harvest the output */ + while ((output_line = get_line_from_file(cmd_output)) != NULL) { + fputs(output_line, stdout); + free(output_line); + } + + /* clean up */ + pclose(cmd_output); + free(execstr); + free(file_to_act_on); + } + +#ifdef BB_FEATURE_CLEAN_UP + free(cmd_to_be_executed); +#endif + + return 0; +} + +/* vi: set sw=4 ts=4: */ diff --git a/busybox/yes.c b/busybox/yes.c new file mode 100644 index 000000000..7d9596d0b --- /dev/null +++ b/busybox/yes.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini yes implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "busybox.h" + +extern int yes_main(int argc, char **argv) +{ + int i; + + if (argc >= 2 && *argv[1] == '-') + show_usage(); + + if (argc == 1) { + while (1) + if (puts("y") == EOF) { + perror("yes"); + return EXIT_FAILURE; + } + } + + while (1) + for (i = 1; i < argc; i++) + if (fputs(argv[i], stdout) == EOF + || putchar(i == argc - 1 ? '\n' : ' ') == EOF) { + perror("yes"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} -- 2.25.1