From 8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373 Mon Sep 17 00:00:00 2001 From: nobody Date: Wed, 13 Oct 2004 09:42:10 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'busybox_1_00'. --- busybox/.cvsignore | 7 + busybox/.indent.pro | 33 + busybox/AUTHORS | 133 + busybox/Changelog | 1381 ++ busybox/INSTALL | 20 + busybox/LICENSE | 340 + busybox/Makefile | 315 + busybox/README | 127 + busybox/Rules.mak | 196 + busybox/TODO | 11 + busybox/applets/Makefile | 32 + busybox/applets/Makefile.in | 37 + busybox/applets/applets.c | 512 + busybox/applets/busybox.c | 193 + busybox/applets/busybox.mkll | 24 + busybox/applets/install.sh | 52 + busybox/archival/Config.in | 258 + busybox/archival/Makefile | 32 + busybox/archival/Makefile.in | 48 + busybox/archival/ar.c | 110 + busybox/archival/bunzip2.c | 91 + busybox/archival/cpio.c | 99 + busybox/archival/dpkg.c | 1833 +++ busybox/archival/dpkg_deb.c | 112 + busybox/archival/gunzip.c | 198 + busybox/archival/gzip.c | 2548 +++ busybox/archival/libunarchive/Makefile | 32 + busybox/archival/libunarchive/Makefile.in | 84 + .../archival/libunarchive/archive_xread_all.c | 32 + .../libunarchive/archive_xread_all_eof.c | 32 + .../archival/libunarchive/check_header_gzip.c | 57 + busybox/archival/libunarchive/data_align.c | 33 + .../archival/libunarchive/data_extract_all.c | 124 + .../libunarchive/data_extract_to_buffer.c | 28 + .../libunarchive/data_extract_to_stdout.c | 23 + busybox/archival/libunarchive/data_skip.c | 27 + .../libunarchive/decompress_bunzip2.c | 611 + .../libunarchive/decompress_uncompress.c | 293 + .../archival/libunarchive/decompress_unzip.c | 982 ++ .../archival/libunarchive/filter_accept_all.c | 32 + .../libunarchive/filter_accept_list.c | 34 + .../filter_accept_list_reassign.c | 55 + .../libunarchive/filter_accept_reject_list.c | 45 + .../archival/libunarchive/find_list_entry.c | 30 + busybox/archival/libunarchive/get_header_ar.c | 126 + .../archival/libunarchive/get_header_cpio.c | 161 + .../archival/libunarchive/get_header_tar.c | 215 + .../libunarchive/get_header_tar_bz2.c | 38 + .../archival/libunarchive/get_header_tar_gz.c | 42 + busybox/archival/libunarchive/header_list.c | 7 + busybox/archival/libunarchive/header_skip.c | 7 + .../libunarchive/header_verbose_list.c | 29 + busybox/archival/libunarchive/init_handle.c | 36 + .../archival/libunarchive/open_transformer.c | 51 + busybox/archival/libunarchive/seek_by_char.c | 32 + busybox/archival/libunarchive/seek_by_jump.c | 35 + .../archival/libunarchive/unpack_ar_archive.c | 34 + busybox/archival/rpm.c | 349 + busybox/archival/rpm2cpio.c | 106 + busybox/archival/tar.c | 891 + busybox/archival/uncompress.c | 115 + busybox/archival/unzip.c | 247 + busybox/console-tools/Config.in | 68 + busybox/console-tools/Makefile | 32 + busybox/console-tools/Makefile.in | 44 + busybox/console-tools/chvt.c | 61 + busybox/console-tools/clear.c | 34 + busybox/console-tools/deallocvt.c | 56 + busybox/console-tools/dumpkmap.c | 91 + busybox/console-tools/loadfont.c | 207 + busybox/console-tools/loadkmap.c | 82 + busybox/console-tools/openvt.c | 84 + busybox/console-tools/reset.c | 45 + busybox/console-tools/setkeycodes.c | 72 + busybox/coreutils/Config.in | 613 + busybox/coreutils/Makefile | 32 + busybox/coreutils/Makefile.in | 98 + busybox/coreutils/basename.c | 62 + busybox/coreutils/cal.c | 379 + busybox/coreutils/cat.c | 67 + busybox/coreutils/chgrp.c | 81 + busybox/coreutils/chmod.c | 112 + busybox/coreutils/chown.c | 105 + busybox/coreutils/chroot.c | 53 + busybox/coreutils/cmp.c | 152 + busybox/coreutils/cp.c | 119 + busybox/coreutils/cut.c | 344 + busybox/coreutils/date.c | 292 + busybox/coreutils/dd.c | 203 + busybox/coreutils/df.c | 170 + busybox/coreutils/dirname.c | 39 + busybox/coreutils/dos2unix.c | 198 + busybox/coreutils/du.c | 269 + busybox/coreutils/echo.c | 165 + busybox/coreutils/env.c | 144 + busybox/coreutils/expr.c | 528 + busybox/coreutils/false.c | 32 + busybox/coreutils/fold.c | 194 + busybox/coreutils/head.c | 138 + busybox/coreutils/hostid.c | 38 + busybox/coreutils/id.c | 135 + busybox/coreutils/install.c | 151 + busybox/coreutils/length.c | 19 + busybox/coreutils/libcoreutils/Makefile | 33 + busybox/coreutils/libcoreutils/Makefile.in | 37 + busybox/coreutils/libcoreutils/coreutils.h | 12 + busybox/coreutils/libcoreutils/cp_mv_stat.c | 45 + .../libcoreutils/getopt_mk_fifo_nod.c | 45 + .../libcoreutils/xgetoptfile_sort_uniq.c | 38 + busybox/coreutils/ln.c | 102 + busybox/coreutils/logname.c | 55 + busybox/coreutils/ls.c | 1134 ++ busybox/coreutils/md5_sha1_sum.c | 204 + busybox/coreutils/mkdir.c | 75 + busybox/coreutils/mkfifo.c | 51 + busybox/coreutils/mknod.c | 63 + busybox/coreutils/mv.c | 139 + busybox/coreutils/od.c | 231 + busybox/coreutils/printf.c | 316 + busybox/coreutils/pwd.c | 37 + busybox/coreutils/realpath.c | 54 + busybox/coreutils/rm.c | 66 + busybox/coreutils/rmdir.c | 73 + busybox/coreutils/seq.c | 51 + busybox/coreutils/sleep.c | 86 + busybox/coreutils/sort.c | 100 + busybox/coreutils/stty.c | 1314 ++ busybox/coreutils/sync.c | 36 + busybox/coreutils/tail.c | 330 + busybox/coreutils/tee.c | 120 + busybox/coreutils/test.c | 559 + busybox/coreutils/touch.c | 76 + busybox/coreutils/tr.c | 248 + busybox/coreutils/true.c | 32 + busybox/coreutils/tty.c | 58 + busybox/coreutils/uname.c | 118 + busybox/coreutils/uniq.c | 112 + busybox/coreutils/usleep.c | 41 + busybox/coreutils/uudecode.c | 201 + busybox/coreutils/uuencode.c | 149 + busybox/coreutils/watch.c | 110 + busybox/coreutils/wc.c | 227 + busybox/coreutils/who.c | 83 + busybox/coreutils/whoami.c | 38 + busybox/coreutils/yes.c | 56 + busybox/debian/busybox-cvs-doc.docs | 1 + busybox/debian/busybox-cvs-static.dirs | 2 + busybox/debian/busybox-cvs-static.manpages | 1 + busybox/debian/busybox-cvs-static.override | 1 + busybox/debian/busybox-cvs.dirs | 1 + busybox/debian/busybox-cvs.manpages | 1 + busybox/debian/changelog | 479 + busybox/debian/compat | 1 + busybox/debian/config-deb | 379 + busybox/debian/config-floppy-udeb-linux | 359 + busybox/debian/config-static | 503 + busybox/debian/config-udeb | 410 + busybox/debian/config-udeb-linux | 419 + busybox/debian/control | 88 + busybox/debian/control-extract | 2 + busybox/debian/copyright | 24 + busybox/debian/rules | 196 + busybox/debianutils/Config.in | 58 + busybox/debianutils/Makefile | 32 + busybox/debianutils/Makefile.in | 41 + busybox/debianutils/mktemp.c | 63 + busybox/debianutils/pipe_progress.c | 55 + busybox/debianutils/readlink.c | 46 + busybox/debianutils/run_parts.c | 114 + busybox/debianutils/start_stop_daemon.c | 296 + busybox/debianutils/which.c | 96 + busybox/docs/.cvsignore | 8 + busybox/docs/autodocifier.pl | 274 + busybox/docs/busybox.net/.cvsignore | 2 + busybox/docs/busybox.net/FAQ.html | 324 + busybox/docs/busybox.net/about.html | 63 + busybox/docs/busybox.net/busybox-growth.ps | 404 + busybox/docs/busybox.net/copyright.txt | 29 + busybox/docs/busybox.net/cvs_anon.html | 57 + busybox/docs/busybox.net/cvs_howto.html | 44 + busybox/docs/busybox.net/cvs_write.html | 32 + busybox/docs/busybox.net/docs.html | 27 + busybox/docs/busybox.net/download.html | 38 + busybox/docs/busybox.net/footer.html | 20 + busybox/docs/busybox.net/header.html | 81 + busybox/docs/busybox.net/images/back.png | Bin 0 -> 322 bytes busybox/docs/busybox.net/images/busybox.jpeg | Bin 0 -> 9023 bytes busybox/docs/busybox.net/images/busybox.png | Bin 0 -> 34014 bytes busybox/docs/busybox.net/images/busybox1.png | Bin 0 -> 10913 bytes busybox/docs/busybox.net/images/busybox2.jpg | Bin 0 -> 8204 bytes busybox/docs/busybox.net/images/busybox3.jpg | Bin 0 -> 3292 bytes busybox/docs/busybox.net/images/dir.png | Bin 0 -> 309 bytes busybox/docs/busybox.net/images/donate.png | Bin 0 -> 807 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 -> 6798 bytes busybox/docs/busybox.net/images/sdsmall.png | Bin 0 -> 1593 bytes busybox/docs/busybox.net/images/text.png | Bin 0 -> 307 bytes busybox/docs/busybox.net/images/vh40.gif | Bin 0 -> 906 bytes .../docs/busybox.net/images/written.in.vi.png | Bin 0 -> 4394 bytes busybox/docs/busybox.net/index.html | 1 + busybox/docs/busybox.net/license.html | 135 + busybox/docs/busybox.net/lists.html | 45 + busybox/docs/busybox.net/news.html | 52 + busybox/docs/busybox.net/oldnews.html | 1060 ++ busybox/docs/busybox.net/products.html | 166 + busybox/docs/busybox.net/screenshot.html | 57 + busybox/docs/busybox.net/shame.html | 77 + busybox/docs/busybox_footer.pod | 258 + busybox/docs/busybox_header.pod | 111 + busybox/docs/contributing.txt | 449 + busybox/docs/new-applet-HOWTO.txt | 163 + busybox/docs/style-guide.txt | 680 + busybox/editors/Config.in | 123 + busybox/editors/Makefile | 32 + busybox/editors/Makefile.in | 48 + busybox/editors/awk.c | 2764 ++++ busybox/editors/patch.c | 290 + busybox/editors/sed.c | 1220 ++ busybox/editors/vi.c | 3983 +++++ busybox/examples/bootfloppy/bootfloppy.txt | 180 + 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 | 105 + 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 | 237 + busybox/examples/devfsd.conf | 133 + busybox/examples/inetd.conf | 73 + busybox/examples/inittab | 90 + busybox/examples/mk2knr.pl | 84 + busybox/examples/udhcp/sample.bound | 31 + busybox/examples/udhcp/sample.deconfig | 4 + busybox/examples/udhcp/sample.nak | 4 + busybox/examples/udhcp/sample.renew | 31 + busybox/examples/udhcp/sample.script | 7 + busybox/examples/udhcp/simple.script | 40 + busybox/examples/udhcp/udhcpd.conf | 123 + busybox/examples/undeb | 53 + busybox/examples/unrpm | 48 + busybox/findutils/Config.in | 133 + busybox/findutils/Makefile | 32 + busybox/findutils/Makefile.in | 38 + busybox/findutils/find.c | 280 + busybox/findutils/grep.c | 397 + busybox/findutils/xargs.c | 586 + busybox/include/.cvsignore | 2 + busybox/include/applets.h | 678 + busybox/include/busybox.h | 121 + busybox/include/dump.h | 49 + busybox/include/grp_.h | 116 + busybox/include/inet_common.h | 33 + busybox/include/libbb.h | 487 + busybox/include/pwd_.h | 106 + busybox/include/shadow_.h | 98 + busybox/include/unarchive.h | 107 + busybox/include/usage.h | 2885 ++++ busybox/init/Config.in | 72 + busybox/init/Makefile | 32 + busybox/init/Makefile.in | 62 + busybox/init/halt.c | 48 + busybox/init/init.c | 1214 ++ busybox/init/init_shared.c | 95 + busybox/init/init_shared.h | 3 + busybox/init/mesg.c | 58 + busybox/init/poweroff.c | 56 + busybox/init/reboot.c | 56 + busybox/libbb/.cvsignore | 1 + busybox/libbb/Makefile | 32 + busybox/libbb/Makefile.in | 107 + busybox/libbb/README | 11 + busybox/libbb/ask_confirmation.c | 49 + busybox/libbb/bb_askpass.c | 87 + busybox/libbb/bb_asprintf.c | 22 + busybox/libbb/change_identity.c | 62 + busybox/libbb/chomp.c | 45 + busybox/libbb/compare_string_array.c | 31 + busybox/libbb/concat_path_file.c | 44 + busybox/libbb/concat_subpath_file.c | 36 + busybox/libbb/copy_file.c | 268 + busybox/libbb/copyfd.c | 90 + busybox/libbb/correct_password.c | 77 + busybox/libbb/create_icmp6_socket.c | 39 + busybox/libbb/create_icmp_socket.c | 37 + busybox/libbb/default_error_retval.c | 32 + busybox/libbb/device_open.c | 53 + busybox/libbb/dump.c | 819 + busybox/libbb/error_msg.c | 46 + busybox/libbb/error_msg_and_die.c | 47 + busybox/libbb/fclose_nonstdin.c | 37 + busybox/libbb/fflush_stdout_and_exit.c | 37 + busybox/libbb/fgets_str.c | 67 + busybox/libbb/find_mount_point.c | 75 + busybox/libbb/find_pid_by_name.c | 70 + busybox/libbb/find_root_device.c | 89 + busybox/libbb/full_read.c | 63 + busybox/libbb/full_write.c | 60 + busybox/libbb/get_console.c | 99 + busybox/libbb/get_last_path_component.c | 56 + busybox/libbb/get_line_from_file.c | 82 + busybox/libbb/get_terminal_width_height.c | 66 + busybox/libbb/get_ug_id.c | 30 + busybox/libbb/getopt_ulflags.c | 171 + busybox/libbb/hash_fd.c | 859 + busybox/libbb/herror_msg.c | 44 + busybox/libbb/herror_msg_and_die.c | 45 + busybox/libbb/human_readable.c | 87 + busybox/libbb/inet_common.c | 249 + busybox/libbb/inode_hash.c | 111 + busybox/libbb/interface.c | 2083 +++ busybox/libbb/isdirectory.c | 60 + busybox/libbb/kernel_version.c | 60 + busybox/libbb/last_char_is.c | 38 + busybox/libbb/llist_add_to.c | 15 + busybox/libbb/login.c | 128 + busybox/libbb/loop.c | 157 + busybox/libbb/make_directory.c | 117 + busybox/libbb/messages.c | 96 + busybox/libbb/mode_string.c | 139 + busybox/libbb/module_syscalls.c | 116 + busybox/libbb/mtab.c | 116 + busybox/libbb/mtab_file.c | 42 + busybox/libbb/my_getgrgid.c | 57 + busybox/libbb/my_getgrnam.c | 49 + busybox/libbb/my_getpwnam.c | 49 + busybox/libbb/my_getpwuid.c | 56 + busybox/libbb/my_getug.c | 64 + busybox/libbb/obscure.c | 251 + busybox/libbb/parse_mode.c | 177 + busybox/libbb/parse_number.c | 64 + busybox/libbb/perror_msg.c | 45 + busybox/libbb/perror_msg_and_die.c | 46 + busybox/libbb/perror_nomsg.c | 30 + busybox/libbb/perror_nomsg_and_die.c | 30 + busybox/libbb/print_file.c | 76 + busybox/libbb/printf.c | 181 + busybox/libbb/process_escape_sequence.c | 112 + busybox/libbb/procps.c | 158 + busybox/libbb/pw_encrypt.c | 45 + busybox/libbb/pwd2spwd.c | 74 + busybox/libbb/qmodule.c | 29 + busybox/libbb/read_package_field.c | 114 + busybox/libbb/recursive_action.c | 141 + busybox/libbb/remove_file.c | 124 + busybox/libbb/restricted_shell.c | 57 + busybox/libbb/run_parts.c | 126 + busybox/libbb/run_shell.c | 87 + busybox/libbb/safe_read.c | 48 + busybox/libbb/safe_strncpy.c | 42 + busybox/libbb/safe_strtol.c | 92 + busybox/libbb/safe_write.c | 48 + busybox/libbb/setup_environment.c | 93 + busybox/libbb/simplify_path.c | 64 + busybox/libbb/skip_whitespace.c | 33 + busybox/libbb/speed_table.c | 130 + busybox/libbb/syscalls.c | 105 + busybox/libbb/syslog_msg_with_name.c | 0 busybox/libbb/trim.c | 49 + busybox/libbb/u_signal_names.c | 189 + busybox/libbb/vdprintf.c | 47 + busybox/libbb/verror_msg.c | 43 + busybox/libbb/vfork_daemon_rexec.c | 78 + busybox/libbb/vherror_msg.c | 37 + busybox/libbb/vperror_msg.c | 45 + busybox/libbb/warn_ignoring_args.c | 30 + busybox/libbb/wfopen.c | 44 + busybox/libbb/wfopen_input.c | 54 + busybox/libbb/xconnect.c | 71 + busybox/libbb/xfuncs.c | 197 + busybox/libbb/xgetcwd.c | 48 + busybox/libbb/xgethostbyname.c | 35 + busybox/libbb/xgethostbyname2.c | 37 + busybox/libbb/xgetlarg.c | 35 + busybox/libbb/xgetularg.c | 160 + busybox/libbb/xreadlink.c | 37 + busybox/libbb/xregcomp.c | 49 + busybox/libpwdgrp/Makefile | 32 + busybox/libpwdgrp/Makefile.in | 53 + busybox/libpwdgrp/pwd_grp.c | 1116 ++ busybox/loginutils/Config.in | 161 + busybox/loginutils/Makefile | 32 + busybox/loginutils/Makefile.in | 57 + busybox/loginutils/addgroup.c | 171 + busybox/loginutils/adduser.c | 314 + busybox/loginutils/delgroup.c | 62 + busybox/loginutils/delline.c | 113 + busybox/loginutils/deluser.c | 68 + busybox/loginutils/getty.c | 1020 ++ busybox/loginutils/login.c | 486 + busybox/loginutils/passwd.c | 400 + busybox/loginutils/su.c | 157 + busybox/loginutils/sulogin.c | 158 + busybox/loginutils/vlock.c | 231 + busybox/miscutils/Config.in | 201 + busybox/miscutils/Makefile | 32 + busybox/miscutils/Makefile.in | 55 + busybox/miscutils/adjtimex.c | 167 + busybox/miscutils/crond.c | 1055 ++ busybox/miscutils/crontab.c | 368 + busybox/miscutils/dc.c | 228 + busybox/miscutils/devfsd.c | 2183 +++ busybox/miscutils/hdparm.c | 2872 ++++ busybox/miscutils/last.c | 107 + busybox/miscutils/makedevs.c | 93 + busybox/miscutils/mt.c | 121 + busybox/miscutils/rx.c | 344 + busybox/miscutils/strings.c | 156 + busybox/miscutils/time.c | 502 + busybox/miscutils/watchdog.c | 81 + busybox/modutils/Config.in | 113 + busybox/modutils/Makefile | 32 + busybox/modutils/Makefile.in | 39 + busybox/modutils/insmod.c | 4039 +++++ busybox/modutils/lsmod.c | 174 + busybox/modutils/modprobe.c | 654 + busybox/modutils/rmmod.c | 124 + busybox/networking/Config.in | 634 + busybox/networking/Makefile | 32 + busybox/networking/Makefile.in | 68 + busybox/networking/arping.c | 499 + busybox/networking/ftpgetput.c | 378 + busybox/networking/hostname.c | 130 + busybox/networking/httpd.c | 2091 +++ busybox/networking/ifconfig.c | 605 + busybox/networking/ifupdown.c | 1463 ++ busybox/networking/inetd.c | 1221 ++ busybox/networking/ip.c | 115 + busybox/networking/ipaddr.c | 27 + busybox/networking/ipcalc.c | 224 + busybox/networking/iplink.c | 27 + busybox/networking/iproute.c | 27 + busybox/networking/iptunnel.c | 27 + busybox/networking/libiproute/Makefile | 32 + busybox/networking/libiproute/Makefile.in | 84 + busybox/networking/libiproute/ip_common.h | 18 + .../libiproute/ip_parse_common_args.c | 76 + busybox/networking/libiproute/ipaddress.c | 825 + busybox/networking/libiproute/iplink.c | 367 + busybox/networking/libiproute/iproute.c | 852 + busybox/networking/libiproute/iptunnel.c | 548 + busybox/networking/libiproute/libnetlink.c | 524 + busybox/networking/libiproute/libnetlink.h | 46 + .../networking/libiproute/linux/pkt_sched.h | 413 + busybox/networking/libiproute/ll_addr.c | 81 + busybox/networking/libiproute/ll_map.c | 164 + busybox/networking/libiproute/ll_map.h | 12 + busybox/networking/libiproute/ll_proto.c | 120 + busybox/networking/libiproute/ll_types.c | 115 + busybox/networking/libiproute/rt_names.c | 385 + busybox/networking/libiproute/rt_names.h | 30 + busybox/networking/libiproute/rtm_map.c | 110 + busybox/networking/libiproute/rtm_map.h | 10 + busybox/networking/libiproute/utils.c | 359 + busybox/networking/libiproute/utils.h | 101 + busybox/networking/nameif.c | 222 + busybox/networking/nc.c | 177 + busybox/networking/netstat.c | 661 + busybox/networking/nslookup.c | 206 + busybox/networking/ping.c | 472 + busybox/networking/ping6.c | 515 + busybox/networking/route.c | 714 + busybox/networking/telnet.c | 769 + busybox/networking/telnetd.c | 660 + busybox/networking/tftp.c | 584 + busybox/networking/traceroute.c | 548 + busybox/networking/udhcp/AUTHORS | 13 + busybox/networking/udhcp/COPYING | 339 + busybox/networking/udhcp/ChangeLog | 260 + busybox/networking/udhcp/Config.in | 62 + busybox/networking/udhcp/Makefile | 32 + busybox/networking/udhcp/Makefile.in | 54 + busybox/networking/udhcp/README | 53 + busybox/networking/udhcp/README.dumpleases | 17 + busybox/networking/udhcp/README.udhcpc | 141 + busybox/networking/udhcp/README.udhcpd | 59 + busybox/networking/udhcp/TODO | 16 + busybox/networking/udhcp/arpping.c | 106 + busybox/networking/udhcp/arpping.h | 35 + busybox/networking/udhcp/clientpacket.c | 248 + busybox/networking/udhcp/clientpacket.h | 14 + busybox/networking/udhcp/clientsocket.c | 62 + busybox/networking/udhcp/clientsocket.h | 7 + busybox/networking/udhcp/common.c | 162 + busybox/networking/udhcp/common.h | 56 + busybox/networking/udhcp/dhcpc.c | 517 + busybox/networking/udhcp/dhcpc.h | 37 + busybox/networking/udhcp/dhcpd.c | 273 + busybox/networking/udhcp/dhcpd.h | 140 + busybox/networking/udhcp/dumpleases.c | 110 + busybox/networking/udhcp/files.c | 346 + busybox/networking/udhcp/files.h | 17 + busybox/networking/udhcp/frontend.c | 16 + busybox/networking/udhcp/leases.c | 158 + busybox/networking/udhcp/leases.h | 23 + busybox/networking/udhcp/libbb_udhcp.h | 54 + busybox/networking/udhcp/options.c | 228 + busybox/networking/udhcp/options.h | 40 + busybox/networking/udhcp/packet.c | 202 + busybox/networking/udhcp/packet.h | 41 + busybox/networking/udhcp/pidfile.c | 75 + busybox/networking/udhcp/pidfile.h | 25 + busybox/networking/udhcp/script.c | 233 + busybox/networking/udhcp/script.h | 6 + busybox/networking/udhcp/serverpacket.c | 275 + busybox/networking/udhcp/serverpacket.h | 12 + busybox/networking/udhcp/signalpipe.c | 78 + busybox/networking/udhcp/signalpipe.h | 22 + busybox/networking/udhcp/socket.c | 132 + busybox/networking/udhcp/socket.h | 8 + busybox/networking/udhcp/static_leases.c | 119 + busybox/networking/udhcp/static_leases.h | 25 + busybox/networking/udhcp/version.h | 6 + busybox/networking/vconfig.c | 184 + busybox/networking/wget.c | 868 + busybox/patches/cmp_n.diff | 377 + busybox/patches/dd_ibs_and_obs.diff | 252 + busybox/patches/eject.diff | 164 + busybox/patches/makdevs_table.diff | 294 + busybox/patches/rpm2cpio_bzip2.patch | 63 + busybox/patches/tftp_timeout_multicast.diff | 1053 ++ busybox/patches/top_system_cpu.diff | 51 + busybox/patches/udhcp_additional_items.diff | 126 + busybox/patches/udhcp_config_paths.diff | 333 + busybox/patches/udhcpd_foreground.diff | 33 + busybox/procps/Config.in | 82 + busybox/procps/Makefile | 32 + busybox/procps/Makefile.in | 43 + busybox/procps/free.c | 84 + busybox/procps/kill.c | 156 + busybox/procps/pidof.c | 71 + busybox/procps/ps.c | 109 + busybox/procps/renice.c | 54 + busybox/procps/sysctl.c | 352 + busybox/procps/top.c | 592 + busybox/procps/uptime.c | 75 + busybox/scripts/.cvsignore | 2 + busybox/scripts/config/.cvsignore | 8 + busybox/scripts/config/Kconfig-language.txt | 255 + busybox/scripts/config/Makefile | 111 + busybox/scripts/config/checklist.c | 372 + busybox/scripts/config/colors.h | 161 + busybox/scripts/config/conf.c | 583 + busybox/scripts/config/confdata.c | 447 + busybox/scripts/config/dialog.h | 196 + busybox/scripts/config/expr.c | 1089 ++ busybox/scripts/config/expr.h | 193 + busybox/scripts/config/inputbox.c | 240 + busybox/scripts/config/lex.zconf.c_shipped | 3688 +++++ busybox/scripts/config/lkc.h | 113 + busybox/scripts/config/lkc_proto.h | 39 + busybox/scripts/config/mconf.c | 713 + busybox/scripts/config/menu.c | 431 + busybox/scripts/config/menubox.c | 436 + busybox/scripts/config/msgbox.c | 85 + busybox/scripts/config/symbol.c | 771 + busybox/scripts/config/textbox.c | 556 + busybox/scripts/config/util.c | 375 + busybox/scripts/config/yesno.c | 118 + busybox/scripts/config/zconf.l | 366 + busybox/scripts/config/zconf.tab.c_shipped | 2127 +++ busybox/scripts/config/zconf.tab.h_shipped | 125 + busybox/scripts/config/zconf.y | 687 + busybox/scripts/mkdep.c | 628 + busybox/scripts/split-include.c | 226 + busybox/shell/Config.in | 229 + busybox/shell/Makefile | 32 + busybox/shell/Makefile.in | 40 + busybox/shell/ash.c | 13586 ++++++++++++++++ busybox/shell/cmdedit.c | 1582 ++ busybox/shell/cmdedit.h | 15 + busybox/shell/hush.c | 2963 ++++ busybox/shell/lash.c | 1696 ++ busybox/shell/msh.c | 5487 +++++++ busybox/sysdeps/linux/Config.in | 294 + busybox/sysdeps/linux/defconfig | 421 + busybox/sysklogd/Config.in | 109 + busybox/sysklogd/Makefile | 32 + busybox/sysklogd/Makefile.in | 39 + busybox/sysklogd/klogd.c | 165 + busybox/sysklogd/logger.c | 204 + busybox/sysklogd/logread.c | 186 + busybox/sysklogd/syslogd.c | 712 + busybox/testsuite/README | 31 + busybox/testsuite/TODO | 18 + ...sename-does-not-remove-identical-extension | 1 + busybox/testsuite/basename/basename-works | 2 + .../bunzip2/bunzip2-reads-from-standard-input | 2 + .../bunzip2/bunzip2-removes-compressed-file | 3 + .../bzcat-does-not-remove-compressed-file | 3 + busybox/testsuite/cat/cat-prints-a-file | 3 + .../cat/cat-prints-a-file-and-standard-input | 7 + busybox/testsuite/cmp/cmp-detects-difference | 9 + busybox/testsuite/cp/cp-a-files-to-dir | 14 + busybox/testsuite/cp/cp-a-preserves-links | 5 + busybox/testsuite/cp/cp-copies-empty-file | 3 + busybox/testsuite/cp/cp-copies-large-file | 3 + busybox/testsuite/cp/cp-copies-small-file | 3 + busybox/testsuite/cp/cp-d-files-to-dir | 11 + busybox/testsuite/cp/cp-dir-create-dir | 4 + busybox/testsuite/cp/cp-dir-existing-dir | 5 + .../cp/cp-does-not-copy-unreadable-file | 6 + busybox/testsuite/cp/cp-files-to-dir | 11 + busybox/testsuite/cp/cp-follows-links | 4 + busybox/testsuite/cp/cp-preserves-hard-links | 6 + busybox/testsuite/cp/cp-preserves-links | 5 + busybox/testsuite/cp/cp-preserves-source-file | 3 + busybox/testsuite/cut/cut-cuts-a-character | 1 + busybox/testsuite/cut/cut-cuts-a-closed-range | 1 + busybox/testsuite/cut/cut-cuts-a-field | 1 + busybox/testsuite/cut/cut-cuts-an-open-range | 1 + .../testsuite/cut/cut-cuts-an-unclosed-range | 1 + busybox/testsuite/date/date-R-works | 2 + busybox/testsuite/date/date-format-works | 1 + busybox/testsuite/date/date-u-works | 2 + busybox/testsuite/date/date-works | 2 + busybox/testsuite/dd/dd-accepts-if | 2 + busybox/testsuite/dd/dd-accepts-of | 2 + ...ies-from-standard-input-to-standard-output | 1 + .../dd/dd-prints-count-to-standard-error | 2 + .../dirname/dirname-handles-absolute-path | 1 + .../dirname/dirname-handles-empty-path | 1 + .../dirname/dirname-handles-multiple-slashes | 1 + .../dirname/dirname-handles-relative-path | 1 + .../testsuite/dirname/dirname-handles-root | 1 + .../dirname/dirname-handles-single-component | 1 + busybox/testsuite/dirname/dirname-works | 2 + busybox/testsuite/du/du-h-works | 4 + busybox/testsuite/du/du-k-works | 4 + busybox/testsuite/du/du-l-works | 4 + busybox/testsuite/du/du-m-works | 4 + busybox/testsuite/du/du-s-works | 4 + busybox/testsuite/du/du-works | 4 + .../echo/echo-does-not-print-newline | 1 + busybox/testsuite/echo/echo-prints-argument | 1 + busybox/testsuite/echo/echo-prints-arguments | 1 + busybox/testsuite/echo/echo-prints-newline | 1 + busybox/testsuite/expr/expr-works | 59 + busybox/testsuite/false/false-is-silent | 1 + busybox/testsuite/false/false-returns-failure | 1 + .../testsuite/find/find-supports-minus-xdev | 1 + .../grep/egrep-is-not-case-insensitive | 2 + .../grep/egrep-supports-extended-regexps | 2 + .../testsuite/grep/grep-handles-binary-files | 1 + .../grep/grep-handles-multiple-regexps | 1 + busybox/testsuite/grep/grep-is-also-egrep | 2 + busybox/testsuite/grep/grep-matches-NUL | 8 + .../gunzip/gunzip-reads-from-standard-input | 2 + .../gzip/gzip-accepts-multiple-files | 3 + .../testsuite/gzip/gzip-accepts-single-minus | 1 + .../testsuite/gzip/gzip-removes-original-file | 3 + busybox/testsuite/head/head-n-works | 4 + busybox/testsuite/head/head-works | 4 + busybox/testsuite/hostid/hostid-works | 2 + busybox/testsuite/hostname/hostname-d-works | 2 + busybox/testsuite/hostname/hostname-i-works | 2 + busybox/testsuite/hostname/hostname-s-works | 1 + busybox/testsuite/hostname/hostname-works | 1 + busybox/testsuite/id/id-g-works | 1 + busybox/testsuite/id/id-u-works | 1 + busybox/testsuite/id/id-un-works | 1 + busybox/testsuite/id/id-ur-works | 1 + busybox/testsuite/ln/ln-creates-hard-links | 4 + busybox/testsuite/ln/ln-creates-soft-links | 4 + .../testsuite/ln/ln-force-creates-hard-links | 5 + .../testsuite/ln/ln-force-creates-soft-links | 5 + busybox/testsuite/ln/ln-preserves-hard-links | 8 + busybox/testsuite/ln/ln-preserves-soft-links | 9 + busybox/testsuite/ls/ls-1-works | 4 + busybox/testsuite/ls/ls-h-works | 4 + busybox/testsuite/ls/ls-l-works | 4 + busybox/testsuite/ls/ls-s-works | 4 + .../md5sum/md5sum-verifies-non-binary-file | 3 + .../testsuite/mkdir/mkdir-makes-a-directory | 2 + .../mkdir/mkdir-makes-parent-directories | 2 + ...msh-supports-underscores-in-variable-names | 1 + busybox/testsuite/mv/mv-files-to-dir | 16 + busybox/testsuite/mv/mv-follows-links | 4 + busybox/testsuite/mv/mv-moves-empty-file | 4 + busybox/testsuite/mv/mv-moves-file | 3 + busybox/testsuite/mv/mv-moves-hardlinks | 4 + busybox/testsuite/mv/mv-moves-large-file | 4 + busybox/testsuite/mv/mv-moves-small-file | 4 + busybox/testsuite/mv/mv-moves-symlinks | 6 + .../testsuite/mv/mv-moves-unreadable-files | 5 + busybox/testsuite/mv/mv-preserves-hard-links | 6 + busybox/testsuite/mv/mv-preserves-links | 5 + .../testsuite/mv/mv-refuses-mv-dir-to-subdir | 23 + busybox/testsuite/mv/mv-removes-source-file | 4 + .../pwd/pwd-prints-working-directory | 1 + busybox/testsuite/rm/rm-removes-file | 3 + .../rmdir/rmdir-removes-parent-directories | 3 + busybox/testsuite/runtest | 102 + .../sed/sed-accepts-blanks-before-command | 1 + busybox/testsuite/sed/sed-aic-commands | 134 + .../sed-append-hold-space-to-pattern-space | 13 + busybox/testsuite/sed/sed-append-next-line | 19 + busybox/testsuite/sed/sed-branch | 1 + busybox/testsuite/sed/sed-branch-conditional | 15 + busybox/testsuite/sed/sed-branch-conditional2 | 11 + busybox/testsuite/sed/sed-branch-no-label | 1 + busybox/testsuite/sed/sed-chains-substs | 1 + busybox/testsuite/sed/sed-chains-substs2 | 1 + .../sed-does-not-substitute-in-deleted-line | 2 + .../sed/sed-handles-embedded-slashes | 1 + busybox/testsuite/sed/sed-handles-empty-lines | 1 + .../sed/sed-handles-unsatisfied-backrefs | 6 + busybox/testsuite/sed/sed-next-line | 12 + .../sed-prints-line-once-for-multiple-substs | 4 + busybox/testsuite/sed/sed-recurses-properly | 1 + busybox/testsuite/sed/sed-regex-match-newline | 10 + .../sed-splits-edit-commands-on-command-line | 9 + busybox/testsuite/sed/sed-subst-subprint | 9 + busybox/testsuite/sed/sed-write-to-stdout | 10 + busybox/testsuite/sort/sort-n-works | 4 + busybox/testsuite/sort/sort-r-works | 4 + busybox/testsuite/sort/sort-works | 4 + .../testsuite/strings/strings-works-like-GNU | 9 + busybox/testsuite/tail/tail-n-works | 4 + busybox/testsuite/tail/tail-works | 4 + .../testsuite/tar/tar-archives-multiple-files | 6 + .../tar/tar-complains-about-missing-file | 3 + .../tar/tar-demands-at-least-one-ctx | 1 + .../testsuite/tar/tar-demands-at-most-one-ctx | 1 + busybox/testsuite/tar/tar-extracts-file | 5 + .../tar/tar-extracts-from-standard-input | 5 + .../testsuite/tar/tar-extracts-multiple-files | 6 + .../tar/tar-extracts-to-standard-output | 3 + busybox/testsuite/tar/tar-handles-cz-options | 5 + ...s-empty-include-and-non-empty-exclude-list | 6 + .../tar/tar-handles-exclude-and-extract-lists | 8 + .../tar/tar-handles-multiple-X-options | 10 + .../testsuite/tar/tar-handles-nested-exclude | 9 + busybox/testsuite/tee/tee-appends-input | 5 + busybox/testsuite/tee/tee-tees-input | 3 + busybox/testsuite/touch/touch-creates-file | 2 + .../touch/touch-does-not-create-file | 2 + ...ouch-touches-files-after-non-existent-file | 3 + busybox/testsuite/tr/tr-d-works | 4 + busybox/testsuite/tr/tr-non-gnu | 1 + busybox/testsuite/tr/tr-works | 9 + busybox/testsuite/true/true-is-silent | 1 + busybox/testsuite/true/true-returns-success | 1 + busybox/testsuite/uptime/uptime-works | 2 + ...uencode-sets-standard-input-mode-correctly | 4 + busybox/testsuite/wc/wc-counts-all | 1 + busybox/testsuite/wc/wc-counts-characters | 1 + busybox/testsuite/wc/wc-counts-lines | 1 + busybox/testsuite/wc/wc-counts-words | 1 + .../wc/wc-prints-longest-line-length | 1 + busybox/testsuite/wget/wget--O-overrides--P | 3 + .../testsuite/wget/wget-handles-empty-path | 1 + .../wget/wget-retrieves-google-index | 2 + busybox/testsuite/wget/wget-supports--P | 3 + .../testsuite/which/which-uses-default-path | 4 + busybox/testsuite/xargs/xargs-works | 4 + busybox/util-linux/Config.in | 357 + busybox/util-linux/Makefile | 32 + busybox/util-linux/Makefile.in | 66 + busybox/util-linux/dmesg.c | 99 + busybox/util-linux/fbset.c | 427 + busybox/util-linux/fdflush.c | 54 + busybox/util-linux/fdformat.c | 161 + busybox/util-linux/fdisk.c | 5880 +++++++ busybox/util-linux/freeramdisk.c | 69 + busybox/util-linux/fsck_minix.c | 1476 ++ busybox/util-linux/getopt.c | 391 + busybox/util-linux/hexdump.c | 142 + busybox/util-linux/hwclock.c | 230 + busybox/util-linux/losetup.c | 59 + busybox/util-linux/mkfs_minix.c | 852 + busybox/util-linux/mkswap.c | 418 + busybox/util-linux/more.c | 211 + busybox/util-linux/mount.c | 497 + busybox/util-linux/nfsmount.c | 1026 ++ busybox/util-linux/nfsmount.h | 242 + busybox/util-linux/pivot_root.c | 35 + busybox/util-linux/rdate.c | 121 + busybox/util-linux/swaponoff.c | 120 + busybox/util-linux/umount.c | 298 + 785 files changed, 168766 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/INSTALL create mode 100644 busybox/LICENSE create mode 100644 busybox/Makefile create mode 100644 busybox/README create mode 100644 busybox/Rules.mak create mode 100644 busybox/TODO create mode 100644 busybox/applets/Makefile create mode 100644 busybox/applets/Makefile.in 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/install.sh create mode 100644 busybox/archival/Config.in create mode 100644 busybox/archival/Makefile create mode 100644 busybox/archival/Makefile.in create mode 100644 busybox/archival/ar.c create mode 100644 busybox/archival/bunzip2.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/Makefile create mode 100644 busybox/archival/libunarchive/Makefile.in create mode 100644 busybox/archival/libunarchive/archive_xread_all.c create mode 100644 busybox/archival/libunarchive/archive_xread_all_eof.c create mode 100644 busybox/archival/libunarchive/check_header_gzip.c create mode 100644 busybox/archival/libunarchive/data_align.c create mode 100644 busybox/archival/libunarchive/data_extract_all.c create mode 100644 busybox/archival/libunarchive/data_extract_to_buffer.c create mode 100644 busybox/archival/libunarchive/data_extract_to_stdout.c create mode 100644 busybox/archival/libunarchive/data_skip.c create mode 100644 busybox/archival/libunarchive/decompress_bunzip2.c create mode 100644 busybox/archival/libunarchive/decompress_uncompress.c create mode 100644 busybox/archival/libunarchive/decompress_unzip.c create mode 100644 busybox/archival/libunarchive/filter_accept_all.c create mode 100644 busybox/archival/libunarchive/filter_accept_list.c create mode 100644 busybox/archival/libunarchive/filter_accept_list_reassign.c create mode 100644 busybox/archival/libunarchive/filter_accept_reject_list.c create mode 100644 busybox/archival/libunarchive/find_list_entry.c create mode 100644 busybox/archival/libunarchive/get_header_ar.c create mode 100644 busybox/archival/libunarchive/get_header_cpio.c create mode 100644 busybox/archival/libunarchive/get_header_tar.c create mode 100644 busybox/archival/libunarchive/get_header_tar_bz2.c create mode 100644 busybox/archival/libunarchive/get_header_tar_gz.c create mode 100644 busybox/archival/libunarchive/header_list.c create mode 100644 busybox/archival/libunarchive/header_skip.c create mode 100644 busybox/archival/libunarchive/header_verbose_list.c create mode 100644 busybox/archival/libunarchive/init_handle.c create mode 100644 busybox/archival/libunarchive/open_transformer.c create mode 100644 busybox/archival/libunarchive/seek_by_char.c create mode 100644 busybox/archival/libunarchive/seek_by_jump.c create mode 100644 busybox/archival/libunarchive/unpack_ar_archive.c create mode 100644 busybox/archival/rpm.c create mode 100644 busybox/archival/rpm2cpio.c create mode 100644 busybox/archival/tar.c create mode 100644 busybox/archival/uncompress.c create mode 100644 busybox/archival/unzip.c create mode 100644 busybox/console-tools/Config.in create mode 100644 busybox/console-tools/Makefile create mode 100644 busybox/console-tools/Makefile.in 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/loadfont.c create mode 100644 busybox/console-tools/loadkmap.c create mode 100644 busybox/console-tools/openvt.c create mode 100644 busybox/console-tools/reset.c create mode 100644 busybox/console-tools/setkeycodes.c create mode 100644 busybox/coreutils/Config.in create mode 100644 busybox/coreutils/Makefile create mode 100644 busybox/coreutils/Makefile.in create mode 100644 busybox/coreutils/basename.c create mode 100644 busybox/coreutils/cal.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/false.c create mode 100644 busybox/coreutils/fold.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/install.c create mode 100644 busybox/coreutils/length.c create mode 100644 busybox/coreutils/libcoreutils/Makefile create mode 100644 busybox/coreutils/libcoreutils/Makefile.in create mode 100644 busybox/coreutils/libcoreutils/coreutils.h create mode 100644 busybox/coreutils/libcoreutils/cp_mv_stat.c create mode 100644 busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c create mode 100644 busybox/coreutils/libcoreutils/xgetoptfile_sort_uniq.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/md5_sha1_sum.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/od.c create mode 100644 busybox/coreutils/printf.c create mode 100644 busybox/coreutils/pwd.c create mode 100644 busybox/coreutils/realpath.c create mode 100644 busybox/coreutils/rm.c create mode 100644 busybox/coreutils/rmdir.c create mode 100644 busybox/coreutils/seq.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/true.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/watch.c create mode 100644 busybox/coreutils/wc.c create mode 100644 busybox/coreutils/who.c create mode 100644 busybox/coreutils/whoami.c create mode 100644 busybox/coreutils/yes.c create mode 100644 busybox/debian/busybox-cvs-doc.docs create mode 100644 busybox/debian/busybox-cvs-static.dirs create mode 100644 busybox/debian/busybox-cvs-static.manpages create mode 100644 busybox/debian/busybox-cvs-static.override create mode 100644 busybox/debian/busybox-cvs.dirs create mode 100644 busybox/debian/busybox-cvs.manpages create mode 100644 busybox/debian/changelog create mode 100644 busybox/debian/compat create mode 100644 busybox/debian/config-deb create mode 100644 busybox/debian/config-floppy-udeb-linux create mode 100644 busybox/debian/config-static create mode 100644 busybox/debian/config-udeb create mode 100644 busybox/debian/config-udeb-linux create mode 100644 busybox/debian/control create mode 100644 busybox/debian/control-extract create mode 100644 busybox/debian/copyright create mode 100755 busybox/debian/rules create mode 100644 busybox/debianutils/Config.in create mode 100644 busybox/debianutils/Makefile create mode 100644 busybox/debianutils/Makefile.in create mode 100644 busybox/debianutils/mktemp.c create mode 100644 busybox/debianutils/pipe_progress.c create mode 100644 busybox/debianutils/readlink.c create mode 100644 busybox/debianutils/run_parts.c create mode 100644 busybox/debianutils/start_stop_daemon.c create mode 100644 busybox/debianutils/which.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/FAQ.html create mode 100644 busybox/docs/busybox.net/about.html create mode 100644 busybox/docs/busybox.net/busybox-growth.ps create mode 100644 busybox/docs/busybox.net/copyright.txt create mode 100644 busybox/docs/busybox.net/cvs_anon.html create mode 100644 busybox/docs/busybox.net/cvs_howto.html create mode 100644 busybox/docs/busybox.net/cvs_write.html create mode 100644 busybox/docs/busybox.net/docs.html create mode 100644 busybox/docs/busybox.net/download.html create mode 100644 busybox/docs/busybox.net/footer.html create mode 100644 busybox/docs/busybox.net/header.html create mode 100644 busybox/docs/busybox.net/images/back.png create mode 100644 busybox/docs/busybox.net/images/busybox.jpeg create mode 100644 busybox/docs/busybox.net/images/busybox.png create mode 100644 busybox/docs/busybox.net/images/busybox1.png create mode 100644 busybox/docs/busybox.net/images/busybox2.jpg create mode 100644 busybox/docs/busybox.net/images/busybox3.jpg create mode 100644 busybox/docs/busybox.net/images/dir.png create mode 100644 busybox/docs/busybox.net/images/donate.png 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/text.png create mode 100644 busybox/docs/busybox.net/images/vh40.gif 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/license.html create mode 100644 busybox/docs/busybox.net/lists.html create mode 100644 busybox/docs/busybox.net/news.html create mode 100644 busybox/docs/busybox.net/oldnews.html create mode 100644 busybox/docs/busybox.net/products.html create mode 100644 busybox/docs/busybox.net/screenshot.html create mode 100644 busybox/docs/busybox.net/shame.html 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/editors/Config.in create mode 100644 busybox/editors/Makefile create mode 100644 busybox/editors/Makefile.in create mode 100644 busybox/editors/awk.c create mode 100644 busybox/editors/patch.c create mode 100644 busybox/editors/sed.c create mode 100644 busybox/editors/vi.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/devfsd.conf create mode 100644 busybox/examples/inetd.conf create mode 100644 busybox/examples/inittab create mode 100755 busybox/examples/mk2knr.pl create mode 100755 busybox/examples/udhcp/sample.bound create mode 100755 busybox/examples/udhcp/sample.deconfig create mode 100755 busybox/examples/udhcp/sample.nak create mode 100755 busybox/examples/udhcp/sample.renew create mode 100644 busybox/examples/udhcp/sample.script create mode 100644 busybox/examples/udhcp/simple.script create mode 100644 busybox/examples/udhcp/udhcpd.conf create mode 100644 busybox/examples/undeb create mode 100644 busybox/examples/unrpm create mode 100644 busybox/findutils/Config.in create mode 100644 busybox/findutils/Makefile create mode 100644 busybox/findutils/Makefile.in create mode 100644 busybox/findutils/find.c create mode 100644 busybox/findutils/grep.c create mode 100644 busybox/findutils/xargs.c create mode 100644 busybox/include/.cvsignore create mode 100644 busybox/include/applets.h create mode 100644 busybox/include/busybox.h create mode 100644 busybox/include/dump.h create mode 100644 busybox/include/grp_.h create mode 100644 busybox/include/inet_common.h create mode 100644 busybox/include/libbb.h create mode 100644 busybox/include/pwd_.h create mode 100644 busybox/include/shadow_.h create mode 100644 busybox/include/unarchive.h create mode 100644 busybox/include/usage.h create mode 100644 busybox/init/Config.in create mode 100644 busybox/init/Makefile create mode 100644 busybox/init/Makefile.in create mode 100644 busybox/init/halt.c create mode 100644 busybox/init/init.c create mode 100644 busybox/init/init_shared.c create mode 100644 busybox/init/init_shared.h create mode 100644 busybox/init/mesg.c create mode 100644 busybox/init/poweroff.c create mode 100644 busybox/init/reboot.c create mode 100644 busybox/libbb/.cvsignore create mode 100644 busybox/libbb/Makefile create mode 100644 busybox/libbb/Makefile.in create mode 100644 busybox/libbb/README create mode 100644 busybox/libbb/ask_confirmation.c create mode 100644 busybox/libbb/bb_askpass.c create mode 100644 busybox/libbb/bb_asprintf.c create mode 100644 busybox/libbb/change_identity.c create mode 100644 busybox/libbb/chomp.c create mode 100644 busybox/libbb/compare_string_array.c create mode 100644 busybox/libbb/concat_path_file.c create mode 100644 busybox/libbb/concat_subpath_file.c create mode 100644 busybox/libbb/copy_file.c create mode 100644 busybox/libbb/copyfd.c create mode 100644 busybox/libbb/correct_password.c create mode 100644 busybox/libbb/create_icmp6_socket.c create mode 100644 busybox/libbb/create_icmp_socket.c create mode 100644 busybox/libbb/default_error_retval.c create mode 100644 busybox/libbb/device_open.c create mode 100644 busybox/libbb/dump.c create mode 100644 busybox/libbb/error_msg.c create mode 100644 busybox/libbb/error_msg_and_die.c create mode 100644 busybox/libbb/fclose_nonstdin.c create mode 100644 busybox/libbb/fflush_stdout_and_exit.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/get_terminal_width_height.c create mode 100644 busybox/libbb/get_ug_id.c create mode 100644 busybox/libbb/getopt_ulflags.c create mode 100644 busybox/libbb/hash_fd.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/inet_common.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/llist_add_to.c create mode 100644 busybox/libbb/login.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 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_getpwuid.c create mode 100644 busybox/libbb/my_getug.c create mode 100644 busybox/libbb/obscure.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/perror_nomsg.c create mode 100644 busybox/libbb/perror_nomsg_and_die.c create mode 100644 busybox/libbb/print_file.c create mode 100644 busybox/libbb/printf.c create mode 100644 busybox/libbb/process_escape_sequence.c create mode 100644 busybox/libbb/procps.c create mode 100644 busybox/libbb/pw_encrypt.c create mode 100644 busybox/libbb/pwd2spwd.c create mode 100644 busybox/libbb/qmodule.c create mode 100644 busybox/libbb/read_package_field.c create mode 100644 busybox/libbb/recursive_action.c create mode 100644 busybox/libbb/remove_file.c create mode 100644 busybox/libbb/restricted_shell.c create mode 100644 busybox/libbb/run_parts.c create mode 100644 busybox/libbb/run_shell.c create mode 100644 busybox/libbb/safe_read.c create mode 100644 busybox/libbb/safe_strncpy.c create mode 100644 busybox/libbb/safe_strtol.c create mode 100644 busybox/libbb/safe_write.c create mode 100644 busybox/libbb/setup_environment.c create mode 100644 busybox/libbb/simplify_path.c create mode 100644 busybox/libbb/skip_whitespace.c create mode 100644 busybox/libbb/speed_table.c create mode 100644 busybox/libbb/syscalls.c create mode 100644 busybox/libbb/syslog_msg_with_name.c create mode 100644 busybox/libbb/trim.c create mode 100644 busybox/libbb/u_signal_names.c create mode 100644 busybox/libbb/vdprintf.c create mode 100644 busybox/libbb/verror_msg.c create mode 100644 busybox/libbb/vfork_daemon_rexec.c create mode 100644 busybox/libbb/vherror_msg.c create mode 100644 busybox/libbb/vperror_msg.c create mode 100644 busybox/libbb/warn_ignoring_args.c create mode 100644 busybox/libbb/wfopen.c create mode 100644 busybox/libbb/wfopen_input.c create mode 100644 busybox/libbb/xconnect.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/xgethostbyname2.c create mode 100644 busybox/libbb/xgetlarg.c create mode 100644 busybox/libbb/xgetularg.c create mode 100644 busybox/libbb/xreadlink.c create mode 100644 busybox/libbb/xregcomp.c create mode 100644 busybox/libpwdgrp/Makefile create mode 100644 busybox/libpwdgrp/Makefile.in create mode 100644 busybox/libpwdgrp/pwd_grp.c create mode 100644 busybox/loginutils/Config.in create mode 100644 busybox/loginutils/Makefile create mode 100644 busybox/loginutils/Makefile.in create mode 100644 busybox/loginutils/addgroup.c create mode 100644 busybox/loginutils/adduser.c create mode 100644 busybox/loginutils/delgroup.c create mode 100644 busybox/loginutils/delline.c create mode 100644 busybox/loginutils/deluser.c create mode 100644 busybox/loginutils/getty.c create mode 100644 busybox/loginutils/login.c create mode 100644 busybox/loginutils/passwd.c create mode 100644 busybox/loginutils/su.c create mode 100644 busybox/loginutils/sulogin.c create mode 100644 busybox/loginutils/vlock.c create mode 100644 busybox/miscutils/Config.in create mode 100644 busybox/miscutils/Makefile create mode 100644 busybox/miscutils/Makefile.in create mode 100644 busybox/miscutils/adjtimex.c create mode 100644 busybox/miscutils/crond.c create mode 100644 busybox/miscutils/crontab.c create mode 100644 busybox/miscutils/dc.c create mode 100644 busybox/miscutils/devfsd.c create mode 100644 busybox/miscutils/hdparm.c create mode 100644 busybox/miscutils/last.c create mode 100644 busybox/miscutils/makedevs.c create mode 100644 busybox/miscutils/mt.c create mode 100644 busybox/miscutils/rx.c create mode 100644 busybox/miscutils/strings.c create mode 100644 busybox/miscutils/time.c create mode 100644 busybox/miscutils/watchdog.c create mode 100644 busybox/modutils/Config.in create mode 100644 busybox/modutils/Makefile create mode 100644 busybox/modutils/Makefile.in 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/networking/Config.in create mode 100644 busybox/networking/Makefile create mode 100644 busybox/networking/Makefile.in create mode 100644 busybox/networking/arping.c create mode 100644 busybox/networking/ftpgetput.c create mode 100644 busybox/networking/hostname.c create mode 100644 busybox/networking/httpd.c create mode 100644 busybox/networking/ifconfig.c create mode 100644 busybox/networking/ifupdown.c create mode 100644 busybox/networking/inetd.c create mode 100644 busybox/networking/ip.c create mode 100644 busybox/networking/ipaddr.c create mode 100644 busybox/networking/ipcalc.c create mode 100644 busybox/networking/iplink.c create mode 100644 busybox/networking/iproute.c create mode 100644 busybox/networking/iptunnel.c create mode 100644 busybox/networking/libiproute/Makefile create mode 100644 busybox/networking/libiproute/Makefile.in create mode 100644 busybox/networking/libiproute/ip_common.h create mode 100644 busybox/networking/libiproute/ip_parse_common_args.c create mode 100644 busybox/networking/libiproute/ipaddress.c create mode 100644 busybox/networking/libiproute/iplink.c create mode 100644 busybox/networking/libiproute/iproute.c create mode 100644 busybox/networking/libiproute/iptunnel.c create mode 100644 busybox/networking/libiproute/libnetlink.c create mode 100644 busybox/networking/libiproute/libnetlink.h create mode 100644 busybox/networking/libiproute/linux/pkt_sched.h create mode 100644 busybox/networking/libiproute/ll_addr.c create mode 100644 busybox/networking/libiproute/ll_map.c create mode 100644 busybox/networking/libiproute/ll_map.h create mode 100644 busybox/networking/libiproute/ll_proto.c create mode 100644 busybox/networking/libiproute/ll_types.c create mode 100644 busybox/networking/libiproute/rt_names.c create mode 100644 busybox/networking/libiproute/rt_names.h create mode 100644 busybox/networking/libiproute/rtm_map.c create mode 100644 busybox/networking/libiproute/rtm_map.h create mode 100644 busybox/networking/libiproute/utils.c create mode 100644 busybox/networking/libiproute/utils.h create mode 100644 busybox/networking/nameif.c create mode 100644 busybox/networking/nc.c create mode 100644 busybox/networking/netstat.c create mode 100644 busybox/networking/nslookup.c create mode 100644 busybox/networking/ping.c create mode 100644 busybox/networking/ping6.c create mode 100644 busybox/networking/route.c create mode 100644 busybox/networking/telnet.c create mode 100644 busybox/networking/telnetd.c create mode 100644 busybox/networking/tftp.c create mode 100644 busybox/networking/traceroute.c create mode 100644 busybox/networking/udhcp/AUTHORS create mode 100644 busybox/networking/udhcp/COPYING create mode 100644 busybox/networking/udhcp/ChangeLog create mode 100644 busybox/networking/udhcp/Config.in create mode 100644 busybox/networking/udhcp/Makefile create mode 100644 busybox/networking/udhcp/Makefile.in create mode 100644 busybox/networking/udhcp/README create mode 100644 busybox/networking/udhcp/README.dumpleases create mode 100644 busybox/networking/udhcp/README.udhcpc create mode 100644 busybox/networking/udhcp/README.udhcpd create mode 100644 busybox/networking/udhcp/TODO create mode 100644 busybox/networking/udhcp/arpping.c create mode 100644 busybox/networking/udhcp/arpping.h create mode 100644 busybox/networking/udhcp/clientpacket.c create mode 100644 busybox/networking/udhcp/clientpacket.h create mode 100644 busybox/networking/udhcp/clientsocket.c create mode 100644 busybox/networking/udhcp/clientsocket.h create mode 100644 busybox/networking/udhcp/common.c create mode 100644 busybox/networking/udhcp/common.h create mode 100644 busybox/networking/udhcp/dhcpc.c create mode 100644 busybox/networking/udhcp/dhcpc.h create mode 100644 busybox/networking/udhcp/dhcpd.c create mode 100644 busybox/networking/udhcp/dhcpd.h create mode 100644 busybox/networking/udhcp/dumpleases.c create mode 100644 busybox/networking/udhcp/files.c create mode 100644 busybox/networking/udhcp/files.h create mode 100644 busybox/networking/udhcp/frontend.c create mode 100644 busybox/networking/udhcp/leases.c create mode 100644 busybox/networking/udhcp/leases.h create mode 100644 busybox/networking/udhcp/libbb_udhcp.h create mode 100644 busybox/networking/udhcp/options.c create mode 100644 busybox/networking/udhcp/options.h create mode 100644 busybox/networking/udhcp/packet.c create mode 100644 busybox/networking/udhcp/packet.h create mode 100644 busybox/networking/udhcp/pidfile.c create mode 100644 busybox/networking/udhcp/pidfile.h create mode 100644 busybox/networking/udhcp/script.c create mode 100644 busybox/networking/udhcp/script.h create mode 100644 busybox/networking/udhcp/serverpacket.c create mode 100644 busybox/networking/udhcp/serverpacket.h create mode 100644 busybox/networking/udhcp/signalpipe.c create mode 100644 busybox/networking/udhcp/signalpipe.h create mode 100644 busybox/networking/udhcp/socket.c create mode 100644 busybox/networking/udhcp/socket.h create mode 100644 busybox/networking/udhcp/static_leases.c create mode 100644 busybox/networking/udhcp/static_leases.h create mode 100644 busybox/networking/udhcp/version.h create mode 100644 busybox/networking/vconfig.c create mode 100644 busybox/networking/wget.c create mode 100644 busybox/patches/cmp_n.diff create mode 100644 busybox/patches/dd_ibs_and_obs.diff create mode 100644 busybox/patches/eject.diff create mode 100644 busybox/patches/makdevs_table.diff create mode 100644 busybox/patches/rpm2cpio_bzip2.patch create mode 100644 busybox/patches/tftp_timeout_multicast.diff create mode 100644 busybox/patches/top_system_cpu.diff create mode 100644 busybox/patches/udhcp_additional_items.diff create mode 100644 busybox/patches/udhcp_config_paths.diff create mode 100644 busybox/patches/udhcpd_foreground.diff create mode 100644 busybox/procps/Config.in create mode 100644 busybox/procps/Makefile create mode 100644 busybox/procps/Makefile.in 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/sysctl.c create mode 100644 busybox/procps/top.c create mode 100644 busybox/procps/uptime.c create mode 100644 busybox/scripts/.cvsignore create mode 100644 busybox/scripts/config/.cvsignore create mode 100644 busybox/scripts/config/Kconfig-language.txt create mode 100644 busybox/scripts/config/Makefile create mode 100644 busybox/scripts/config/checklist.c create mode 100644 busybox/scripts/config/colors.h create mode 100644 busybox/scripts/config/conf.c create mode 100644 busybox/scripts/config/confdata.c create mode 100644 busybox/scripts/config/dialog.h create mode 100644 busybox/scripts/config/expr.c create mode 100644 busybox/scripts/config/expr.h create mode 100644 busybox/scripts/config/inputbox.c create mode 100644 busybox/scripts/config/lex.zconf.c_shipped create mode 100644 busybox/scripts/config/lkc.h create mode 100644 busybox/scripts/config/lkc_proto.h create mode 100644 busybox/scripts/config/mconf.c create mode 100644 busybox/scripts/config/menu.c create mode 100644 busybox/scripts/config/menubox.c create mode 100644 busybox/scripts/config/msgbox.c create mode 100644 busybox/scripts/config/symbol.c create mode 100644 busybox/scripts/config/textbox.c create mode 100644 busybox/scripts/config/util.c create mode 100644 busybox/scripts/config/yesno.c create mode 100644 busybox/scripts/config/zconf.l create mode 100644 busybox/scripts/config/zconf.tab.c_shipped create mode 100644 busybox/scripts/config/zconf.tab.h_shipped create mode 100644 busybox/scripts/config/zconf.y create mode 100644 busybox/scripts/mkdep.c create mode 100644 busybox/scripts/split-include.c create mode 100644 busybox/shell/Config.in create mode 100644 busybox/shell/Makefile create mode 100644 busybox/shell/Makefile.in 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/sysdeps/linux/Config.in create mode 100644 busybox/sysdeps/linux/defconfig create mode 100644 busybox/sysklogd/Config.in create mode 100644 busybox/sysklogd/Makefile create mode 100644 busybox/sysklogd/Makefile.in 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/testsuite/README create mode 100644 busybox/testsuite/TODO create mode 100644 busybox/testsuite/basename/basename-does-not-remove-identical-extension create mode 100644 busybox/testsuite/basename/basename-works create mode 100644 busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input create mode 100644 busybox/testsuite/bunzip2/bunzip2-removes-compressed-file create mode 100644 busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file create mode 100644 busybox/testsuite/cat/cat-prints-a-file create mode 100644 busybox/testsuite/cat/cat-prints-a-file-and-standard-input create mode 100644 busybox/testsuite/cmp/cmp-detects-difference create mode 100644 busybox/testsuite/cp/cp-a-files-to-dir create mode 100644 busybox/testsuite/cp/cp-a-preserves-links create mode 100644 busybox/testsuite/cp/cp-copies-empty-file create mode 100644 busybox/testsuite/cp/cp-copies-large-file create mode 100644 busybox/testsuite/cp/cp-copies-small-file create mode 100644 busybox/testsuite/cp/cp-d-files-to-dir create mode 100644 busybox/testsuite/cp/cp-dir-create-dir create mode 100644 busybox/testsuite/cp/cp-dir-existing-dir create mode 100644 busybox/testsuite/cp/cp-does-not-copy-unreadable-file create mode 100644 busybox/testsuite/cp/cp-files-to-dir create mode 100644 busybox/testsuite/cp/cp-follows-links create mode 100644 busybox/testsuite/cp/cp-preserves-hard-links create mode 100644 busybox/testsuite/cp/cp-preserves-links create mode 100644 busybox/testsuite/cp/cp-preserves-source-file create mode 100644 busybox/testsuite/cut/cut-cuts-a-character create mode 100644 busybox/testsuite/cut/cut-cuts-a-closed-range create mode 100644 busybox/testsuite/cut/cut-cuts-a-field create mode 100644 busybox/testsuite/cut/cut-cuts-an-open-range create mode 100644 busybox/testsuite/cut/cut-cuts-an-unclosed-range create mode 100644 busybox/testsuite/date/date-R-works create mode 100644 busybox/testsuite/date/date-format-works create mode 100644 busybox/testsuite/date/date-u-works create mode 100644 busybox/testsuite/date/date-works create mode 100644 busybox/testsuite/dd/dd-accepts-if create mode 100644 busybox/testsuite/dd/dd-accepts-of create mode 100644 busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output create mode 100644 busybox/testsuite/dd/dd-prints-count-to-standard-error create mode 100644 busybox/testsuite/dirname/dirname-handles-absolute-path create mode 100644 busybox/testsuite/dirname/dirname-handles-empty-path create mode 100644 busybox/testsuite/dirname/dirname-handles-multiple-slashes create mode 100644 busybox/testsuite/dirname/dirname-handles-relative-path create mode 100644 busybox/testsuite/dirname/dirname-handles-root create mode 100644 busybox/testsuite/dirname/dirname-handles-single-component create mode 100644 busybox/testsuite/dirname/dirname-works create mode 100644 busybox/testsuite/du/du-h-works create mode 100644 busybox/testsuite/du/du-k-works create mode 100644 busybox/testsuite/du/du-l-works create mode 100644 busybox/testsuite/du/du-m-works create mode 100644 busybox/testsuite/du/du-s-works create mode 100644 busybox/testsuite/du/du-works create mode 100644 busybox/testsuite/echo/echo-does-not-print-newline create mode 100644 busybox/testsuite/echo/echo-prints-argument create mode 100644 busybox/testsuite/echo/echo-prints-arguments create mode 100644 busybox/testsuite/echo/echo-prints-newline create mode 100644 busybox/testsuite/expr/expr-works create mode 100644 busybox/testsuite/false/false-is-silent create mode 100644 busybox/testsuite/false/false-returns-failure create mode 100644 busybox/testsuite/find/find-supports-minus-xdev create mode 100644 busybox/testsuite/grep/egrep-is-not-case-insensitive create mode 100644 busybox/testsuite/grep/egrep-supports-extended-regexps create mode 100644 busybox/testsuite/grep/grep-handles-binary-files create mode 100644 busybox/testsuite/grep/grep-handles-multiple-regexps create mode 100644 busybox/testsuite/grep/grep-is-also-egrep create mode 100644 busybox/testsuite/grep/grep-matches-NUL create mode 100644 busybox/testsuite/gunzip/gunzip-reads-from-standard-input create mode 100644 busybox/testsuite/gzip/gzip-accepts-multiple-files create mode 100644 busybox/testsuite/gzip/gzip-accepts-single-minus create mode 100644 busybox/testsuite/gzip/gzip-removes-original-file create mode 100644 busybox/testsuite/head/head-n-works create mode 100644 busybox/testsuite/head/head-works create mode 100644 busybox/testsuite/hostid/hostid-works create mode 100644 busybox/testsuite/hostname/hostname-d-works create mode 100644 busybox/testsuite/hostname/hostname-i-works create mode 100644 busybox/testsuite/hostname/hostname-s-works create mode 100644 busybox/testsuite/hostname/hostname-works create mode 100644 busybox/testsuite/id/id-g-works create mode 100644 busybox/testsuite/id/id-u-works create mode 100644 busybox/testsuite/id/id-un-works create mode 100644 busybox/testsuite/id/id-ur-works create mode 100644 busybox/testsuite/ln/ln-creates-hard-links create mode 100644 busybox/testsuite/ln/ln-creates-soft-links create mode 100644 busybox/testsuite/ln/ln-force-creates-hard-links create mode 100644 busybox/testsuite/ln/ln-force-creates-soft-links create mode 100644 busybox/testsuite/ln/ln-preserves-hard-links create mode 100644 busybox/testsuite/ln/ln-preserves-soft-links create mode 100644 busybox/testsuite/ls/ls-1-works create mode 100644 busybox/testsuite/ls/ls-h-works create mode 100644 busybox/testsuite/ls/ls-l-works create mode 100644 busybox/testsuite/ls/ls-s-works create mode 100644 busybox/testsuite/md5sum/md5sum-verifies-non-binary-file create mode 100644 busybox/testsuite/mkdir/mkdir-makes-a-directory create mode 100644 busybox/testsuite/mkdir/mkdir-makes-parent-directories create mode 100644 busybox/testsuite/msh/msh-supports-underscores-in-variable-names create mode 100644 busybox/testsuite/mv/mv-files-to-dir create mode 100644 busybox/testsuite/mv/mv-follows-links create mode 100644 busybox/testsuite/mv/mv-moves-empty-file create mode 100644 busybox/testsuite/mv/mv-moves-file create mode 100644 busybox/testsuite/mv/mv-moves-hardlinks create mode 100644 busybox/testsuite/mv/mv-moves-large-file create mode 100644 busybox/testsuite/mv/mv-moves-small-file create mode 100644 busybox/testsuite/mv/mv-moves-symlinks create mode 100644 busybox/testsuite/mv/mv-moves-unreadable-files create mode 100644 busybox/testsuite/mv/mv-preserves-hard-links create mode 100644 busybox/testsuite/mv/mv-preserves-links create mode 100644 busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir create mode 100644 busybox/testsuite/mv/mv-removes-source-file create mode 100644 busybox/testsuite/pwd/pwd-prints-working-directory create mode 100644 busybox/testsuite/rm/rm-removes-file create mode 100644 busybox/testsuite/rmdir/rmdir-removes-parent-directories create mode 100755 busybox/testsuite/runtest create mode 100644 busybox/testsuite/sed/sed-accepts-blanks-before-command create mode 100644 busybox/testsuite/sed/sed-aic-commands create mode 100644 busybox/testsuite/sed/sed-append-hold-space-to-pattern-space create mode 100644 busybox/testsuite/sed/sed-append-next-line create mode 100644 busybox/testsuite/sed/sed-branch create mode 100644 busybox/testsuite/sed/sed-branch-conditional create mode 100644 busybox/testsuite/sed/sed-branch-conditional2 create mode 100644 busybox/testsuite/sed/sed-branch-no-label create mode 100644 busybox/testsuite/sed/sed-chains-substs create mode 100644 busybox/testsuite/sed/sed-chains-substs2 create mode 100644 busybox/testsuite/sed/sed-does-not-substitute-in-deleted-line create mode 100644 busybox/testsuite/sed/sed-handles-embedded-slashes create mode 100644 busybox/testsuite/sed/sed-handles-empty-lines create mode 100644 busybox/testsuite/sed/sed-handles-unsatisfied-backrefs create mode 100644 busybox/testsuite/sed/sed-next-line create mode 100644 busybox/testsuite/sed/sed-prints-line-once-for-multiple-substs create mode 100644 busybox/testsuite/sed/sed-recurses-properly create mode 100644 busybox/testsuite/sed/sed-regex-match-newline create mode 100644 busybox/testsuite/sed/sed-splits-edit-commands-on-command-line create mode 100644 busybox/testsuite/sed/sed-subst-subprint create mode 100644 busybox/testsuite/sed/sed-write-to-stdout create mode 100644 busybox/testsuite/sort/sort-n-works create mode 100644 busybox/testsuite/sort/sort-r-works create mode 100644 busybox/testsuite/sort/sort-works create mode 100644 busybox/testsuite/strings/strings-works-like-GNU create mode 100644 busybox/testsuite/tail/tail-n-works create mode 100644 busybox/testsuite/tail/tail-works create mode 100644 busybox/testsuite/tar/tar-archives-multiple-files create mode 100644 busybox/testsuite/tar/tar-complains-about-missing-file create mode 100644 busybox/testsuite/tar/tar-demands-at-least-one-ctx create mode 100644 busybox/testsuite/tar/tar-demands-at-most-one-ctx create mode 100644 busybox/testsuite/tar/tar-extracts-file create mode 100644 busybox/testsuite/tar/tar-extracts-from-standard-input create mode 100644 busybox/testsuite/tar/tar-extracts-multiple-files create mode 100644 busybox/testsuite/tar/tar-extracts-to-standard-output create mode 100644 busybox/testsuite/tar/tar-handles-cz-options create mode 100644 busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list create mode 100644 busybox/testsuite/tar/tar-handles-exclude-and-extract-lists create mode 100644 busybox/testsuite/tar/tar-handles-multiple-X-options create mode 100644 busybox/testsuite/tar/tar-handles-nested-exclude create mode 100644 busybox/testsuite/tee/tee-appends-input create mode 100644 busybox/testsuite/tee/tee-tees-input create mode 100644 busybox/testsuite/touch/touch-creates-file create mode 100644 busybox/testsuite/touch/touch-does-not-create-file create mode 100644 busybox/testsuite/touch/touch-touches-files-after-non-existent-file create mode 100644 busybox/testsuite/tr/tr-d-works create mode 100644 busybox/testsuite/tr/tr-non-gnu create mode 100644 busybox/testsuite/tr/tr-works create mode 100644 busybox/testsuite/true/true-is-silent create mode 100644 busybox/testsuite/true/true-returns-success create mode 100644 busybox/testsuite/uptime/uptime-works create mode 100644 busybox/testsuite/uuencode/uuencode-sets-standard-input-mode-correctly create mode 100644 busybox/testsuite/wc/wc-counts-all create mode 100644 busybox/testsuite/wc/wc-counts-characters create mode 100644 busybox/testsuite/wc/wc-counts-lines create mode 100644 busybox/testsuite/wc/wc-counts-words create mode 100644 busybox/testsuite/wc/wc-prints-longest-line-length create mode 100644 busybox/testsuite/wget/wget--O-overrides--P create mode 100644 busybox/testsuite/wget/wget-handles-empty-path create mode 100644 busybox/testsuite/wget/wget-retrieves-google-index create mode 100644 busybox/testsuite/wget/wget-supports--P create mode 100644 busybox/testsuite/which/which-uses-default-path create mode 100644 busybox/testsuite/xargs/xargs-works create mode 100644 busybox/util-linux/Config.in create mode 100644 busybox/util-linux/Makefile create mode 100644 busybox/util-linux/Makefile.in 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/fdformat.c create mode 100644 busybox/util-linux/fdisk.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/hexdump.c create mode 100644 busybox/util-linux/hwclock.c create mode 100644 busybox/util-linux/losetup.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 diff --git a/busybox/.cvsignore b/busybox/.cvsignore new file mode 100644 index 000000000..4e4f5863e --- /dev/null +++ b/busybox/.cvsignore @@ -0,0 +1,7 @@ +busybox +busybox.links +_install +.config +.menuconfig.log +.config.cmd +.config.old diff --git a/busybox/.indent.pro b/busybox/.indent.pro new file mode 100644 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..87df22d55 --- /dev/null +++ b/busybox/AUTHORS @@ -0,0 +1,133 @@ +List of the authors of code contained in BusyBox. + +If you have code in BusyBox, you should be listed here. If you should be +listed, or the description of what you have done needs more detail, or is +incorect, _please_ let me know. + + -Erik + +----------- + +Emanuele Aina + run-parts + +Erik Andersen + Tons of new stuff, major rewrite of most of the + core apps, tons of new apps as noted in header files. + Lots of tedious effort writing these boring docs that + nobody is going to actually read. + +Laurence Anderson + rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm + +Jeff Angielski + ftpput, ftpget + +Edward Betts + expr, hostid, logname, whoami + +John Beppu + du, nslookup, sort + +Brian Candler + tiny-ls(ls) + +Randolph Chung + fbset, ping, hostname + +Dave Cinege + more(v2), makedevs, dutmp, modularization, auto links file, + various fixes, Linux Router Project maintenance + +Jordan Crouse + ipcalc + +Magnus Damm + tftp client + insmod powerpc support + +Larry Doolittle + pristine source directory compilation, lots of patches and fixes. + +Glenn Engel + httpd + +Gennady Feldman + Sysklogd (single threaded syslogd, IPC Circular buffer support, + logread), various fixes. + +Robert Griebl + modprobe, hwclock, suid/sgid handling, tinylogin integration + many bugfixes and enhancements + +Karl M. Hegbloom + cp_mv.c, the test suite, various fixes to utility.c, &c. + +Daniel Jacobowitz + mktemp.c + +Matt Kraai + documentation, bugfixes, test suite + +Stephan Linz + ipcalc, Red Hat equivalence + +John Lombardo + tr + +Glenn McGrath + Common unarchving code and unarchiving applets, ifupdown, ftpgetput, + nameif, sed, patch, fold, install, uudecode. + Various bugfixes, review and apply numerous patches. + +Manuel Novoa III + cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes, + mesg, vconfig, make_directory, parse_mode, dirname, mode_string, + get_last_path_component, simplify_path, and a number trivial libbb routines + + also bug fixes, partial rewrites, and size optimizations in + ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir, + mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable, + interface, dutmp, ifconfig, route + +Vladimir Oleynik + cmdedit; xargs(current), httpd(current); + ports: ash, crond, fdisk, inetd, stty, traceroute, top; + locale, various fixes + and irreconcilable critic of everything not perfect. + +Bruce Perens + Original author of BusyBox in 1995, 1996. Some of his code can + still be found hiding here and there... + +Tim Riker + bug fixes, member of fan club + +Kent Robotti + reset, tons and tons of bug reports and patches. + +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(previous), + 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 + +Tito Ragusa + devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm and fdformat. + diff --git a/busybox/Changelog b/busybox/Changelog new file mode 100644 index 000000000..721fc8270 --- /dev/null +++ b/busybox/Changelog @@ -0,0 +1,1381 @@ +--------------------- +PatchSet 4347 +Date: 2004/08/16 10:29:28 +Author: andersen +Branch: HEAD +Tag: busybox_1_00_rc3 +Log: +Prepare for release + +Members: + Changelog:1.294->1.295 + docs/busybox_header.pod:1.17->1.18 + docs/busybox.net/news.html:1.21->1.22 + docs/busybox.net/screenshot.html:1.11->1.12 + +--------------------- +PatchSet 4348 +Date: 2004/08/18 17:57:16 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Fixup 'dc' usage + +Members: + include/usage.h:1.218->1.219 + +--------------------- +PatchSet 4349 +Date: 2004/08/19 18:22:13 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from Vladimir N. Oleynik: + +On Wed Aug 18, 2004 at 06:52:57PM +0800, Matt Johnston wrote: +> I've come across some strange-seeming behaviour when running programs +> under Busybox (1.0.0-rc3) ash. If the child process sets stdin to be +> non-blocking and then exits, the parent ash will also exit. A quick strace +> shows that a subsequent read() from stdin returns EAGAIN (as would be +> expected): + +Thanks! +Patch attached. + + +--w +vodz + +Members: + shell/ash.c:1.104->1.105 + shell/cmdedit.c:1.92->1.93 + +--------------------- +PatchSet 4350 +Date: 2004/08/19 18:25:02 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from Tito documenting the '-q' option + +Members: + include/usage.h:1.219->1.220 + +--------------------- +PatchSet 4351 +Date: 2004/08/19 18:26:26 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from Tito adding support for '-q' + +Members: + procps/kill.c:1.52->1.53 + +--------------------- +PatchSet 4352 +Date: 2004/08/19 18:30:31 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from Mike Castle to cleanup some modutils issues, in +particular making alias support work better. + +Members: + modutils/modprobe.c:1.39->1.40 + +--------------------- +PatchSet 4353 +Date: 2004/08/19 19:15:06 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +regularly update the status line display + -Erik + +Members: + editors/vi.c:1.37->1.38 + +--------------------- +PatchSet 4354 +Date: 2004/08/19 19:17:30 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from Rodney Radford adding x86_64 support. + +Members: + modutils/insmod.c:1.122->1.123 + +--------------------- +PatchSet 4355 +Date: 2004/08/25 02:02:19 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch from Manousaridis Angelos to cleanup stale file descriptors, it was preventing unmounting an initial filesystem. + +Members: + loginutils/getty.c:1.13->1.14 + loginutils/login.c:1.19->1.20 + +--------------------- +PatchSet 4356 +Date: 2004/08/26 21:45:21 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Felipe Kellermann writes: + +Unfortunatelly I've not followed the last two or three weeks commits (new +semester started and so now I rarely have time to fix my personal bridge) +but tonight I synched my tree and immediately noticed a rather nasty bug! + +[Using libbb/interface.c:1.24] +# grep eth0 /proc/net/dev | xargs +eth0:311708397 237346 1670 0 1789 1670 0 0 22580308 120297 0 0 0 102 0 0 + +# ifconfig eth0 +eth0 Link encap:Ethernet HWaddr 00:20:AF:7C:EA:B7 + inet addr:10.0.0.1 Bcast:10.0.0.127 Mask:255.255.255.128 + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) + Interrupt:5 Base address:0x320 + + +All values `ifconfig' is showing are `zeroed' -- I quickly looked at the +last commits I missed and noticed that there were a commit relating to +ifconfig, libbb/interface.c:1.23->1.24 (PatchSet 4338). + +I've reversed the patch and now everything is working again. I compared +the get_name's return values from the 1.23 and 1.24 and quickly noticed +that the new revision is leaving `p' right on the sep while the rev 1.23 +was leaving it right on the starting of the values... + +1-line, 1/3-minute patch attached :-) + +Members: + libbb/interface.c:1.24->1.25 + +--------------------- +PatchSet 4357 +Date: 2004/08/26 22:18:56 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Tito writes: + +Hi, +I've spent the half night staring at the devilish my_getpwuid and my_getgrgid functions +trying to find out a way to avoid actual and future potential buffer overflow problems +without breaking existing code. +Finally I've found a not intrusive way to do this that surely doesn't break existing code +and fixes a couple of problems too. +The attached patch: +1) changes the behaviour of my_getpwuid and my_getgrgid to avoid potetntial buffer overflows +2) fixes all occurences of this function calls in tar.c , id.c , ls.c, whoami.c, logger.c, libbb.h. +3) The behaviour of tar, ls and logger is unchanged. +4) The behavior of ps with somewhat longer usernames messing up output is fixed. +5) The only bigger change was the increasing of size of the buffers in id.c to avoid + false negatives (unknown user: xxxxxx) with usernames longer than 8 chars. + The value i used ( 32 chars ) was taken from the tar header ( see gname and uname). + Maybe this buffers can be reduced a bit ( to 16 or whatever ), this is up to you. +6) The increase of size of the binary is not so dramatic: + size busybox + text data bss dec hex filename + 239568 2300 36816 278684 4409c busybox + size busybox_fixed + text data bss dec hex filename + 239616 2300 36816 278732 440cc busybox +7) The behaviour of whoami changed: + actually it prints out an username cut down to the size of the buffer. + This could be fixed by increasing the size of the buffer as in id.c or + avoid the use of my_getpwuid and use getpwuid directly instead. + Maybe this colud be also remain unchanged...... + +Please apply if you think it is ok to do so. +The diff applies on today's cvs tarball (2004-08-25). +Thanks in advance, +Ciao, +Tito + +Members: + archival/tar.c:1.194->1.195 + coreutils/id.c:1.24->1.25 + coreutils/ls.c:1.110->1.111 + coreutils/whoami.c:1.21->1.22 + include/libbb.h:1.133->1.134 + libbb/my_getgrgid.c:1.7->1.8 + libbb/my_getpwuid.c:1.7->1.8 + libbb/procps.c:1.13->1.14 + sysklogd/logger.c:1.39->1.40 + +--------------------- +PatchSet 4358 +Date: 2004/08/26 22:22:50 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Vladimir N. Oleynik writes: + +Ming-Ching, + +>>No. Here there are no mistakes. +>>You using POST metod. +>>For get data you should read from stdin CONTENT_LENGTH bytes. + +>Hower as I posted a little while ago, there is indeed a bug +>in POST method if the CONTENT_LENGTH is bigger +>than sizeof(wbuf[128]). So if your CGI script is expecting to +>read the full CONTENT_LENGTH, it might block forever, +>because it will only transfer sizeof(wbuf) to the CGI. + +Ok, Ok. I should find time to understand with a problem. +Try attached patch. + + +--w +vodz + +Members: + networking/httpd.c:1.26->1.27 + +--------------------- +PatchSet 4359 +Date: 2004/08/26 22:26:26 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Save a line or two + +Members: + loginutils/getty.c:1.14->1.15 + loginutils/login.c:1.20->1.21 + +--------------------- +PatchSet 4360 +Date: 2004/08/26 22:36:02 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Tito writes: + +Hi, +I've fixed also the issue of whoami cutting down usernames. +This time I cannot send a diff because i don't know if my previous patches will be applied +or not, so I send in the whole file. +The changes I've made don't affect size but ensure that usernames of whatever lenght +are correctly displayed. +root@localhost:/dev/pts/3:/root/Desktop/busybox/coreutils# size whoami_orig.o + text data bss dec hex filename + 102 0 0 102 66 whoami_orig.o +root@localhost:/dev/pts/3:/root/Desktop/busybox/coreutils# size whoami.o + text data bss dec hex filename + 93 0 0 93 5d whoami.o + +This should be applied even if the other patches aren't as this matches the behaviour of the GNU whoami. + +Thanks in advance, +Ciao, +Tito + +Members: + coreutils/whoami.c:1.22->1.23 + +--------------------- +PatchSet 4361 +Date: 2004/08/26 23:01:34 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +The login applet should always be setuid root + +Members: + include/applets.h:1.114->1.115 + +--------------------- +PatchSet 4362 +Date: 2004/08/26 23:13:00 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Improve the setuid situation a bit, and make it more apparent +when people really ought to make busybox setuid root. + -Erik + +Members: + Makefile:1.296->1.297 + loginutils/Config.in:1.8->1.9 + miscutils/Config.in:1.18->1.19 + +--------------------- +PatchSet 4363 +Date: 2004/08/26 23:15:29 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Togg writes: + +Syslogd wont start if remote-logging is enabled and the connection to the +remote-log server is not possible on syslogd startup. + +I found a patch somewhere which works like a charm. It uses sendto() which +seems more reliable for this issue. + +Please see attached patch. Many people will be more happy with this included +I think. + +Regards, +Togg + +Members: + sysklogd/syslogd.c:1.113->1.114 + +--------------------- +PatchSet 4364 +Date: 2004/08/27 19:55:28 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Quiet a few warnings + +Members: + init/mesg.c:1.2->1.3 + shell/msh.c:1.20->1.21 + +--------------------- +PatchSet 4365 +Date: 2004/08/28 00:43:05 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Fixup some warnings + +Members: + archival/bunzip2.c:1.19->1.20 + archival/libunarchive/decompress_bunzip2.c:1.13->1.14 + coreutils/uniq.c:1.21->1.22 + modutils/insmod.c:1.123->1.124 + networking/ipcalc.c:1.10->1.11 + util-linux/mkfs_minix.c:1.42->1.43 + +--------------------- +PatchSet 4366 +Date: 2004/09/02 22:21:39 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Tito writes: + +Hi Erik, +Hi to all, +This is part five of the my_get*id story. +I've tweaked a bit this two functions to make them more flexible, +but this changes will not affect existing code. +Now they work so: +1) my_getpwuid( char *user, uid_t uid, int bufsize) + + if bufsize is > 0 char *user cannot be set to NULL + on success username is written on static allocated buffer + on failure uid as string is written to buffer and NULL is returned + if bufsize is = 0 char *user can be set to NULL + on success username is returned + on failure NULL is returned + if bufsize is < 0 char *user can be set to NULL + on success username is returned + on failure an error message is printed and the program exits + + 2) 1) my_getgrgid( char *group, uid_t uid, int bufsize) + + if bufsize is > 0 char *group cannot be set to NULL + on success groupname is written on static allocated buffer + on failure gid as string is written to buffer and NULL is returned + if bufsize is = 0 char *group can be set to NULL + on success groupname is returned + on failure NULL is returned + if bufsize is < 0 char *group can be set to nULL + on success groupname is returned + on failure an error message is printed and the program exits + +This changes were needed mainly for my new id applet. +It is somewhat bigger then the previous but matches the behaviour of GNU id +and is capable to handle usernames of whatever length. +BTW: at a first look it seems to me that it will integrate well (with just a few changes) +with the pending patch in patches/id_groups_alias.patch. +The increase in size is balanced by the removal of my_getpwnamegid.c +from libbb as this was used only in previous id applet and by size optimizations +made possible in whoami.c and in passwd.c. +I know that we are in feature freeze but I think that i've tested it enough +(at least I hope so.......). + +Members: + coreutils/id.c:1.25->1.26 + coreutils/whoami.c:1.23->1.24 + include/libbb.h:1.134->1.135 + libbb/Makefile.in:1.36->1.37 + libbb/my_getgrgid.c:1.8->1.9 + libbb/my_getpwuid.c:1.8->1.9 + loginutils/passwd.c:1.7->1.8 + +--------------------- +PatchSet 4367 +Date: 2004/09/02 22:22:16 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Tito writes: + +The second patch contains: +1) a size optimization for adduser.c +2) removes a warning about an unused variable in syslogd.c if CONFIG_FEATURE_REMOTE_LOG is not set +3)cosmetic fixes for addgroup_full_usage and adduser_full_usage + +Ciao, +Tito + +Members: + include/usage.h:1.220->1.221 + loginutils/adduser.c:1.10->1.11 + sysklogd/syslogd.c:1.114->1.115 + +--------------------- +PatchSet 4368 +Date: 2004/09/02 23:03:24 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Based on patches from Mike Frysinger, add insmod support for +sparc and ia64 (itanium). + +Also, reorganize the insmod architecture support code to be +alphasorted and less messy. + +Update the readme to list current insmod arch support. + +Members: + README:1.35->1.36 + modutils/insmod.c:1.124->1.125 + +--------------------- +PatchSet 4369 +Date: 2004/09/02 23:11:52 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +No longer needed + +Members: + libbb/my_getpwnamegid.c:1.7->1.8(DEAD) + patches/id_groups_alias.patch:1.1->1.2(DEAD) + +--------------------- +PatchSet 4370 +Date: 2004/09/02 23:13:10 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Jonas Holmberg from axis dot com writes: + +This patch makes msh handle variable expansion within backticks more +correctly. + +Current behaviour (wrong): +-------------------------- + +BusyBox v1.00-rc3 (2004.08.26-11:51+0000) Built-in shell (msh) +Enter 'help' for a list of built-in commands. + +$ A='`echo hello`' +$ echo $A +`echo hello` +$ echo `echo $A` +hello +$ + + +New behaviour (correct): +------------------------ + +BusyBox v1.00-rc3 (2004.08.26-11:51+0000) Built-in shell (msh) +Enter 'help' for a list of built-in commands. + +$ A='`echo hello`' +$ echo $A +`echo hello` +$ echo `echo $A` +`echo hello` +$ + +The current behaviour (wrong according to standards) was actually my +fault. msh handles backticks by executing a subshell (which makes it +work on MMU-less systems). Executing a subshell makes it hard to only +expand variables once in the parent. Therefore I export all variables +that will be expanded within the backticks and let the subshell handle +the expansion instead. + +The bug was found while searching for security leaks in CGI-scripts. +Current behaviour of msh makes it easy to expand backticks by mistake +in $QUERY_STRING. I recommend appling the patch before release of bb +1.00. + +/Jonas + +Members: + shell/msh.c:1.21->1.22 + +--------------------- +PatchSet 4371 +Date: 2004/09/08 10:01:07 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patrick Huesmann noticed BusyBox would not link when +CONFIG_FEATURE_COMMAND_EDITING was defined *and* +CONFIG_FEATURE_COMMAND_TAB_COMPLETION was undefined. + +Vladimir N. Oleynik writes: + +Its declare always, also if CONFIG_FEATURE_COMMAND_TAB_COMPLETION +undefined. +Patch to CVS version attached. + +--w +vodz + +Members: + shell/ash.c:1.105->1.106 + +--------------------- +PatchSet 4372 +Date: 2004/09/08 10:56:06 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Felipe Kellermann writes: + +The Togg's sysklogd patch to use sendto() on remote logging is formatting +strangely (using `<' and '>' surrounding the `msg' string message). This +is OK, but this is not the standard way of formatting this message. + +So this patch does the following: + +o Fix the formatting to the standard way. +o Uses `MAXLINE' when needed; +o Don't loop sending messages without a "sleeping time", + I'm now doing `now = 1', `now <<= 1'; +o Don't die on `init_RemoteLog' when starting up (feature!) + We're now trying to connect every time we have an invalid fd; +o Removes one static uneeded variable. +o Removes two automatic uneeded variables. + +Members: + sysklogd/syslogd.c:1.115->1.116 + +--------------------- +PatchSet 4373 +Date: 2004/09/08 20:13:05 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Fixup URL + +Members: + docs/busybox.net/cvs_write.html:1.9->1.10 + +--------------------- +PatchSet 4374 +Date: 2004/09/14 13:59:44 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +I have to assume both Avaks and LSILogic are deliberatly ignoring me. + +Members: + docs/busybox.net/shame.html:1.18->1.19 + +--------------------- +PatchSet 4375 +Date: 2004/09/14 16:08:02 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch from tito to add argument checking. + +Members: + loginutils/addgroup.c:1.12->1.13 + +--------------------- +PatchSet 4376 +Date: 2004/09/14 16:23:56 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch from Felipe Kellermann, adds missing applet usage options, removes usage +for options that are currently not implemented and fixes typos. + +Members: + include/usage.h:1.221->1.222 + +--------------------- +PatchSet 4377 +Date: 2004/09/14 17:24:58 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch from Felipe Kellermann, remove some unnecessary dups, i declared a few extra const's also. + +Members: + networking/ifupdown.c:1.50->1.51 + networking/telnet.c:1.43->1.44 + networking/telnetd.c:1.12->1.13 + networking/tftp.c:1.28->1.29 + util-linux/getopt.c:1.13->1.14 + +--------------------- +PatchSet 4378 +Date: 2004/09/14 18:12:13 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch by Felipe Kellermann, fix a bug introduced in the last patch by adding a condition around the remote logging, also adds some comments. + +Members: + sysklogd/syslogd.c:1.116->1.117 + +--------------------- +PatchSet 4379 +Date: 2004/09/14 18:56:52 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Apply patch from Felipe Kellermann to simlify logic of sort functions. +I reversed the result of the sort functions to make the big numbers go to the top. + +Members: + procps/top.c:1.12->1.13 + +--------------------- +PatchSet 4380 +Date: 2004/09/14 19:14:00 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +remove a cut/paste mistake, i better get some sleep. + +Members: + procps/top.c:1.13->1.14 + +--------------------- +PatchSet 4381 +Date: 2004/09/15 02:05:23 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch by Felipe Kellermann, use the common escape handling function and remove some unused code. + +Members: + coreutils/printf.c:1.22->1.23 + +--------------------- +PatchSet 4382 +Date: 2004/09/15 02:39:09 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Only write to shadow file is shadow passwords are enabled. Patch by magicfox modified by myself to retain check for shadow file access. + +Members: + loginutils/passwd.c:1.8->1.9 + +--------------------- +PatchSet 4383 +Date: 2004/09/15 03:04:07 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Tito writes, +"This patch fixes all the bugs in id previously spotted by vodz and me. +The binary size increased a bit, but now it should work as expected." + +Members: + coreutils/id.c:1.26->1.27 + include/libbb.h:1.135->1.136 + libbb/Makefile.in:1.37->1.38 + libbb/my_getgrgid.c:1.9->1.10 + libbb/my_getpwuid.c:1.9->1.10 + libbb/my_getug.c:INITIAL->1.1 + +--------------------- +PatchSet 4384 +Date: 2004/09/15 03:24:32 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Add a missing brace, patch by Hideki IWAMOTO + +Members: + coreutils/stty.c:1.9->1.10 + +--------------------- +PatchSet 4385 +Date: 2004/09/23 20:08:46 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Add some notes on how to make telnetd actually work + +Members: + networking/Config.in:1.28->1.29 + +--------------------- +PatchSet 4386 +Date: 2004/09/24 01:25:39 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +A bit of extra explanation regarding STANDALONE + +Members: + shell/Config.in:1.16->1.17 + +--------------------- +PatchSet 4387 +Date: 2004/09/24 02:04:13 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch from David Daney to make the -i option work with -l. + +Members: + coreutils/ls.c:1.111->1.112 + +--------------------- +PatchSet 4388 +Date: 2004/09/24 02:36:44 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Remove this error message at Vodz request, it was misleading. + +Members: + libbb/correct_password.c:1.4->1.5 + +--------------------- +PatchSet 4389 +Date: 2004/09/24 09:09:44 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Fix a typo + +Members: + shell/Config.in:1.17->1.18 + +--------------------- +PatchSet 4390 +Date: 2004/09/24 09:18:55 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch from Egor Duda +Attached patch prevents modprobe from trying to call 'insmod (null)' +whenever nonexistent module is either passed to modprobe via command +line or mentioned in modules.dep + +this replaces cryptic error +sh: Syntax error: word unexpected (expecting ")") +with +modprobe: module some-module not found. + +egor. + +Members: + modutils/modprobe.c:1.40->1.41 + +--------------------- +PatchSet 4391 +Date: 2004/09/24 09:24:27 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch from Dmitry Zakharov to fix a bug triggered by freeswan's scripts. + +Members: + editors/awk.c:1.10->1.11 + +--------------------- +PatchSet 4392 +Date: 2004/09/30 00:24:21 +Author: bug1 +Branch: HEAD +Tag: (none) +Log: +Patch from William Barsse to fix a segfault when multiple files are specified. + +Members: + coreutils/tail.c:1.47->1.48 + +--------------------- +PatchSet 4393 +Date: 2004/10/07 00:35:59 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Make it more apparent that archive creation is not supported + +Members: + archival/ar.c:1.49->1.50 + +--------------------- +PatchSet 4394 +Date: 2004/10/08 07:21:58 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from Michael Tokarev: + +Scenario: + + touch x -- creates plain file name `x' + mkdir x -- exits successefully + +libbb/make_directory.c, bb_make_directory(), contains +the following code: + + if (mkdir(path, 0777) < 0) { + /* If we failed for any other reason than the directory + * already exists, output a diagnostic and return -1.*/ + if (errno != EEXIST) { + fail_msg = "create"; + umask(mask); + break; + } + /* Since the directory exists, don't attempt to change + * permissions if it was the full target. Note that + * this is not an error conditon. */ + if (!c) { + umask(mask); + return 0; + } + } + +The assumption that EEXIST error is due to that the *directory* +already exists is wrong: any file type with that name will cause +this error to be returned. Proper way IMHO will be is to stat() +the path and check whenever this is really a directory. Below +(attached) is a patch to fix this issue. + +Members: + libbb/make_directory.c:1.15->1.16 + +--------------------- +PatchSet 4395 +Date: 2004/10/08 07:45:08 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +egor duda writes: + +Hi! + +I've created a patch to busybox' build system to allow building it in +separate tree in a manner similar to kbuild from kernel version 2.6. + +That is, one runs command like +'make O=/build/some/where/for/specific/target/and/options' +and everything is built in this exact directory, provided that it exists. + +I understand that applyingc such invasive changes during 'release +candidates' stage of development is at best unwise. So, i'm currently +asking for comments about this patch, starting from whether such thing +is needed at all to whether it coded properly. + +'make check' should work now, and one make creates Makefile in build +directory, so one can run 'make' in build directory after that. + +One possible caveat is that if we build in some directory other than +source one, the source directory should be 'distclean'ed first. + +egor + +Members: + Makefile:1.297->1.298 + Rules.mak:1.37->1.38 + applets/Makefile:1.5->1.6 + applets/Makefile.in:1.5->1.6 + archival/Makefile:1.7->1.8 + archival/Makefile.in:1.5->1.6 + archival/libunarchive/Makefile:1.5->1.6 + archival/libunarchive/Makefile.in:1.23->1.24 + console-tools/Makefile:1.4->1.5 + console-tools/Makefile.in:1.5->1.6 + coreutils/Makefile:1.3->1.4 + coreutils/Makefile.in:1.9->1.10 + coreutils/libcoreutils/Makefile:1.3->1.4 + coreutils/libcoreutils/Makefile.in:1.3->1.4 + debianutils/Makefile:1.3->1.4 + debianutils/Makefile.in:1.5->1.6 + editors/Makefile:1.4->1.5 + editors/Makefile.in:1.5->1.6 + findutils/Makefile:1.4->1.5 + findutils/Makefile.in:1.4->1.5 + init/Makefile:1.5->1.6 + init/Makefile.in:1.9->1.10 + libbb/Makefile:1.10->1.11 + libbb/Makefile.in:1.38->1.39 + libpwdgrp/Makefile:1.3->1.4 + libpwdgrp/Makefile.in:1.4->1.5 + loginutils/Makefile:1.3->1.4 + loginutils/Makefile.in:1.8->1.9 + miscutils/Makefile:1.7->1.8 + miscutils/Makefile.in:1.12->1.13 + modutils/Makefile:1.4->1.5 + modutils/Makefile.in:1.3->1.4 + networking/Makefile:1.7->1.8 + networking/Makefile.in:1.19->1.20 + networking/libiproute/Makefile:1.3->1.4 + networking/libiproute/Makefile.in:1.6->1.7 + networking/udhcp/Makefile:1.3->1.4 + networking/udhcp/Makefile.in:1.10->1.11 + procps/Makefile:1.4->1.5 + procps/Makefile.in:1.6->1.7 + scripts/config/Makefile:1.4->1.5 + shell/Makefile:1.4->1.5 + shell/Makefile.in:1.3->1.4 + sysklogd/Makefile:1.5->1.6 + sysklogd/Makefile.in:1.3->1.4 + testsuite/runtest:1.8->1.9 + testsuite/du/du-h-works:1.1->1.2 + testsuite/du/du-k-works:1.1->1.2 + testsuite/du/du-l-works:1.1->1.2 + testsuite/du/du-m-works:1.1->1.2 + testsuite/du/du-s-works:1.1->1.2 + testsuite/du/du-works:1.1->1.2 + testsuite/head/head-n-works:1.1->1.2 + testsuite/head/head-works:1.1->1.2 + testsuite/ls/ls-1-works:1.1->1.2 + testsuite/ls/ls-h-works:1.1->1.2 + testsuite/ls/ls-l-works:1.1->1.2 + testsuite/ls/ls-s-works:1.1->1.2 + testsuite/sort/sort-n-works:1.1->1.2 + testsuite/sort/sort-r-works:1.1->1.2 + testsuite/sort/sort-works:1.1->1.2 + testsuite/tail/tail-n-works:1.1->1.2 + testsuite/tail/tail-works:1.1->1.2 + testsuite/xargs/xargs-works:1.1->1.2 + util-linux/Makefile:1.6->1.7 + util-linux/Makefile.in:1.8->1.9 + +--------------------- +PatchSet 4396 +Date: 2004/10/08 07:58:30 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +As noticed by egor duda, current_menu is declared as 'extern struct menu +*current_menu;' in scripts/config/lkc.h line 63, and this conflicts with +static definition in mconf.c. + +Members: + scripts/config/mconf.c:1.5->1.6 + +--------------------- +PatchSet 4397 +Date: 2004/10/08 08:03:29 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +last_patch139.gz from Vladimir N. Oleynik: + +>I also don't mean to disagree about leaving 30x status codes until after +>1.0. In fact, although redirecting http://host/dir to http://host/dir/ +>with a 301 is common practice (e.g. Apache, IIS), AFAIK it isn't +>actually required (or mentioned) by the HTTP specs. + +Ok. +Attached patch have 302 and 408 implemented features. + + +--w +vodz + +Members: + networking/httpd.c:1.27->1.28 + +--------------------- +PatchSet 4398 +Date: 2004/10/08 08:07:40 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Tito writes: + +Hi to all, +This patch contains just some fixes for some misleading +comments in my_getpwuid.c and my_getug.c. +The code is untouched so this patch will not +cause troubles. + +Please apply. + +Thanks in advance and Ciao, +Tito + +Members: + libbb/my_getpwuid.c:1.10->1.11 + libbb/my_getug.c:1.1->1.2 + +--------------------- +PatchSet 4399 +Date: 2004/10/08 08:10:57 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Hiroshi Ito writes: + + Hello + + I'm using busy box on mipsel machine. + + "grep -f file" will cause segmentation fault. + +Vladimir N. Oleynik writes: + +Hiroshi, + +Thank for bug report, but your patch is full broken. +Worked patch attached. +(really changes is zero initialize, and indent correcting). + + +--w +vodz + +Members: + findutils/grep.c:1.85->1.86 + +--------------------- +PatchSet 4400 +Date: 2004/10/08 08:14:58 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Hiroshi Ito writes: + +ash + "unset OLDPWD; cd -" causes segmentation fault. + ( OLDPWD is not set when sh is invoked from getty. ) + +patch against current CVS is attached. + +Members: + shell/ash.c:1.106->1.107 + +--------------------- +PatchSet 4401 +Date: 2004/10/08 08:17:39 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Hiroshi Ito writes: + +"kill -HUP 1" reloads inittab, and when I append one line to inittab +and send HUP signal two times, It will starts 2 process. + +patch against current CVS is attached. + +Members: + init/init.c:1.204->1.205 + +--------------------- +PatchSet 4402 +Date: 2004/10/08 08:21:54 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Hiroshi Ito writes: + +Hello, all. + +Busybox init does not handle removed inittab entry correctly. + +# I'm sorry about my poor english, but you can find +# what I would like to say from patch, isn't it? + +even if you apply this path, +when yoy try to change a command line option in inittab, +you have to do following steps. +1. remove old line from initrd +2. send HUP signal to init +3. kill old proces which is invoked from init. +4. append new line to inittab +5. send HUP signal to init, again + +patch is against current CVS + last patch witch I send it last. + +Members: + init/init.c:1.205->1.206 + +--------------------- +PatchSet 4403 +Date: 2004/10/08 08:27:40 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from Denis Vlasenko to fix a problem where +wget http://1.2.3.4/abc/ loses last '/' + +Members: + networking/wget.c:1.74->1.75 + +--------------------- +PatchSet 4404 +Date: 2004/10/08 08:49:25 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Wade Berrier writes: + +Hello, + +Here's a patch for a first attempt at static leases for udhcpd. +Included in the tarball are 2 files (static_leases.c, static_leases.h) +and a patch against the latest cvs. + +In the config file you can configure static leases with the following +format: + +static_lease 00:60:08:11:CE:4E 192.168.0.54 +static_lease 00:60:08:11:CE:3E 192.168.0.44 + +Comments/suggestions/improvements are welcome. + + +Wade + +Members: + examples/udhcp/udhcpd.conf:1.3->1.4 + networking/udhcp/Makefile.in:1.11->1.12 + networking/udhcp/dhcpd.c:1.6->1.7 + networking/udhcp/dhcpd.h:1.6->1.7 + networking/udhcp/files.c:1.14->1.15 + networking/udhcp/leases.c:1.6->1.7 + networking/udhcp/serverpacket.c:1.6->1.7 + networking/udhcp/static_leases.c:INITIAL->1.1 + networking/udhcp/static_leases.h:INITIAL->1.1 + +--------------------- +PatchSet 4405 +Date: 2004/10/08 08:57:35 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from Claus Klein to increase, and make more apparent +the hard coded limit on the number of mounts + +Members: + libbb/mtab.c:1.5->1.6 + +--------------------- +PatchSet 4406 +Date: 2004/10/08 09:43:34 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Fix CONFIG_ASH_MATH_SUPPORT_64 so it actually works + +Members: + shell/ash.c:1.107->1.108 + +--------------------- +PatchSet 4407 +Date: 2004/10/08 10:50:08 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Add an initial FAQ + +Members: + docs/busybox.net/FAQ.html:INITIAL->1.1 + docs/busybox.net/header.html:1.8->1.9 + +--------------------- +PatchSet 4408 +Date: 2004/10/08 10:52:08 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Fix the supported architectures section + +Members: + README:1.36->1.37 + +--------------------- +PatchSet 4409 +Date: 2004/10/08 10:52:33 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Bump version + +Members: + Rules.mak:1.38->1.39 + +--------------------- +PatchSet 4410 +Date: 2004/10/08 10:54:20 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +unmerged fix + +Members: + docs/busybox.net/news.html:1.22->1.23 + +--------------------- +PatchSet 4411 +Date: 2004/10/08 11:11:02 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +oops + +Members: + docs/busybox.net/FAQ.html:1.1->1.2 + +--------------------- +PatchSet 4412 +Date: 2004/10/11 20:52:16 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Patch from David Daney: + +It seems that date -s MMDDHHMMYYYY.ss + +will ignore the .ss part. This patch tries to fix the problem. + +David Daney. + +Members: + coreutils/date.c:1.47->1.48 + +--------------------- +PatchSet 4413 +Date: 2004/10/13 06:25:51 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Make certain clients of bb_make_directory default to honoring +the user's umask + +Members: + archival/libunarchive/data_extract_all.c:1.20->1.21 + libbb/make_directory.c:1.16->1.17 + miscutils/devfsd.c:1.9->1.10 + +--------------------- +PatchSet 4414 +Date: 2004/10/13 07:18:05 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +Simon Poole writes: + +Erik, + +Attached is a patch for the udhcpc sample scripts, to correct the order in +which routers are applied if the DHCP server provides more than one (as per +section 3.5 of RFC2132). + +Apologies for not being on the mailing list and thanks for your continued +efforts. + +Simon. + +Members: + examples/udhcp/sample.bound:1.1->1.2 + examples/udhcp/sample.renew:1.1->1.2 + examples/udhcp/simple.script:1.1->1.2 + +--------------------- +PatchSet 4415 +Date: 2004/10/13 07:25:01 +Author: andersen +Branch: HEAD +Tag: (none) +Log: +return failure when nslookup fails + +Members: + networking/nslookup.c:1.32->1.33 + diff --git a/busybox/INSTALL b/busybox/INSTALL new file mode 100644 index 000000000..c9cdf8e26 --- /dev/null +++ b/busybox/INSTALL @@ -0,0 +1,20 @@ +1) Run 'make config' or 'make menuconfig' and select the + functionality that you wish to enable. + +2) Run 'make dep' + +3) Check the Makefile for any Makefile setting you wish + to adjust for your system (things like like setting + your cross compiler, adjusting optimizations, etc) + +4) Run 'make' + +5) Go get a drink of water, drink a soda, visit the bathroom, + or whatever while it compiles. It doesn't take very + long to compile, so you don't really need to waste too + much time waiting... + +6) Run 'make install' or 'make PREFIX=/target install' to + install busybox and all the needed links. Some people + will prefer to install using hardlinks and will instead + want to run 'make install-hardlinks'.... diff --git a/busybox/LICENSE b/busybox/LICENSE new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/busybox/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + 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..3e2b3ef18 --- /dev/null +++ b/busybox/Makefile @@ -0,0 +1,315 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +#-------------------------------------------------------------- +# You shouldn't need to mess with anything beyond this point... +#-------------------------------------------------------------- +noconfig_targets := menuconfig config oldconfig randconfig \ + defconfig allyesconfig allnoconfig clean distclean \ + release tags + +ifndef TOPDIR +TOPDIR=$(CURDIR)/ +endif +ifndef top_srcdir +top_srcdir=$(CURDIR) +endif +ifndef top_builddir +top_builddir=$(CURDIR) +endif + +srctree=$(top_srcdir) +vpath %/Config.in $(srctree) + +include $(top_builddir)/Rules.mak + +DIRS:=applets archival archival/libunarchive coreutils console-tools \ + debianutils editors findutils init miscutils modutils networking \ + networking/libiproute networking/udhcp procps loginutils shell \ + sysklogd util-linux libpwdgrp coreutils/libcoreutils libbb + +SRC_DIRS:=$(patsubst %,$(top_srcdir)/%,$(DIRS)) + +ifeq ($(strip $(CONFIG_SELINUX)),y) +CFLAGS += -I/usr/include/selinux +LIBRARIES += -lsecure +endif + +CONFIG_CONFIG_IN = $(top_srcdir)/sysdeps/$(TARGET_OS)/Config.in +CONFIG_DEFCONFIG = $(top_srcdir)/sysdeps/$(TARGET_OS)/defconfig + +ALL_DIRS:= $(DIRS) scripts/config +ALL_MAKEFILES:=$(patsubst %,%/Makefile,$(ALL_DIRS)) + +ifeq ($(KBUILD_SRC),) + +ifdef O + ifeq ("$(origin O)", "command line") + KBUILD_OUTPUT := $(O) + endif +endif + +# That's our default target when none is given on the command line +.PHONY: _all +_all: + +ifneq ($(KBUILD_OUTPUT),) +# Invoke a second make in the output directory, passing relevant variables +# check that the output directory actually exists +saved-output := $(KBUILD_OUTPUT) +KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd) +$(if $(wildcard $(KBUILD_OUTPUT)),, \ + $(error output directory "$(saved-output)" does not exist)) + +.PHONY: $(MAKECMDGOALS) + +$(filter-out _all,$(MAKECMDGOALS)) _all: $(KBUILD_OUTPUT)/Rules.mak $(KBUILD_OUTPUT)/Makefile + $(MAKE) -C $(KBUILD_OUTPUT) \ + top_srcdir=$(CURDIR) \ + top_builddir=$(KBUILD_OUTPUT) \ + TOPDIR=$(KBUILD_OUTPUT) \ + KBUILD_SRC=$(CURDIR) \ + -f $(CURDIR)/Makefile $@ + +$(KBUILD_OUTPUT)/Rules.mak: + @echo > $@ + @echo top_srcdir=$(CURDIR) >> $@ + @echo top_builddir=$(KBUILD_OUTPUT) >> $@ + @echo include $(top_srcdir)/Rules.mak >> $@ + +$(KBUILD_OUTPUT)/Makefile: + @echo > $@ + @echo top_srcdir=$(CURDIR) >> $@ + @echo top_builddir=$(KBUILD_OUTPUT) >> $@ + @echo KBUILD_SRC='$$(top_srcdir)' >> $@ + @echo include '$$(KBUILD_SRC)'/Makefile >> $@ + +# Leave processing to above invocation of make +skip-makefile := 1 +endif # ifneq ($(KBUILD_OUTPUT),) +endif # ifeq ($(KBUILD_SRC),) + +ifeq ($(skip-makefile),) + +_all: all + +ifeq ($(strip $(HAVE_DOT_CONFIG)),y) + +all: busybox busybox.links doc + +all_tree: $(ALL_MAKEFILES) + +$(ALL_MAKEFILES): %/Makefile: $(top_srcdir)/%/Makefile + d=`dirname $@`; [ -d "$$d" ] || mkdir -p "$$d"; cp $< $@ + +# In this section, we need .config +-include $(top_builddir)/.config.cmd +include $(patsubst %,%/Makefile.in, $(SRC_DIRS)) +-include $(top_builddir)/.depend + +busybox: $(ALL_MAKEFILES) .depend include/config.h $(libraries-y) + $(CC) $(LDFLAGS) -o $@ -Wl,--start-group $(libraries-y) $(LIBRARIES) -Wl,--end-group + $(STRIPCMD) $@ + +busybox.links: $(top_srcdir)/applets/busybox.mkll include/config.h $(top_srcdir)/include/applets.h + - $(SHELL) $^ >$@ + +install: applets/install.sh busybox busybox.links + $(SHELL) $< $(PREFIX) +ifeq ($(strip $(CONFIG_FEATURE_SUID)),y) + @echo + @echo + @echo -------------------------------------------------- + @echo You will probably need to make your busybox binary + @echo setuid root to ensure all configured applets will + @echo work properly. + @echo -------------------------------------------------- + @echo +endif + +uninstall: busybox.links + rm -f $(PREFIX)/bin/busybox + for i in `cat busybox.links` ; do rm -f $(PREFIX)$$i; done + +install-hardlinks: applets/install.sh busybox busybox.links + $(SHELL) $< $(PREFIX) --hardlinks + +check: busybox + bindir=$(top_builddir) srcdir=$(top_srcdir)/testsuite \ + $(top_srcdir)/testsuite/runtest + +# Documentation Targets +doc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html + +docs/busybox.pod : $(top_srcdir)/docs/busybox_header.pod $(top_srcdir)/include/usage.h $(top_srcdir)/docs/busybox_footer.pod + -mkdir -p docs + - ( cat $(top_srcdir)/docs/busybox_header.pod; \ + $(top_srcdir)/docs/autodocifier.pl $(top_srcdir)/include/usage.h; \ + cat $(top_srcdir)/docs/busybox_footer.pod ) > docs/busybox.pod + +docs/BusyBox.txt: docs/busybox.pod + @echo + @echo BusyBox Documentation + @echo + -mkdir -p docs + -pod2text $< > $@ + +docs/BusyBox.1: docs/busybox.pod + - mkdir -p docs + - pod2man --center=BusyBox --release="version $(VERSION)" \ + $< > $@ + +docs/BusyBox.html: docs/busybox.net/BusyBox.html + - mkdir -p docs + -@ rm -f docs/BusyBox.html + -@ cp docs/busybox.net/BusyBox.html docs/BusyBox.html + +docs/busybox.net/BusyBox.html: docs/busybox.pod + -@ mkdir -p docs/busybox.net + - pod2html --noindex $< > \ + docs/busybox.net/BusyBox.html + -@ rm -f pod2htm* + +# The nifty new buildsystem stuff +scripts/mkdep: $(top_srcdir)/scripts/mkdep.c + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< + +scripts/split-include: $(top_srcdir)/scripts/split-include.c + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< + +.depend: scripts/mkdep + rm -f .depend .hdepend; + mkdir -p include/config; + scripts/mkdep -I include -- \ + `find $(top_srcdir) -name \*.c -print | sed -e "s,^./,,"` >> .depend; + scripts/mkdep -I include -- \ + `find $(top_srcdir) -name \*.h -print | sed -e "s,^./,,"` >> .hdepend; + +depend dep: include/config.h .depend + +include/config/MARKER: depend scripts/split-include + scripts/split-include include/config.h include/config + @ touch include/config/MARKER + +include/config.h: .config + @if [ ! -x $(top_builddir)/scripts/config/conf ] ; then \ + $(MAKE) -C scripts/config conf; \ + fi; + @$(top_builddir)/scripts/config/conf -o $(CONFIG_CONFIG_IN) + +finished2: + @echo + @echo Finished installing... + @echo + +else # ifeq ($(strip $(HAVE_DOT_CONFIG)),y) + +all: menuconfig + +# configuration +# --------------------------------------------------------------------------- + +$(ALL_MAKEFILES): %/Makefile: $(top_srcdir)/%/Makefile + d=`dirname $@`; [ -d "$$d" ] || mkdir -p "$$d"; cp $< $@ + +scripts/config/conf: scripts/config/Makefile Rules.mak + $(MAKE) -C scripts/config conf + -@if [ ! -f .config ] ; then \ + cp $(CONFIG_DEFCONFIG) .config; \ + fi + +scripts/config/mconf: scripts/config/Makefile Rules.mak + $(MAKE) -C scripts/config ncurses conf mconf + -@if [ ! -f .config ] ; then \ + cp $(CONFIG_DEFCONFIG) .config; \ + fi + +menuconfig: scripts/config/mconf + @./scripts/config/mconf $(CONFIG_CONFIG_IN) + +config: scripts/config/conf + @./scripts/config/conf $(CONFIG_CONFIG_IN) + +oldconfig: scripts/config/conf + @./scripts/config/conf -o $(CONFIG_CONFIG_IN) + +randconfig: scripts/config/conf + @./scripts/config/conf -r $(CONFIG_CONFIG_IN) + +allyesconfig: scripts/config/conf + @./scripts/config/conf -y $(CONFIG_CONFIG_IN) + sed -i -e "s/^CONFIG_DEBUG.*/# CONFIG_DEBUG is not set/" .config + sed -i -e "s/^USING_CROSS_COMPILER.*/# USING_CROSS_COMPILER is not set/" .config + sed -i -e "s/^CONFIG_STATIC.*/# CONFIG_STATIC is not set/" .config + sed -i -e "s/^CONFIG_SELINUX.*/# CONFIG_SELINUX is not set/" .config + @./scripts/config/conf -o $(CONFIG_CONFIG_IN) + +allnoconfig: scripts/config/conf + @./scripts/config/conf -n $(CONFIG_CONFIG_IN) + +defconfig: scripts/config/conf + @./scripts/config/conf -d $(CONFIG_CONFIG_IN) + +clean: + - rm -f docs/busybox.dvi docs/busybox.ps \ + docs/busybox.pod docs/busybox.net/busybox.html \ + docs/busybox pod2htm* *.gdb *.elf *~ core .*config.log \ + docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html \ + docs/busybox.net/BusyBox.html busybox.links libbb/loop.h \ + .config.old .hdepend busybox + - rm -rf _install + - find . -name .\*.flags -exec rm -f {} \; + - find . -name \*.o -exec rm -f {} \; + - find . -name \*.a -exec rm -f {} \; + +distclean: clean + - rm -f scripts/split-include scripts/mkdep + - rm -rf include/config include/config.h + - find . -name .depend -exec rm -f {} \; + rm -f .config .config.old .config.cmd + - $(MAKE) -C scripts/config clean + +release: distclean #doc + cd ..; \ + rm -rf $(PROG)-$(VERSION); \ + cp -a busybox $(PROG)-$(VERSION); \ + \ + find $(PROG)-$(VERSION)/ -type d \ + -name CVS \ + -print \ + -exec rm -rf {} \; ; \ + \ + find $(PROG)-$(VERSION)/ -type f \ + -name .\#* \ + -print \ + -exec rm -f {} \; ; \ + \ + tar -cvzf $(PROG)-$(VERSION).tar.gz $(PROG)-$(VERSION)/; + +tags: + ctags -R . + + +endif # ifeq ($(strip $(HAVE_DOT_CONFIG)),y) + +endif # ifeq ($(skip-makefile),) + +.PHONY: dummy subdirs release distclean clean config oldconfig \ + menuconfig tags check test depend buildtree + diff --git a/busybox/README b/busybox/README new file mode 100644 index 000000000..bf2ae6f3f --- /dev/null +++ b/busybox/README @@ -0,0 +1,127 @@ +Please see the LICENSE file for details on copying and usage. + +BusyBox combines tiny versions of many common UNIX utilities into a single +small executable. It provides minimalist replacements for most of the utilities +you usually find in GNU coreutils, util-linux, etc. The utilities in BusyBox +generally have fewer options than their full-featured GNU cousins; however, the +options that are included provide the expected functionality and behave very +much like their GNU counterparts. + +BusyBox has been written with size-optimization and limited resources in mind. +It is also extremely modular so you can easily include or exclude commands (or +features) at compile time. This makes it easy to customize your embedded +systems. To create a working system, just add /dev, /etc, and a Linux kernel. +BusyBox provides a fairly complete POSIX environment for any small or embedded +system. + +BusyBox is extremely configurable. This allows you to include only the +components you need, thereby reducing binary size. Run 'make config' or +'make menuconfig' to select the functionality that you wish to enable. + +After the build is complete, a busybox.links file is generated. This is +used by 'make install' to create symlinks to the BusyBox binary for all +compiled in functions. By default, 'make install' will place the symlink +forest into `pwd`/_install unless you have defined the PREFIX environment +variable (i.e., 'make PREFIX=/tmp/foo install') + +If you wish to install hard links, rather than symlinks, you can use +'make PREFIX=/tmp/foo install-hardlinks' instead. + +---------------- + +Supported architectures: + + BusyBox in general will build on any architecture supported by gcc. + Kernel module loading for 2.2 and 2.4 Linux kernels is currently + limited to ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, + S390, SH3/4/5, Sparc, v850e, and x86_64 for 2.4.x kernels. For 2.6.x + kernels, kernel module loading support should work on all architectures. + + +Supported C Libraries: + + uClibc and glibc are supported. People have been looking at newlib and + dietlibc, but they are currently considered unsupported, untested, or + worse. Linux-libc5 is no longer supported -- you should probably use uClibc + instead if you want a small C library. + +Supported kernels: + + Full functionality requires Linux 2.2.x or better. A large fraction of the + code should run on just about anything. While the current code is fairly + Linux specific, it should be fairly easy to port the majority of the code + to support, say, FreeBSD or Solaris, or Mac OS X, or even Windows (if you + are into that sort of thing). + +---------------- + +Getting help: + +When you find you need help, you can check out the BusyBox mailing list +archives at http://busybox.net/lists/busybox/ or even join +the mailing list if you are interested. + +---------------- + +Bugs: + +If you find bugs, please submit a detailed bug report to the BusyBox mailing +list at busybox@mail.busybox.net. A well-written bug report should include a +transcript of a shell session that demonstrates the bad behavior and enables +anyone else to duplicate the bug on their own machine. The following is such +an example: + + To: busybox@mail.busybox.net + From: diligent@testing.linux.org + Subject: /bin/date doesn't work + + Package: BusyBox + Version: 1.00 + + When I execute BusyBox 'date' it produces unexpected results. + With GNU date I get the following output: + + $ date + Fri Oct 8 14:19:41 MDT 2004 + + But when I use BusyBox date I get this instead: + + $ date + illegal instruction + + I am using Debian unstable, kernel version 2.4.25-vrs2 on a Netwinder, + and the latest uClibc from CVS. Thanks for the wonderful program! + + -Diligent + +Note the careful description and use of examples showing not only what BusyBox +does, but also a counter example showing what an equivalent GNU app does. Bug +reports lacking such detail may never be fixed... Thanks for understanding. + +---------------- + +Downloads: + +Source for the latest released version, as well as daily snapshots, can always +be downloaded from + http://busybox.net/downloads/ + +---------------- + +CVS: + +BusyBox now has its own publicly browsable CVS tree at: + http://busybox.net/cgi-bin/cvsweb/busybox/ + +Anonymous CVS access is available. For instructions, check out: + http://busybox.net/cvs_anon.html + +For those that are actively contributing there is even CVS write access: + http://busybox.net/cvs_write.html + +---------------- + +Please feed suggestions, bug reports, insults, and bribes back to: + Erik Andersen + + diff --git a/busybox/Rules.mak b/busybox/Rules.mak new file mode 100644 index 000000000..d04d4b9f2 --- /dev/null +++ b/busybox/Rules.mak @@ -0,0 +1,196 @@ +# Rules.make for busybox +# +# Copyright (C) 1999-2004 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 +# + +#-------------------------------------------------------- +PROG := busybox +VERSION := 1.00 +BUILDTIME := $(shell TZ=UTC date -u "+%Y.%m.%d-%H:%M%z") + + +#-------------------------------------------------------- +# With a modern GNU make(1) (highly recommended, that's what all the +# developers use), all of the following configuration values can be +# overridden at the command line. For example: +# make CROSS=powerpc-linux- BB_SRC_DIR=$HOME/busybox PREFIX=/mnt/app +#-------------------------------------------------------- + +# If you are running a cross compiler, you will want to set 'CROSS' +# to something more interesting... Target architecture is determined +# by asking the CC compiler what arch it compiles things for, so unless +# your compiler is broken, you should not need to specify TARGET_ARCH +CROSS =$(subst ",, $(strip $(CROSS_COMPILER_PREFIX))) +CC = $(CROSS)gcc +AR = $(CROSS)ar +AS = $(CROSS)as +LD = $(CROSS)ld +NM = $(CROSS)nm +STRIP = $(CROSS)strip +CPP = $(CC) -E +# MAKEFILES = $(top_builddir)/.config + +# What OS are you compiling busybox for? This allows you to include +# OS specific things, syscall overrides, etc. +TARGET_OS=linux + +# Select the compiler needed to build binaries for your development system +HOSTCC = gcc +HOSTCFLAGS= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer + +# Ensure consistent sort order, 'gcc -print-search-dirs' behavior, etc. +LC_ALL:= C + +# If you want to add some simple compiler switches (like -march=i686), +# especially from the command line, use this instead of CFLAGS directly. +# For optimization overrides, it's better still to set OPTIMIZATION. +CFLAGS_EXTRA=$(subst ",, $(strip $(EXTRA_CFLAGS_OPTIONS))) + +# If you have a "pristine" source directory, point BB_SRC_DIR to it. +# Experimental and incomplete; tell the mailing list +# if you do or don't like it so far. +BB_SRC_DIR= + +# To compile vs some other alternative libc, you may need to use/adjust +# the following lines to meet your needs... +# +# If you are using Red Hat 6.x with the compatible RPMs (for developing under +# Red Hat 5.x and glibc 2.0) uncomment the following. Be sure to read about +# using the compatible RPMs (compat-*) at http://www.redhat.com ! +#LIBCDIR:=/usr/i386-glibc20-linux +# +# For other libraries, you are on your own. But these may (or may not) help... +#LDFLAGS+=-nostdlib +#LIBRARIES:=$(LIBCDIR)/lib/libc.a -lgcc +#CROSS_CFLAGS+=-nostdinc -I$(LIBCDIR)/include -I$(GCCINCDIR) +#GCCINCDIR:=$(shell gcc -print-search-dirs | sed -ne "s/install: \(.*\)/\1include/gp") + +WARNINGS=-Wall -Wstrict-prototypes -Wshadow +CFLAGS=-I$(top_builddir)/include -I$(top_srcdir)/include -I$(srcdir) +ARFLAGS=-r + +#-------------------------------------------------------- +export VERSION BUILDTIME TOPDIR HOSTCC HOSTCFLAGS CROSS CC AR AS LD NM STRIP CPP +ifeq ($(strip $(TARGET_ARCH)),) +TARGET_ARCH=$(shell $(CC) -dumpmachine | sed -e s'/-.*//' \ + -e 's/i.86/i386/' \ + -e 's/sparc.*/sparc/' \ + -e 's/arm.*/arm/g' \ + -e 's/m68k.*/m68k/' \ + -e 's/ppc/powerpc/g' \ + -e 's/v850.*/v850/g' \ + -e 's/sh[234]/sh/' \ + -e 's/mips-.*/mips/' \ + -e 's/mipsel-.*/mipsel/' \ + -e 's/cris.*/cris/' \ + ) +endif + +# Pull in the user's busybox configuration +ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),) +-include $(top_builddir)/.config +endif + +# A nifty macro to make testing gcc features easier +check_gcc=$(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; \ + then echo "$(1)"; else echo "$(2)"; fi) + +#-------------------------------------------------------- +# Arch specific compiler optimization stuff should go here. +# Unless you want to override the defaults, do not set anything +# for OPTIMIZATION... + +# use '-Os' optimization if available, else use -O2 +OPTIMIZATION= +OPTIMIZATION=${call check_gcc,-Os,-O2} + +# Some nice architecture specific optimizations +ifeq ($(strip $(TARGET_ARCH)),arm) + OPTIMIZATION+=-fstrict-aliasing +endif +ifeq ($(strip $(TARGET_ARCH)),i386) + OPTIMIZATION+=$(call check_gcc,-march=i386,) + OPTIMIZATION+=$(call check_gcc,-mpreferred-stack-boundary=2,) + OPTIMIZATION+=$(call check_gcc,-falign-functions=0 -falign-jumps=0 -falign-loops=0,\ + -malign-functions=0 -malign-jumps=0 -malign-loops=0) +endif +OPTIMIZATIONS=$(OPTIMIZATION) -fomit-frame-pointer + +# +#-------------------------------------------------------- +# If you're going to do a lot of builds with a non-vanilla configuration, +# it makes sense to adjust parameters above, so you can type "make" +# by itself, instead of following it by the same half-dozen overrides +# every time. The stuff below, on the other hand, is probably less +# prone to casual user adjustment. +# + +ifeq ($(strip $(CONFIG_LFS)),y) + # For large file summit support + CFLAGS+=-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +endif +ifeq ($(strip $(CONFIG_DMALLOC)),y) + # For testing mem leaks with dmalloc + CFLAGS+=-DDMALLOC + LIBRARIES:=-ldmalloc +else + ifeq ($(strip $(CONFIG_EFENCE)),y) + LIBRARIES:=-lefence + endif +endif +ifeq ($(strip $(CONFIG_DEBUG)),y) + CFLAGS +=$(WARNINGS) -g -D_GNU_SOURCE + LDFLAGS +=-Wl,-warn-common + STRIPCMD:=/bin/true -Not_stripping_since_we_are_debugging +else + CFLAGS+=$(WARNINGS) $(OPTIMIZATIONS) -D_GNU_SOURCE -DNDEBUG + LDFLAGS += -s -Wl,-warn-common + STRIPCMD:=$(STRIP) --remove-section=.note --remove-section=.comment +endif +ifeq ($(strip $(CONFIG_STATIC)),y) + LDFLAGS += --static +endif + +ifeq ($(strip $(PREFIX)),) + PREFIX:=`pwd`/_install +endif + +# Additional complications due to support for pristine source dir. +# Include files in the build directory should take precedence over +# the copy in BB_SRC_DIR, both during the compilation phase and the +# shell script that finds the list of object files. +# Work in progress by . +# +ifneq ($(strip $(BB_SRC_DIR)),) + VPATH:=$(BB_SRC_DIR) +endif + +OBJECTS:=$(APPLET_SOURCES:.c=.o) busybox.o usage.o applets.o +CFLAGS += $(CROSS_CFLAGS) +ifdef BB_INIT_SCRIPT + CFLAGS += -DINIT_SCRIPT='"$(BB_INIT_SCRIPT)"' +endif + +# Put user-supplied flags at the end, where they +# have a chance of winning. +CFLAGS += $(CFLAGS_EXTRA) + +.PHONY: dummy + + +.EXPORT_ALL_VARIABLES: + diff --git a/busybox/TODO b/busybox/TODO new file mode 100644 index 000000000..ffffd4f53 --- /dev/null +++ b/busybox/TODO @@ -0,0 +1,11 @@ +Busybox TODO + +Stuff that needs to be done + +---- +tr - missing SuS3 features in busybox 1.0pre10 + +tr doesnt support [:blank:], [:digit:] or other predefined classes, [=equiv=] +support is also missing. +---- + diff --git a/busybox/applets/Makefile b/busybox/applets/Makefile new file mode 100644 index 000000000..b566e4d12 --- /dev/null +++ b/busybox/applets/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/applets +APPLETS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir).depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/applets/Makefile.in b/busybox/applets/Makefile.in new file mode 100644 index 000000000..e31bb6fd9 --- /dev/null +++ b/busybox/applets/Makefile.in @@ -0,0 +1,37 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +APPLETS_AR:=applets.a +ifndef $(APPLETS_DIR) +APPLETS_DIR:=$(top_builddir)/applets/ +endif +srcdir=$(top_srcdir)/applets + +APPLET_SRC:=applets.c busybox.c +APPLET_OBJ:= $(patsubst %.c,$(APPLETS_DIR)%.o, $(APPLET_SRC)) + +libraries-y+=$(APPLETS_DIR)$(APPLETS_AR) + +$(APPLETS_DIR)$(APPLETS_AR): $(APPLET_OBJ) + $(AR) -ro $@ $(APPLET_OBJ) + +$(APPLET_OBJ): $(top_builddir)/.config +$(APPLET_OBJ): $(APPLETS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/applets/applets.c b/busybox/applets/applets.c new file mode 100644 index 000000000..9db16b41d --- /dev/null +++ b/busybox/applets/applets.c @@ -0,0 +1,512 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) tons of folks. Tracking down who wrote what + * isn't something I'm going to worry about... If you wrote something + * here, please feel free to acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +const char usage_messages[] = + +#define MAKE_USAGE +#include "usage.h" + +#include "applets.h" + +; + +#undef MAKE_USAGE +#undef APPLET +#undef APPLET_NOUSAGE +#undef PROTOTYPES +#include "applets.h" + + +static struct BB_applet *applet_using; + +/* The -1 arises because of the {0,NULL,0,-1} entry above. */ +const size_t NUM_APPLETS = (sizeof (applets) / sizeof (struct BB_applet) - 1); + + +#ifdef CONFIG_FEATURE_SUID + +static void check_suid (struct BB_applet *app); + +#ifdef CONFIG_FEATURE_SUID_CONFIG + +#include +#include +#include "pwd_.h" +#include "grp_.h" + +static void parse_config_file (void); + +#define CONFIG_FILE "/etc/busybox.conf" + +/* applets [] is const, so we have to define this "override" structure */ +struct BB_suid_config +{ + struct BB_applet *m_applet; + + uid_t m_uid; + gid_t m_gid; + mode_t m_mode; + + struct BB_suid_config *m_next; +}; + +static struct BB_suid_config *suid_config; +static int suid_cfg_readable; + +#endif /* CONFIG_FEATURE_SUID_CONFIG */ + +#endif /* CONFIG_FEATURE_SUID */ + + + +extern void +bb_show_usage (void) +{ + const char *format_string; + const char *usage_string = usage_messages; + int i; + + for (i = applet_using - applets; i > 0;) { + if (!*usage_string++) { + --i; + } + } + + format_string = "%s\n\nUsage: %s %s\n\n"; + if (*usage_string == '\b') + format_string = "%s\n\nNo help available.\n\n"; + fprintf (stderr, format_string, bb_msg_full_version, applet_using->name, + usage_string); + + exit (EXIT_FAILURE); +} + +static int +applet_name_compare (const void *x, const void *y) +{ + const char *name = x; + const struct BB_applet *applet = y; + + return strcmp (name, applet->name); +} + +extern const size_t NUM_APPLETS; + +struct BB_applet * +find_applet_by_name (const char *name) +{ + return bsearch (name, applets, NUM_APPLETS, sizeof (struct BB_applet), + applet_name_compare); +} + +void +run_applet_by_name (const char *name, int argc, char **argv) +{ + static int recurse_level = 0; + extern int been_there_done_that; /* From busybox.c */ + +#ifdef CONFIG_FEATURE_SUID_CONFIG + if (recurse_level == 0) + parse_config_file (); +#endif + + recurse_level++; + /* Do a binary search to find the applet entry given the name. */ + if ((applet_using = find_applet_by_name (name)) != NULL) { + bb_applet_name = applet_using->name; + if (argv[1] && strcmp (argv[1], "--help") == 0) { + if (strcmp (applet_using->name, "busybox") == 0) { + if (argv[2]) + applet_using = find_applet_by_name (argv[2]); + else + applet_using = NULL; + } + if (applet_using) + bb_show_usage (); + been_there_done_that = 1; + busybox_main (0, NULL); + } +#ifdef CONFIG_FEATURE_SUID + check_suid (applet_using); +#endif + + exit ((*(applet_using->main)) (argc, argv)); + } + /* Just in case they have renamed busybox - Check argv[1] */ + if (recurse_level == 1) { + run_applet_by_name ("busybox", argc, argv); + } + recurse_level--; +} + + +#ifdef CONFIG_FEATURE_SUID + +#ifdef CONFIG_FEATURE_SUID_CONFIG + +/* check if u is member of group g */ +static int +ingroup (uid_t u, gid_t g) +{ + struct group *grp = getgrgid (g); + + if (grp) { + char **mem; + + for (mem = grp->gr_mem; *mem; mem++) { + struct passwd *pwd = getpwnam (*mem); + + if (pwd && (pwd->pw_uid == u)) + return 1; + } + } + return 0; +} + +#endif + + +void +check_suid (struct BB_applet *applet) +{ + uid_t ruid = getuid (); /* real [ug]id */ + uid_t rgid = getgid (); + +#ifdef CONFIG_FEATURE_SUID_CONFIG + if (suid_cfg_readable) { + struct BB_suid_config *sct; + + for (sct = suid_config; sct; sct = sct->m_next) { + if (sct->m_applet == applet) + break; + } + if (sct) { + mode_t m = sct->m_mode; + + if (sct->m_uid == ruid) /* same uid */ + m >>= 6; + else if ((sct->m_gid == rgid) || ingroup (ruid, sct->m_gid)) /* same group / in group */ + m >>= 3; + + if (!(m & S_IXOTH)) /* is x bit not set ? */ + bb_error_msg_and_die ("You have no permission to run this applet!"); + + if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { /* *both* have to be set for sgid */ + if (setegid (sct->m_gid)) + bb_error_msg_and_die + ("BusyBox binary has insufficient rights to set proper GID for applet!"); + } else + setgid (rgid); /* no sgid -> drop */ + + if (sct->m_mode & S_ISUID) { + if (seteuid (sct->m_uid)) + bb_error_msg_and_die + ("BusyBox binary has insufficient rights to set proper UID for applet!"); + } else + setuid (ruid); /* no suid -> drop */ + } else { + /* default: drop all priviledges */ + setgid (rgid); + setuid (ruid); + } + return; + } else { +#ifndef CONFIG_FEATURE_SUID_CONFIG_QUIET + static int onetime = 0; + + if (!onetime) { + onetime = 1; + fprintf (stderr, "Using fallback suid method\n"); + } +#endif + } +#endif + + if (applet->need_suid == _BB_SUID_ALWAYS) { + if (geteuid () != 0) + bb_error_msg_and_die ("This applet requires root priviledges!"); + } else if (applet->need_suid == _BB_SUID_NEVER) { + setgid (rgid); /* drop all priviledges */ + setuid (ruid); + } +} + +#ifdef CONFIG_FEATURE_SUID_CONFIG + +/* This should probably be a libbb routine. In that case, + * I'd probably rename it to something like bb_trimmed_slice. + */ +static char *get_trimmed_slice(char *s, char *e) +{ + /* First, consider the value at e to be nul and back up until we + * reach a non-space char. Set the char after that (possibly at + * the original e) to nul. */ + while (e-- > s) { + if (!isspace(*e)) { + break; + } + } + e[1] = 0; + + /* Next, advance past all leading space and return a ptr to the + * first non-space char; possibly the terminating nul. */ + return (char *) bb_skip_whitespace(s); +} + + +#define parse_error(x) { err=x; goto pe_label; } + +/* Don't depend on the tools to combine strings. */ +static const char config_file[] = CONFIG_FILE; + +/* There are 4 chars + 1 nul for each of user/group/other. */ +static const char mode_chars[] = "Ssx-\0Ssx-\0Ttx-"; + +/* We don't supply a value for the nul, so an index adjustment is + * necessary below. Also, we use unsigned short here to save some + * space even though these are really mode_t values. */ +static const unsigned short mode_mask[] = { + /* SST sst xxx --- */ + S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */ + S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */ + 0, S_IXOTH, S_IXOTH, 0 /* other */ +}; + +static void parse_config_file(void) +{ + struct BB_suid_config *sct_head; + struct BB_suid_config *sct; + struct BB_applet *applet; + FILE *f; + char *err; + char *s; + char *e; + int i, lc, section; + char buffer[256]; + struct stat st; + + assert(!suid_config); /* Should be set to NULL by bss init. */ + + if ((stat(config_file, &st) != 0) /* No config file? */ + || !S_ISREG(st.st_mode) /* Not a regular file? */ + || (st.st_uid != 0) /* Not owned by root? */ + || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ + || !(f = fopen(config_file, "r")) /* Can not open? */ + ) { + return; + } + + suid_cfg_readable = 1; + sct_head = NULL; + section = lc = 0; + + do { + s = buffer; + + if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ + if (ferror(f)) { /* Make sure it wasn't a read error. */ + parse_error("reading"); + } + fclose(f); + suid_config = sct_head; /* Success, so set the pointer. */ + return; + } + + lc++; /* Got a (partial) line. */ + + /* If a line is too long for our buffer, we consider it an error. + * The following test does mistreat one corner case though. + * If the final line of the file does not end with a newline and + * yet exactly fills the buffer, it will be treated as too long + * even though there isn't really a problem. But it isn't really + * worth adding code to deal with such an unlikely situation, and + * we do err on the side of caution. Besides, the line would be + * too long if it did end with a newline. */ + if (!strchr(s, '\n') && !feof(f)) { + parse_error("line too long"); + } + + /* Trim leading and trailing whitespace, ignoring comments, and + * check if the resulting string is empty. */ + if (!*(s = get_trimmed_slice(s, strchrnul(s, '#')))) { + continue; + } + + /* Check for a section header. */ + + if (*s == '[') { + /* Unlike the old code, we ignore leading and trailing + * whitespace for the section name. We also require that + * there are no stray characters after the closing bracket. */ + if (!(e = strchr(s, ']')) /* Missing right bracket? */ + || e[1] /* Trailing characters? */ + || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ + ) { + parse_error("section header"); + } + /* Right now we only have one section so just check it. + * If more sections are added in the future, please don't + * resort to cascading ifs with multiple strcasecmp calls. + * That kind of bloated code is all too common. A loop + * and a string table would be a better choice unless the + * number of sections is very small. */ + if (strcasecmp(s, "SUID") == 0) { + section = 1; + continue; + } + section = -1; /* Unknown section so set to skip. */ + continue; + } + + /* Process sections. */ + + if (section == 1) { /* SUID */ + /* Since we trimmed leading and trailing space above, we're + * now looking for strings of the form + * [::space::]*=[::space::]* + * where both key and value could contain inner whitespace. */ + + /* First get the key (an applet name in our case). */ + if (!!(e = strchr(s, '='))) { + s = get_trimmed_slice(s, e); + } + if (!e || !*s) { /* Missing '=' or empty key. */ + parse_error("keyword"); + } + + /* Ok, we have an applet name. Process the rhs if this + * applet is currently built in and ignore it otherwise. + * Note: This can hide config file bugs which only pop + * up when the busybox configuration is changed. */ + if ((applet = find_applet_by_name(s))) { + /* Note: We currently don't check for duplicates! + * The last config line for each applet will be the + * one used since we insert at the head of the list. + * I suppose this could be considered a feature. */ + sct = xmalloc(sizeof(struct BB_suid_config)); + sct->m_applet = applet; + sct->m_mode = 0; + sct->m_next = sct_head; + sct_head = sct; + + /* Get the specified mode. */ + + e = (char *) bb_skip_whitespace(e+1); + + for (i=0 ; i < 3 ; i++) { + const char *q; + if (!*(q = strchrnul(mode_chars + 5*i, *e++))) { + parse_error("mode"); + } + /* Adjust by -i to account for nul. */ + sct->m_mode |= mode_mask[(q - mode_chars) - i]; + } + + /* Now get the the user/group info. */ + + s = (char *) bb_skip_whitespace(e); + + /* Note: We require whitespace between the mode and the + * user/group info. */ + if ((s == e) || !(e = strchr(s, '.'))) { + parse_error("."); + } + *e++ = 0; + + /* We can't use get_ug_id here since it would exit() + * if a uid or gid was not found. Oh well... */ + { + char *e2; + + sct->m_uid = strtoul(s, &e2, 10); + if (*e2 || (s == e2)) { + struct passwd *pwd; + if (!(pwd = getpwnam(s))) { + parse_error("user"); + } + sct->m_uid = pwd->pw_uid; + } + + sct->m_gid = strtoul(e, &e2, 10); + if (*e2 || (e == e2)) { + struct group *grp; + if (!(grp = getgrnam(e))) { + parse_error("group"); + } + sct->m_gid = grp->gr_gid; + } + } + } + continue; + } + + /* Unknown sections are ignored. */ + + /* Encountering configuration lines prior to seeing a + * section header is treated as an error. This is how + * the old code worked, but it may not be desirable. + * We may want to simply ignore such lines in case they + * are used in some future version of busybox. */ + if (!section) { + parse_error("keyword outside section"); + } + + } while (1); + + pe_label: + fprintf(stderr, "Parse error in %s, line %d: %s\n", + config_file, lc, err); + + fclose(f); + /* Release any allocated memory before returning. */ + while (sct_head) { + sct = sct_head->m_next; + free(sct_head); + sct_head = sct; + } + return; +} + +#endif + +#endif + +/* END CODE */ +/* + Local Variables: + c-file-style: "linux" + c-basic-offset: 4 + tab-width: 4 +End: +*/ diff --git a/busybox/applets/busybox.c b/busybox/applets/busybox.c new file mode 100644 index 000000000..dbb5e176b --- /dev/null +++ b/busybox/applets/busybox.c @@ -0,0 +1,193 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include "busybox.h" +#ifdef CONFIG_LOCALE_SUPPORT +#include +#endif + +int been_there_done_that = 0; /* Also used in applets.c */ +const char *bb_applet_name; + +#ifdef CONFIG_FEATURE_INSTALLER +/* + * directory table + * this should be consistent w/ the enum, busybox.h::Location, + * or else... + */ +static const char usr_bin [] ="/usr/bin"; +static const char usr_sbin[] ="/usr/sbin"; + +static const char* const install_dir[] = { + &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */ + &usr_bin [4], /* "/bin" */ + &usr_sbin[4], /* "/sbin" */ + usr_bin, + usr_sbin +}; + +/* abstract link() */ +typedef int (*__link_f)(const char *, const char *); + +/* + * Where in the filesystem is this busybox? + * [return] + * malloc'd string w/ full pathname of busybox's location + * NULL on failure + */ +static inline char *busybox_fullpath(void) +{ + return xreadlink("/proc/self/exe"); +} + +/* create (sym)links for each applet */ +static void install_links(const char *busybox, int use_symbolic_links) +{ + __link_f Link = link; + + char *fpc; + int i; + int rc; + + if (use_symbolic_links) + Link = symlink; + + for (i = 0; applets[i].name != NULL; i++) { + fpc = concat_path_file( + install_dir[applets[i].location], applets[i].name); + rc = Link(busybox, fpc); + if (rc!=0 && errno!=EEXIST) { + bb_perror_msg("%s", fpc); + } + free(fpc); + } +} + +#endif /* CONFIG_FEATURE_INSTALLER */ + +int main(int argc, char **argv) +{ + const char *s; + + bb_applet_name = argv[0]; + + if (bb_applet_name[0] == '-') + bb_applet_name++; + + for (s = bb_applet_name; *s != '\0';) { + if (*s++ == '/') + bb_applet_name = s; + } + +#ifdef CONFIG_LOCALE_SUPPORT +#ifdef CONFIG_INIT + if(getpid()!=1) /* Do not set locale for `init' */ +#endif + { + setlocale(LC_ALL, ""); + } +#endif + + run_applet_by_name(bb_applet_name, argc, argv); + bb_error_msg_and_die("applet not found"); +} + + +int busybox_main(int argc, char **argv) +{ + int col = 0, len, i; + +#ifdef CONFIG_FEATURE_INSTALLER + /* + * This style of argument parsing doesn't scale well + * in the event that busybox starts wanting more --options. + * If someone has a cleaner approach, by all means implement it. + */ + if (argc > 1 && (strcmp(argv[1], "--install") == 0)) { + int use_symbolic_links = 0; + int rc = 0; + char *busybox; + + /* to use symlinks, or not to use symlinks... */ + if (argc > 2) { + if ((strcmp(argv[2], "-s") == 0)) { + use_symbolic_links = 1; + } + } + + /* link */ + busybox = busybox_fullpath(); + if (busybox) { + install_links(busybox, use_symbolic_links); + free(busybox); + } else { + rc = 1; + } + return rc; + } +#endif /* CONFIG_FEATURE_INSTALLER */ + + argc--; + + /* If we've already been here once, exit now */ + if (been_there_done_that == 1 || argc < 1) { + const struct BB_applet *a = applets; + int output_width = 60; + +#ifdef CONFIG_FEATURE_AUTOWIDTH + /* Obtain the terminal width. */ + get_terminal_width_height(0, &output_width, NULL); + /* leading tab and room to wrap */ + output_width -= 20; +#endif + + fprintf(stderr, "%s\n\n" + "Usage: busybox [function] [arguments]...\n" + " or: [function] [arguments]...\n\n" + "\tBusyBox is a multi-call binary that combines many common Unix\n" + "\tutilities into a single executable. Most people will create a\n" + "\tlink to busybox for each function they wish to use, and BusyBox\n" + "\twill act like whatever it was invoked as.\n" + "\nCurrently defined functions:\n", bb_msg_full_version); + + while (a->name != 0) { + col += + fprintf(stderr, "%s%s", ((col == 0) ? "\t" : ", "), + (a++)->name); + if (col > output_width && a->name != 0) { + fprintf(stderr, ",\n"); + col = 0; + } + } + fprintf(stderr, "\n\n"); + exit(0); + } + + /* Flag that we've been here already */ + been_there_done_that = 1; + + /* Move the command line down a notch */ + /* Preserve pointers so setproctitle() works consistently */ + len = argv[argc] + strlen(argv[argc]) - argv[1]; + memmove(argv[0], argv[1], len); + memset(argv[0] + len, 0, argv[1] - argv[0]); + + /* Fix up the argv pointers */ + len = argv[1] - argv[0]; + memmove(argv, argv + 1, sizeof(char *) * (argc + 1)); + for (i = 0; i < argc; i++) + argv[i] -= len; + + return (main(argc, argv)); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/applets/busybox.mkll b/busybox/applets/busybox.mkll new file mode 100755 index 000000000..5b6677d03 --- /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:-include/config.h} +APPLETS_H=${2:-include/applets.h} +gcc -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H | + awk '/^[ \t]*LINK/{ + dir=substr($2,8) + gsub("_","/",dir) + if(dir=="/ROOT") dir="" + file=$3 + gsub("\"","",file) + if (file=="busybox") next + print tolower(dir) "/" file + }' diff --git a/busybox/applets/install.sh b/busybox/applets/install.sh new file mode 100755 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/archival/Config.in b/busybox/archival/Config.in new file mode 100644 index 000000000..db358db08 --- /dev/null +++ b/busybox/archival/Config.in @@ -0,0 +1,258 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Archival Utilities" + +config CONFIG_AR + bool "ar" + default n + help + ar is an archival utility program used to create, modify, and + extract contents from archives. An archive is a single file holding + a collection of other files in a structure that makes it possible to + retrieve the original individual files (called archive members). + The original files' contents, mode (permissions), timestamp, owner, + and group are preserved in the archive, and can be restored on + extraction. + + The stored filename is limited to 15 characters. (for more information + see long filename support). + ar has 60 bytes of overheads for every stored file. + + This implementation of ar can extract archives, it cannot create or + modify them. + On an x86 system, the ar applet adds about 1K. + + Unless you have a specific application which requires ar, you should + probably say N here. + +config CONFIG_FEATURE_AR_LONG_FILENAMES + bool " Enable support for long filenames (not need for debs)" + default n + depends on CONFIG_AR + help + By default the ar format can only store the first 15 characters of the + filename, this option removes that limitation. + It supports the GNU ar long filename method which moves multiple long + filenames into a the data section of a new ar entry. + +config CONFIG_BUNZIP2 + bool "bunzip2" + default n + help + bunzip2 is a compression utility using the Burrows-Wheeler block + sorting text compression algorithm, and Huffman coding. Compression + is generally considerably better than that achieved by more + conventional LZ77/LZ78-based compressors, and approaches the + performance of the PPM family of statistical compressors. + + The BusyBox bunzip2 applet is limited to de-compression only. + On an x86 system, this applet adds about 11K. + + Unless you have a specific application which requires bunzip2, you + should probably say N here. + +config CONFIG_CPIO + bool "cpio" + default n + help + cpio is an archival utility program used to create, modify, and extract + contents from archives. + cpio has 110 bytes of overheads for every stored file. + + This implementation of cpio can extract cpio archives created in the + "newc" or "crc" format, it cannot create or modify them. + + Unless you have a specific application which requires cpio, you should + probably say N here. + +config CONFIG_DPKG + bool "dpkg" + default n + help + dpkg is a medium-level tool to install, build, remove and manage Debian packages. + + This implementation of dpkg has a number of limitations, you should use the + official dpkg if possible. + +config CONFIG_DPKG_DEB + bool "dpkg_deb" + default n + help + dpkg-deb packs, unpacks and provides information about Debian archives. + + This implementation of dpkg-deb cannot pack archives. + + Unless you have a specific application which requires dpkg-deb, you should + probably say N here. + +config CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY + bool " extract only (-x)" + default n + depends on CONFIG_DPKG_DEB + help + This reduces dpkg-deb to the equivalent of "ar -p data.tar.gz | tar -zx". + However it saves space as none of the extra dpkg-deb, ar or tar options are + needed, they are linked to internally. + +config CONFIG_GUNZIP + bool "gunzip" + default n + help + gunzip is used to decompress archives created by gzip. + You can use the `-t' option to test the integrity of + an archive, without decompressing it. + +config CONFIG_FEATURE_GUNZIP_UNCOMPRESS + bool " Uncompress support" + default n + depends on CONFIG_GUNZIP + help + Enable if you want gunzip to have the ability to decompress + archives created by the program compress (not much + used anymore). + +config CONFIG_GZIP + bool "gzip" + default n + help + gzip is used to compress files. + It's probably the most widely used UNIX compression program. + +config CONFIG_RPM2CPIO + bool "rpm2cpio" + default n + help + Converts an RPM file into a CPIO archive. + +config CONFIG_RPM + bool "rpm" + default n + help + Mini RPM applet - queries and extracts + +config CONFIG_TAR + bool "tar" + default n + help + tar is an archiving program. It's commonly used with gzip to + create compressed archives. It's probably the most widely used + UNIX archive program. + +config CONFIG_FEATURE_TAR_CREATE + bool " Enable archive creation" + default y + depends on CONFIG_TAR + help + If you enable this option you'll be able to create + tar archives using the `-c' option. + +config CONFIG_FEATURE_TAR_BZIP2 + bool " Enable -j option to handle .tar.bz2 files" + default n + depends on CONFIG_TAR + help + If you enable this option you'll be able to extract + archives compressed with bzip2. + +config CONFIG_FEATURE_TAR_FROM + bool " Enable -X (exclude from) and -T (include from) options)" + default n + depends on CONFIG_TAR + help + If you enable this option you'll be able to specify + a list of files to include or exclude from an archive. + +config CONFIG_FEATURE_TAR_GZIP + bool " Enable -z option" + default y + depends on CONFIG_TAR + help + If you enable this option tar will be able to call gzip, + when creating or extracting tar gziped archives. + +config CONFIG_FEATURE_TAR_COMPRESS + bool " Enable -Z option" + default n + depends on CONFIG_TAR + help + If you enable this option tar will be able to call uncompress, + when extracting .tar.Z archives. + +config CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY + bool " Enable support for old tar header format" + default N + depends on CONFIG_TAR + help + This option is required to unpack archives created in + the old GNU format; help to kill this old format by + repacking your ancient archives with the new format. + +config CONFIG_FEATURE_TAR_GNU_EXTENSIONS + bool " Enable support for some GNU tar extensions" + default y + depends on CONFIG_TAR + help + With this option busybox supports GNU long filenames and + linknames. + +config CONFIG_FEATURE_TAR_LONG_OPTIONS + bool " Enable long options" + default n + depends on CONFIG_TAR + help + Enable use of long options, increases size by about 400 Bytes + +config CONFIG_UNCOMPRESS + bool "uncompress" + default n + help + uncompress is used to decompress archives created by compress. + Not much used anymore, replaced by gzip/gunzip. + +config CONFIG_UNZIP + bool "unzip" + default n + help + unzip will list or extract files from a ZIP archive, + commonly found on DOS/WIN systems. The default behavior + (with no options) is to extract the archive into the + current directory. Use the `-d' option to extract to a + directory of your choice. + +comment "Common options for cpio and tar" + depends on CONFIG_CPIO || CONFIG_TAR + +config CONFIG_FEATURE_UNARCHIVE_TAPE + bool " Enable tape drive support" + default n + depends on CONFIG_CPIO || CONFIG_TAR + help + I don't think this is needed anymore. + +comment "Common options for dpkg and dpkg_deb" + depends on CONFIG_DPKG || CONFIG_DPKG_DEB + +config CONFIG_FEATURE_DEB_TAR_GZ + bool " gzip debian packages (normal)" + default y if CONFIG_DPKG || CONFIG_DPKG_DEB + depends on CONFIG_DPKG || CONFIG_DPKG_DEB + help + This is the default compression method inside the debian ar file. + + If you want compatibility with standard .deb's you should say yes here. + +config CONFIG_FEATURE_DEB_TAR_BZ2 + bool " bzip2 debian packages" + default n + depends on CONFIG_DPKG || CONFIG_DPKG_DEB + help + This allows dpkg and dpkg-deb to extract deb's that are compressed internally + with bzip2 instead of gzip. + + You only want this if you are creating your own custom debian packages that + use an internal control.tar.bz2 or data.tar.bz2. + +endmenu diff --git a/busybox/archival/Makefile b/busybox/archival/Makefile new file mode 100644 index 000000000..a96daa4df --- /dev/null +++ b/busybox/archival/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +ARCHIVAL_DIR:=./ +srcdir=$(top_srcdir)/archival +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/archival/Makefile.in b/busybox/archival/Makefile.in new file mode 100644 index 000000000..76ab6cd08 --- /dev/null +++ b/busybox/archival/Makefile.in @@ -0,0 +1,48 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +ARCHIVAL_AR:=archival.a +ifndef $(ARCHIVAL_DIR) +ARCHIVAL_DIR:=$(top_builddir)/archival/ +endif +srcdir=$(top_srcdir)/archival + +ARCHIVAL-y:= +ARCHIVAL-$(CONFIG_APT_GET) += +ARCHIVAL-$(CONFIG_AR) += ar.o +ARCHIVAL-$(CONFIG_BUNZIP2) += bunzip2.o +ARCHIVAL-$(CONFIG_CPIO) += cpio.o +ARCHIVAL-$(CONFIG_DPKG) += dpkg.o +ARCHIVAL-$(CONFIG_DPKG_DEB) += dpkg_deb.o +ARCHIVAL-$(CONFIG_GUNZIP) += gunzip.o +ARCHIVAL-$(CONFIG_GZIP) += gzip.o +ARCHIVAL-$(CONFIG_RPM2CPIO) += rpm2cpio.o +ARCHIVAL-$(CONFIG_RPM) += rpm.o +ARCHIVAL-$(CONFIG_TAR) += tar.o +ARCHIVAL-$(CONFIG_UNCOMPRESS) += uncompress.o +ARCHIVAL-$(CONFIG_UNZIP) += unzip.o + +libraries-y+=$(ARCHIVAL_DIR)$(ARCHIVAL_AR) + +$(ARCHIVAL_DIR)$(ARCHIVAL_AR): $(patsubst %,$(ARCHIVAL_DIR)%, $(ARCHIVAL-y)) + $(AR) -ro $@ $(patsubst %,$(ARCHIVAL_DIR)%, $(ARCHIVAL-y)) + +$(ARCHIVAL_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/archival/ar.c b/busybox/archival/ar.c new file mode 100644 index 000000000..44c5db035 --- /dev/null +++ b/busybox/archival/ar.c @@ -0,0 +1,110 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ar implementation for busybox + * + * Copyright (C) 2000 by Glenn McGrath + * Written by Glenn McGrath 1 June 2000 + * + * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * There is no single standard to adhere to so ar may not portable + * between different systems + * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unarchive.h" +#include "busybox.h" + +static void header_verbose_list_ar(const file_header_t *file_header) +{ + const char *mode = bb_mode_string(file_header->mode); + char *mtime; + + mtime = ctime(&file_header->mtime); + mtime[16] = ' '; + memmove(&mtime[17], &mtime[20], 4); + mtime[21] = '\0'; + printf("%s %d/%d%7d %s %s\n", &mode[1], file_header->uid, file_header->gid, (int) file_header->size, &mtime[4], file_header->name); +} + +#define AR_CTX_PRINT 0x01 +#define AR_CTX_LIST 0x02 +#define AR_CTX_EXTRACT 0x04 +#define AR_OPT_PRESERVE_DATE 0x08 +#define AR_OPT_VERBOSE 0x10 +#define AR_OPT_CREATE 0x20 + +extern int ar_main(int argc, char **argv) +{ + archive_handle_t *archive_handle; + unsigned long opt; + char magic[8]; + + archive_handle = init_handle(); + + bb_opt_complementaly = "p~tx:t~px:x~pt"; + opt = bb_getopt_ulflags(argc, argv, "ptxovc"); + + if ((opt & 0x80000000UL) || (optind == argc)) { + bb_show_usage(); + } + + if (opt & AR_CTX_PRINT) { + archive_handle->action_data = data_extract_to_stdout; + } + if (opt & AR_CTX_LIST) { + archive_handle->action_header = header_list; + } + if (opt & AR_CTX_EXTRACT) { + archive_handle->action_data = data_extract_all; + } + if (opt & AR_OPT_PRESERVE_DATE) { + archive_handle->flags |= ARCHIVE_PRESERVE_DATE; + } + if (opt & AR_OPT_VERBOSE) { + archive_handle->action_header = header_verbose_list_ar; + } + if (opt & AR_OPT_CREATE) { + bb_error_msg_and_die("Archive creation not supported. Install binutils 'ar'."); + } + + archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY); + + while (optind < argc) { + archive_handle->filter = filter_accept_list; + archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind++]); + } + + archive_xread_all(archive_handle, magic, 7); + if (strncmp(magic, "!", 7) != 0) { + bb_error_msg_and_die("Invalid ar magic"); + } + archive_handle->offset += 7; + + while (get_header_ar(archive_handle) == EXIT_SUCCESS); + + return EXIT_SUCCESS; +} diff --git a/busybox/archival/bunzip2.c b/busybox/archival/bunzip2.c new file mode 100644 index 000000000..bedd38c22 --- /dev/null +++ b/busybox/archival/bunzip2.c @@ -0,0 +1,91 @@ +/* + * Modified for busybox by Glenn McGrath + * Added support output to stdout by Thomas Lundquist + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" +#include "unarchive.h" + +#define BUNZIP2_OPT_STDOUT 1 +#define BUNZIP2_OPT_FORCE 2 + +int bunzip2_main(int argc, char **argv) +{ + char *compressed_name; + /* Note: Ignore the warning about save_name being used uninitialized. + * That is not the case, but gcc has trouble working that out... */ + char *save_name; + unsigned long opt; + int status; + int src_fd; + int dst_fd; + + opt = bb_getopt_ulflags(argc, argv, "cf"); + + /* if called as bzcat force the stdout flag */ + if (bb_applet_name[2] == 'c') { + opt |= BUNZIP2_OPT_STDOUT; + } + + /* Set input filename and number */ + compressed_name = argv[optind]; + if ((compressed_name) && (compressed_name[0] != '-') && (compressed_name[1] != '\0')) { + /* Open input file */ + src_fd = bb_xopen(compressed_name, O_RDONLY); + } else { + src_fd = STDIN_FILENO; + opt |= BUNZIP2_OPT_STDOUT; + } + + /* Check that the input is sane. */ + if (isatty(src_fd) && (opt & BUNZIP2_OPT_FORCE) == 0) { + bb_error_msg_and_die("compressed data not read from terminal. Use -f to force it."); + } + + if (opt & BUNZIP2_OPT_STDOUT) { + dst_fd = STDOUT_FILENO; + } else { + int len = strlen(compressed_name) - 4; + if (strcmp(compressed_name + len, ".bz2") != 0) { + bb_error_msg_and_die("Invalid extension"); + } + save_name = bb_xstrndup(compressed_name, len); + dst_fd = bb_xopen(save_name, O_WRONLY | O_CREAT); + } + + status = uncompressStream(src_fd, dst_fd); + if(!(opt & BUNZIP2_OPT_STDOUT)) { + char *delete_name; + if (status) { + delete_name = save_name; + } else { + delete_name = compressed_name; + } + if (unlink(delete_name) < 0) { + bb_error_msg_and_die("Couldn't remove %s", delete_name); + } + } + + return status; +} diff --git a/busybox/archival/cpio.c b/busybox/archival/cpio.c new file mode 100644 index 000000000..0fbe7b8e5 --- /dev/null +++ b/busybox/archival/cpio.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cpio implementation for busybox + * + * Copyright (C) 2001 by Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Limitations: + * Doesn't check CRC's + * Only supports new ASCII and CRC formats + * + */ +#include +#include +#include +#include +#include +#include "unarchive.h" +#include "busybox.h" + +#define CPIO_OPT_EXTRACT 0x01 +#define CPIO_OPT_TEST 0x02 +#define CPIO_OPT_UNCONDITIONAL 0x04 +#define CPIO_OPT_VERBOSE 0x08 +#define CPIO_OPT_FILE 0x10 +#define CPIO_OPT_CREATE_LEADING_DIR 0x20 +#define CPIO_OPT_PRESERVE_MTIME 0x40 + +extern int cpio_main(int argc, char **argv) +{ + archive_handle_t *archive_handle; + char *cpio_filename = NULL; + unsigned long opt; + + /* Initialise */ + archive_handle = init_handle(); + archive_handle->src_fd = STDIN_FILENO; + archive_handle->seek = seek_by_char; + archive_handle->flags = ARCHIVE_EXTRACT_NEWER | ARCHIVE_PRESERVE_DATE; + + opt = bb_getopt_ulflags(argc, argv, "ituvF:dm", &cpio_filename); + + /* One of either extract or test options must be given */ + if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) { + bb_show_usage(); + } + + if (opt & CPIO_OPT_TEST) { + /* if both extract and test option are given, ignore extract option */ + if (opt & CPIO_OPT_EXTRACT) { + opt &= ~CPIO_OPT_EXTRACT; + } + archive_handle->action_header = header_list; + } + if (opt & CPIO_OPT_EXTRACT) { + archive_handle->action_data = data_extract_all; + } + if (opt & CPIO_OPT_UNCONDITIONAL) { + archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL; + archive_handle->flags &= ~ARCHIVE_EXTRACT_NEWER; + } + if (opt & CPIO_OPT_VERBOSE) { + if (archive_handle->action_header == header_list) { + archive_handle->action_header = header_verbose_list; + } else { + archive_handle->action_header = header_list; + } + } + if (cpio_filename) { /* CPIO_OPT_FILE */ + archive_handle->src_fd = bb_xopen(cpio_filename, O_RDONLY); + archive_handle->seek = seek_by_jump; + } + if (opt & CPIO_OPT_CREATE_LEADING_DIR) { + archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS; + } + + while (optind < argc) { + archive_handle->filter = filter_accept_list; + archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind]); + optind++; + } + + while (get_header_cpio(archive_handle) == EXIT_SUCCESS); + + return(EXIT_SUCCESS); +} diff --git a/busybox/archival/dpkg.c b/busybox/archival/dpkg.c new file mode 100644 index 000000000..c096518a2 --- /dev/null +++ b/busybox/archival/dpkg.c @@ -0,0 +1,1833 @@ +/* + * Mini dpkg implementation for busybox. + * This is not meant as a replacement for dpkg + * + * Written By Glenn McGrath with the help of others + * Copyright (C) 2001 by Glenn McGrath + * + * Started life as a busybox implementation of udpkg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Known difference between busybox dpkg and the official dpkg that i don't + * consider important, its worth keeping a note of differences anyway, just to + * make it easier to maintain. + * - The first value for the Confflile: field isnt placed on a new line. + * - When installing a package the Status: field is placed at the end of the + * section, rather than just after the Package: field. + * + * Bugs that need to be fixed + * - (unknown, please let me know when you find any) + * + */ + +#include +#include +#include +#include +#include +#include "unarchive.h" +#include "busybox.h" + +/* NOTE: If you vary HASH_PRIME sizes be aware, + * 1) Tweaking these will have a big effect on how much memory this program uses. + * 2) For computational efficiency these hash tables should be at least 20% + * larger than the maximum number of elements stored in it. + * 3) All _HASH_PRIME's must be a prime number or chaos is assured, if your looking + * for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt + * 4) If you go bigger than 15 bits you may get into trouble (untested) as its + * sometimes cast to an unsigned int, if you go to 16 bit you will overlap + * int's and chaos is assured, 16381 is the max prime for 14 bit field + */ + +/* NAME_HASH_PRIME, Stores package names and versions, + * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME, + * as there a lot of duplicate version numbers */ +#define NAME_HASH_PRIME 16381 +char *name_hashtable[NAME_HASH_PRIME + 1]; + +/* PACKAGE_HASH_PRIME, Maximum number of unique packages, + * It must not be smaller than STATUS_HASH_PRIME, + * Currently only packages from status_hashtable are stored in here, but in + * future this may be used to store packages not only from a status file, + * but an available_hashtable, and even multiple packages files. + * Package can be stored more than once if they have different versions. + * e.g. The same package may have different versions in the status file + * and available file */ +#define PACKAGE_HASH_PRIME 10007 +typedef struct edge_s { + unsigned int operator:3; + unsigned int type:4; + unsigned int name:14; + unsigned int version:14; +} edge_t; + +typedef struct common_node_s { + unsigned int name:14; + unsigned int version:14; + unsigned int num_of_edges:14; + edge_t **edge; +} common_node_t; +common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1]; + +/* Currently it doesnt store packages that have state-status of not-installed + * So it only really has to be the size of the maximum number of packages + * likely to be installed at any one time, so there is a bit of leeway here */ +#define STATUS_HASH_PRIME 8191 +typedef struct status_node_s { + unsigned int package:14; /* has to fit PACKAGE_HASH_PRIME */ + unsigned int status:14; /* has to fit STATUS_HASH_PRIME */ +} status_node_t; +status_node_t *status_hashtable[STATUS_HASH_PRIME + 1]; + +/* Even numbers are for 'extras', like ored dependencies or null */ +enum edge_type_e { + EDGE_NULL = 0, + EDGE_PRE_DEPENDS = 1, + EDGE_OR_PRE_DEPENDS = 2, + EDGE_DEPENDS = 3, + EDGE_OR_DEPENDS = 4, + EDGE_REPLACES = 5, + EDGE_PROVIDES = 7, + EDGE_CONFLICTS = 9, + EDGE_SUGGESTS = 11, + EDGE_RECOMMENDS = 13, + EDGE_ENHANCES = 15 +}; +enum operator_e { + VER_NULL = 0, + VER_EQUAL = 1, + VER_LESS = 2, + VER_LESS_EQUAL = 3, + VER_MORE = 4, + VER_MORE_EQUAL = 5, + VER_ANY = 6 +}; + +enum dpkg_opt_e { + dpkg_opt_purge = 1, + dpkg_opt_remove = 2, + dpkg_opt_unpack = 4, + dpkg_opt_configure = 8, + dpkg_opt_install = 16, + dpkg_opt_package_name = 32, + dpkg_opt_filename = 64, + dpkg_opt_list_installed = 128, + dpkg_opt_force_ignore_depends = 256 +}; + +typedef struct deb_file_s { + char *control_file; + char *filename; + unsigned int package:14; +} deb_file_t; + + +void make_hash(const char *key, unsigned int *start, unsigned int *decrement, const int hash_prime) +{ + unsigned long int hash_num = key[0]; + int len = strlen(key); + int i; + + /* Maybe i should have uses a "proper" hashing algorithm here instead + * of making one up myself, seems to be working ok though. */ + for(i = 1; i < len; i++) { + /* shifts the ascii based value and adds it to previous value + * shift amount is mod 24 because long int is 32 bit and data + * to be shifted is 8, don't want to shift data to where it has + * no effect*/ + hash_num += ((key[i] + key[i-1]) << ((key[i] * i) % 24)); + } + *start = (unsigned int) hash_num % hash_prime; + *decrement = (unsigned int) 1 + (hash_num % (hash_prime - 1)); +} + +/* this adds the key to the hash table */ +int search_name_hashtable(const char *key) +{ + unsigned int probe_address = 0; + unsigned int probe_decrement = 0; +// char *temp; + + make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME); + while(name_hashtable[probe_address] != NULL) { + if (strcmp(name_hashtable[probe_address], key) == 0) { + return(probe_address); + } else { + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += NAME_HASH_PRIME; + } + } + } + name_hashtable[probe_address] = bb_xstrdup(key); + return(probe_address); +} + +/* this DOESNT add the key to the hashtable + * TODO make it consistent with search_name_hashtable + */ +unsigned int search_status_hashtable(const char *key) +{ + unsigned int probe_address = 0; + unsigned int probe_decrement = 0; + + make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME); + while(status_hashtable[probe_address] != NULL) { + if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) { + break; + } else { + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += STATUS_HASH_PRIME; + } + } + } + return(probe_address); +} + +/* Need to rethink version comparison, maybe the official dpkg has something i can use ? */ +int version_compare_part(const char *version1, const char *version2) +{ + int upstream_len1 = 0; + int upstream_len2 = 0; + char *name1_char; + char *name2_char; + int len1 = 0; + int len2 = 0; + int tmp_int; + int ver_num1; + int ver_num2; + int ret; + + if (version1 == NULL) { + version1 = bb_xstrdup(""); + } + if (version2 == NULL) { + version2 = bb_xstrdup(""); + } + upstream_len1 = strlen(version1); + upstream_len2 = strlen(version2); + + while ((len1 < upstream_len1) || (len2 < upstream_len2)) { + /* Compare non-digit section */ + tmp_int = strcspn(&version1[len1], "0123456789"); + name1_char = bb_xstrndup(&version1[len1], tmp_int); + len1 += tmp_int; + tmp_int = strcspn(&version2[len2], "0123456789"); + name2_char = bb_xstrndup(&version2[len2], tmp_int); + len2 += tmp_int; + tmp_int = strcmp(name1_char, name2_char); + free(name1_char); + free(name2_char); + if (tmp_int != 0) { + ret = tmp_int; + goto cleanup_version_compare_part; + } + + /* Compare digits */ + tmp_int = strspn(&version1[len1], "0123456789"); + name1_char = bb_xstrndup(&version1[len1], tmp_int); + len1 += tmp_int; + tmp_int = strspn(&version2[len2], "0123456789"); + name2_char = bb_xstrndup(&version2[len2], tmp_int); + len2 += tmp_int; + ver_num1 = atoi(name1_char); + ver_num2 = atoi(name2_char); + free(name1_char); + free(name2_char); + if (ver_num1 < ver_num2) { + ret = -1; + goto cleanup_version_compare_part; + } + else if (ver_num1 > ver_num2) { + ret = 1; + goto cleanup_version_compare_part; + } + } + ret = 0; +cleanup_version_compare_part: + return(ret); +} + +/* if ver1 < ver2 return -1, + * if ver1 = ver2 return 0, + * if ver1 > ver2 return 1, + */ +int version_compare(const unsigned int ver1, const unsigned int ver2) +{ + char *ch_ver1 = name_hashtable[ver1]; + char *ch_ver2 = name_hashtable[ver2]; + + char epoch1, epoch2; + char *deb_ver1, *deb_ver2; + char *ver1_ptr, *ver2_ptr; + char *upstream_ver1; + char *upstream_ver2; + int result; + + /* Compare epoch */ + if (ch_ver1[1] == ':') { + epoch1 = ch_ver1[0]; + ver1_ptr = strchr(ch_ver1, ':') + 1; + } else { + epoch1 = '0'; + ver1_ptr = ch_ver1; + } + if (ch_ver2[1] == ':') { + epoch2 = ch_ver2[0]; + ver2_ptr = strchr(ch_ver2, ':') + 1; + } else { + epoch2 = '0'; + ver2_ptr = ch_ver2; + } + if (epoch1 < epoch2) { + return(-1); + } + else if (epoch1 > epoch2) { + return(1); + } + + /* Compare upstream version */ + upstream_ver1 = bb_xstrdup(ver1_ptr); + upstream_ver2 = bb_xstrdup(ver2_ptr); + + /* Chop off debian version, and store for later use */ + deb_ver1 = strrchr(upstream_ver1, '-'); + deb_ver2 = strrchr(upstream_ver2, '-'); + if (deb_ver1) { + deb_ver1[0] = '\0'; + deb_ver1++; + } + if (deb_ver2) { + deb_ver2[0] = '\0'; + deb_ver2++; + } + result = version_compare_part(upstream_ver1, upstream_ver2); + + free(upstream_ver1); + free(upstream_ver2); + + if (result != 0) { + return(result); + } + + /* Compare debian versions */ + return(version_compare_part(deb_ver1, deb_ver2)); +} + +int test_version(const unsigned int version1, const unsigned int version2, const unsigned int operator) +{ + const int version_result = version_compare(version1, version2); + switch(operator) { + case (VER_ANY): + return(TRUE); + case (VER_EQUAL): + if (version_result == 0) { + return(TRUE); + } + break; + case (VER_LESS): + if (version_result < 0) { + return(TRUE); + } + break; + case (VER_LESS_EQUAL): + if (version_result <= 0) { + return(TRUE); + } + break; + case (VER_MORE): + if (version_result > 0) { + return(TRUE); + } + break; + case (VER_MORE_EQUAL): + if (version_result >= 0) { + return(TRUE); + } + break; + } + return(FALSE); +} + + +int search_package_hashtable(const unsigned int name, const unsigned int version, const unsigned int operator) +{ + unsigned int probe_address = 0; + unsigned int probe_decrement = 0; + + make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME); + while(package_hashtable[probe_address] != NULL) { + if (package_hashtable[probe_address]->name == name) { + if (operator == VER_ANY) { + return(probe_address); + } + if (test_version(package_hashtable[probe_address]->version, version, operator)) { + return(probe_address); + } + } + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += PACKAGE_HASH_PRIME; + } + } + return(probe_address); +} + +/* + * This function searches through the entire package_hashtable looking + * for a package which provides "needle". It returns the index into + * the package_hashtable for the providing package. + * + * needle is the index into name_hashtable of the package we are + * looking for. + * + * start_at is the index in the package_hashtable to start looking + * at. If start_at is -1 then start at the beginning. This is to allow + * for repeated searches since more than one package might provide + * needle. + * + * FIXME: I don't think this is very efficient, but I thought I'd keep + * it simple for now until it proves to be a problem. + */ +int search_for_provides(int needle, int start_at) { + int i, j; + common_node_t *p; + for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) { + p = package_hashtable[i]; + if ( p == NULL ) continue; + for(j = 0; j < p->num_of_edges; j++) + if ( p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle ) + return i; + } + return -1; +} + +/* + * Add an edge to a node + */ +void add_edge_to_node(common_node_t *node, edge_t *edge) +{ + node->num_of_edges++; + node->edge = xrealloc(node->edge, sizeof(edge_t) * (node->num_of_edges + 1)); + node->edge[node->num_of_edges - 1] = edge; +} + +/* + * Create one new node and one new edge for every dependency. + * + * Dependencies which contain multiple alternatives are represented as + * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a + * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of + * the OR edge contains the full dependency string while the version + * field contains the number of EDGE nodes which follow as part of + * this alternative. + */ +void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned int edge_type) +{ + char *line = bb_xstrdup(whole_line); + char *line2; + char *line_ptr1 = NULL; + char *line_ptr2 = NULL; + char *field; + char *field2; + char *version; + edge_t *edge; + edge_t *or_edge; + int offset_ch; + + field = strtok_r(line, ",", &line_ptr1); + do { + /* skip leading spaces */ + field += strspn(field, " "); + line2 = bb_xstrdup(field); + field2 = strtok_r(line2, "|", &line_ptr2); + if ( (edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS) && + (strcmp(field, field2) != 0)) { + or_edge = (edge_t *)xmalloc(sizeof(edge_t)); + or_edge->type = edge_type + 1; + } else { + or_edge = NULL; + } + + if ( or_edge ) { + or_edge->name = search_name_hashtable(field); + or_edge->version = 0; // tracks the number of altenatives + + add_edge_to_node(parent_node, or_edge); + } + + do { + edge = (edge_t *) xmalloc(sizeof(edge_t)); + edge->type = edge_type; + + /* Skip any extra leading spaces */ + field2 += strspn(field2, " "); + + /* Get dependency version info */ + version = strchr(field2, '('); + if (version == NULL) { + edge->operator = VER_ANY; + /* Get the versions hash number, adding it if the number isnt already in there */ + edge->version = search_name_hashtable("ANY"); + } else { + /* Skip leading ' ' or '(' */ + version += strspn(field2, " "); + version += strspn(version, "("); + /* Calculate length of any operator characters */ + offset_ch = strspn(version, "<=>"); + /* Determine operator */ + if (offset_ch > 0) { + if (strncmp(version, "=", offset_ch) == 0) { + edge->operator = VER_EQUAL; + } + else if (strncmp(version, "<<", offset_ch) == 0) { + edge->operator = VER_LESS; + } + else if (strncmp(version, "<=", offset_ch) == 0) { + edge->operator = VER_LESS_EQUAL; + } + else if (strncmp(version, ">>", offset_ch) == 0) { + edge->operator = VER_MORE; + } + else if (strncmp(version, ">=", offset_ch) == 0) { + edge->operator = VER_MORE_EQUAL; + } else { + bb_error_msg_and_die("Illegal operator\n"); + } + } + /* skip to start of version numbers */ + version += offset_ch; + version += strspn(version, " "); + + /* Truncate version at trailing ' ' or ')' */ + version[strcspn(version, " )")] = '\0'; + /* Get the versions hash number, adding it if the number isnt already in there */ + edge->version = search_name_hashtable(version); + } + + /* Get the dependency name */ + field2[strcspn(field2, " (")] = '\0'; + edge->name = search_name_hashtable(field2); + + if ( or_edge ) + or_edge->version++; + + add_edge_to_node(parent_node, edge); + } while ((field2 = strtok_r(NULL, "|", &line_ptr2)) != NULL); + free(line2); + } while ((field = strtok_r(NULL, ",", &line_ptr1)) != NULL); + free(line); + + return; +} + +void free_package(common_node_t *node) +{ + unsigned short i; + if (node) { + for (i = 0; i < node->num_of_edges; i++) { + free(node->edge[i]); + } + if ( node->edge ) + free(node->edge); + free(node); + } +} + +unsigned int fill_package_struct(char *control_buffer) +{ + common_node_t *new_node = (common_node_t *) xcalloc(1, sizeof(common_node_t)); + const char *field_names[] = { "Package", "Version", "Pre-Depends", "Depends", + "Replaces", "Provides", "Conflicts", "Suggests", "Recommends", "Enhances", 0}; + char *field_name; + char *field_value; + int field_start = 0; + int num = -1; + int buffer_length = strlen(control_buffer); + + new_node->version = search_name_hashtable("unknown"); + while (field_start < buffer_length) { + unsigned short field_num; + + field_start += read_package_field(&control_buffer[field_start], + &field_name, &field_value); + + if (field_name == NULL) { + goto fill_package_struct_cleanup; /* Oh no, the dreaded goto statement ! */ + } + + field_num = compare_string_array(field_names, field_name); + switch(field_num) { + case 0: /* Package */ + new_node->name = search_name_hashtable(field_value); + break; + case 1: /* Version */ + new_node->version = search_name_hashtable(field_value); + break; + case 2: /* Pre-Depends */ + add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS); + break; + case 3: /* Depends */ + add_split_dependencies(new_node, field_value, EDGE_DEPENDS); + break; + case 4: /* Replaces */ + add_split_dependencies(new_node, field_value, EDGE_REPLACES); + break; + case 5: /* Provides */ + add_split_dependencies(new_node, field_value, EDGE_PROVIDES); + break; + case 6: /* Conflicts */ + add_split_dependencies(new_node, field_value, EDGE_CONFLICTS); + break; + case 7: /* Suggests */ + add_split_dependencies(new_node, field_value, EDGE_SUGGESTS); + break; + case 8: /* Recommends */ + add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS); + break; + case 9: /* Enhances */ + add_split_dependencies(new_node, field_value, EDGE_ENHANCES); + break; + } +fill_package_struct_cleanup: + free(field_name); + free(field_value); + } + + if (new_node->version == search_name_hashtable("unknown")) { + free_package(new_node); + return(-1); + } + num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL); + if (package_hashtable[num] == NULL) { + package_hashtable[num] = new_node; + } else { + free_package(new_node); + } + return(num); +} + +/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */ +unsigned int get_status(const unsigned int status_node, const int num) +{ + char *status_string = name_hashtable[status_hashtable[status_node]->status]; + char *state_sub_string; + unsigned int state_sub_num; + int len; + int i; + + /* set tmp_string to point to the start of the word number */ + for (i = 1; i < num; i++) { + /* skip past a word */ + status_string += strcspn(status_string, " "); + /* skip past the separating spaces */ + status_string += strspn(status_string, " "); + } + len = strcspn(status_string, " \n\0"); + state_sub_string = bb_xstrndup(status_string, len); + state_sub_num = search_name_hashtable(state_sub_string); + free(state_sub_string); + return(state_sub_num); +} + +void set_status(const unsigned int status_node_num, const char *new_value, const int position) +{ + const unsigned int new_value_len = strlen(new_value); + const unsigned int new_value_num = search_name_hashtable(new_value); + unsigned int want = get_status(status_node_num, 1); + unsigned int flag = get_status(status_node_num, 2); + unsigned int status = get_status(status_node_num, 3); + int want_len = strlen(name_hashtable[want]); + int flag_len = strlen(name_hashtable[flag]); + int status_len = strlen(name_hashtable[status]); + char *new_status; + + switch (position) { + case (1): + want = new_value_num; + want_len = new_value_len; + break; + case (2): + flag = new_value_num; + flag_len = new_value_len; + break; + case (3): + status = new_value_num; + status_len = new_value_len; + break; + default: + bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen"); + } + + new_status = (char *) xmalloc(want_len + flag_len + status_len + 3); + sprintf(new_status, "%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]); + status_hashtable[status_node_num]->status = search_name_hashtable(new_status); + free(new_status); + return; +} + +const char *describe_status(int status_num) { + int status_want, status_state ; + if ( status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0 ) + return "is not installed or flagged to be installed\n"; + + status_want = get_status(status_num, 1); + status_state = get_status(status_num, 3); + + if ( status_state == search_name_hashtable("installed") ) { + if ( status_want == search_name_hashtable("install") ) + return "is installed"; + if ( status_want == search_name_hashtable("deinstall") ) + return "is marked to be removed"; + if ( status_want == search_name_hashtable("purge") ) + return "is marked to be purged"; + } + if ( status_want == search_name_hashtable("unknown") ) + return "is in an indeterminate state"; + if ( status_want == search_name_hashtable("install") ) + return "is marked to be installed"; + + return "is not installed or flagged to be installed"; +} + + +void index_status_file(const char *filename) +{ + FILE *status_file; + char *control_buffer; + char *status_line; + status_node_t *status_node = NULL; + unsigned int status_num; + + status_file = bb_xfopen(filename, "r"); + while ((control_buffer = fgets_str(status_file, "\n\n")) != NULL) { + const unsigned int package_num = fill_package_struct(control_buffer); + if (package_num != -1) { + status_node = xmalloc(sizeof(status_node_t)); + /* fill_package_struct doesnt handle the status field */ + status_line = strstr(control_buffer, "Status:"); + if (status_line != NULL) { + status_line += 7; + status_line += strspn(status_line, " \n\t"); + status_line = bb_xstrndup(status_line, strcspn(status_line, "\n\0")); + status_node->status = search_name_hashtable(status_line); + free(status_line); + } + status_node->package = package_num; + status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]); + status_hashtable[status_num] = status_node; + } + free(control_buffer); + } + fclose(status_file); + return; +} + +#if 0 /* this code is no longer used */ +char *get_depends_field(common_node_t *package, const int depends_type) +{ + char *depends = NULL; + char *old_sep = (char *)xcalloc(1, 3); + char *new_sep = (char *)xcalloc(1, 3); + int line_size = 0; + int depends_size; + + int i; + + for (i = 0; i < package->num_of_edges; i++) { + if ((package->edge[i]->type == EDGE_OR_PRE_DEPENDS) || + (package->edge[i]->type == EDGE_OR_DEPENDS)) { + } + + if ((package->edge[i]->type == depends_type) || + (package->edge[i]->type == depends_type + 1)) { + /* Check if its the first time through */ + + depends_size = 8 + strlen(name_hashtable[package->edge[i]->name]) + + strlen(name_hashtable[package->edge[i]->version]); + line_size += depends_size; + depends = (char *) xrealloc(depends, line_size + 1); + + /* Check to see if this dependency is the type we are looking for + * +1 to check for 'extra' types, e.g. ored dependecies */ + strcpy(old_sep, new_sep); + if (package->edge[i]->type == depends_type) { + strcpy(new_sep, ", "); + } + else if (package->edge[i]->type == depends_type + 1) { + strcpy(new_sep, "| "); + } + + if (depends_size == line_size) { + strcpy(depends, ""); + } else { + if ((strcmp(old_sep, "| ") == 0) && (strcmp(new_sep, "| ") == 0)) { + strcat(depends, " | "); + } else { + strcat(depends, ", "); + } + } + + strcat(depends, name_hashtable[package->edge[i]->name]); + if (strcmp(name_hashtable[package->edge[i]->version], "NULL") != 0) { + if (package->edge[i]->operator == VER_EQUAL) { + strcat(depends, " (= "); + } + else if (package->edge[i]->operator == VER_LESS) { + strcat(depends, " (<< "); + } + else if (package->edge[i]->operator == VER_LESS_EQUAL) { + strcat(depends, " (<= "); + } + else if (package->edge[i]->operator == VER_MORE) { + strcat(depends, " (>> "); + } + else if (package->edge[i]->operator == VER_MORE_EQUAL) { + strcat(depends, " (>= "); + } else { + strcat(depends, " ("); + } + strcat(depends, name_hashtable[package->edge[i]->version]); + strcat(depends, ")"); + } + } + } + return(depends); +} +#endif + +void write_buffer_no_status(FILE *new_status_file, const char *control_buffer) +{ + char *name; + char *value; + int start = 0; + while (1) { + start += read_package_field(&control_buffer[start], &name, &value); + if (name == NULL) { + break; + } + if (strcmp(name, "Status") != 0) { + fprintf(new_status_file, "%s: %s\n", name, value); + } + } + return; +} + +/* This could do with a cleanup */ +void write_status_file(deb_file_t **deb_file) +{ + FILE *old_status_file = bb_xfopen("/var/lib/dpkg/status", "r"); + FILE *new_status_file = bb_xfopen("/var/lib/dpkg/status.udeb", "w"); + char *package_name; + char *status_from_file; + char *control_buffer = NULL; + char *tmp_string; + int status_num; + int field_start = 0; + int write_flag; + int i = 0; + + /* Update previously known packages */ + while ((control_buffer = fgets_str(old_status_file, "\n\n")) != NULL) { + if ((tmp_string = strstr(control_buffer, "Package:")) == NULL) { + continue; + } + + tmp_string += 8; + tmp_string += strspn(tmp_string, " \n\t"); + package_name = bb_xstrndup(tmp_string, strcspn(tmp_string, "\n\0")); + write_flag = FALSE; + tmp_string = strstr(control_buffer, "Status:"); + if (tmp_string != NULL) { + /* Seperate the status value from the control buffer */ + tmp_string += 7; + tmp_string += strspn(tmp_string, " \n\t"); + status_from_file = bb_xstrndup(tmp_string, strcspn(tmp_string, "\n")); + } else { + status_from_file = NULL; + } + + /* Find this package in the status hashtable */ + status_num = search_status_hashtable(package_name); + if (status_hashtable[status_num] != NULL) { + const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status]; + if (strcmp(status_from_file, status_from_hashtable) != 0) { + /* New status isnt exactly the same as old status */ + const int state_status = get_status(status_num, 3); + if ((strcmp("installed", name_hashtable[state_status]) == 0) || + (strcmp("unpacked", name_hashtable[state_status]) == 0)) { + /* We need to add the control file from the package */ + i = 0; + while(deb_file[i] != NULL) { + if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) { + /* Write a status file entry with a modified status */ + /* remove trailing \n's */ + write_buffer_no_status(new_status_file, deb_file[i]->control_file); + set_status(status_num, "ok", 2); + fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]); + write_flag = TRUE; + break; + } + i++; + } + /* This is temperary, debugging only */ + if (deb_file[i] == NULL) { + bb_error_msg_and_die("ALERT: Couldnt find a control file, your status file may be broken, status may be incorrect for %s", package_name); + } + } + else if (strcmp("not-installed", name_hashtable[state_status]) == 0) { + /* Only write the Package, Status, Priority and Section lines */ + fprintf(new_status_file, "Package: %s\n", package_name); + fprintf(new_status_file, "Status: %s\n", status_from_hashtable); + + while (1) { + char *field_name; + char *field_value; + field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); + if (field_name == NULL) { + break; + } + if ((strcmp(field_name, "Priority") == 0) || + (strcmp(field_name, "Section") == 0)) { + fprintf(new_status_file, "%s: %s\n", field_name, field_value); + } + } + write_flag = TRUE; + fputs("\n", new_status_file); + } + else if (strcmp("config-files", name_hashtable[state_status]) == 0) { + /* only change the status line */ + while (1) { + char *field_name; + char *field_value; + field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); + if (field_name == NULL) { + break; + } + /* Setup start point for next field */ + if (strcmp(field_name, "Status") == 0) { + fprintf(new_status_file, "Status: %s\n", status_from_hashtable); + } else { + fprintf(new_status_file, "%s: %s\n", field_name, field_value); + } + } + write_flag = TRUE; + fputs("\n", new_status_file); + } + } + } + /* If the package from the status file wasnt handle above, do it now*/ + if (! write_flag) { + fprintf(new_status_file, "%s\n\n", control_buffer); + } + + free(status_from_file); + free(package_name); + free(control_buffer); + } + + /* Write any new packages */ + for(i = 0; deb_file[i] != NULL; i++) { + status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]); + if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) { + write_buffer_no_status(new_status_file, deb_file[i]->control_file); + set_status(status_num, "ok", 2); + fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]); + } + } + fclose(old_status_file); + fclose(new_status_file); + + + /* Create a separate backfile to dpkg */ + if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) { + struct stat stat_buf; + if (stat("/var/lib/dpkg/status", &stat_buf) == 0) { + bb_error_msg_and_die("Couldnt create backup status file"); + } + /* Its ok if renaming the status file fails because status + * file doesnt exist, maybe we are starting from scratch */ + bb_error_msg("No status file found, creating new one"); + } + + if (rename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status") == -1) { + bb_error_msg_and_die("DANGER: Couldnt create status file, you need to manually repair your status file"); + } +} + +/* This function returns TRUE if the given package can satisfy a + * dependency of type depend_type. + * + * A pre-depends is satisfied only if a package is already installed, + * which a regular depends can be satisfied by a package which we want + * to install. + */ +int package_satisfies_dependency(int package, int depend_type) +{ + int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]); + + /* status could be unknown if package is a pure virtual + * provides which cannot satisfy any dependency by itself. + */ + if ( status_hashtable[status_num] == NULL ) + return 0; + + switch (depend_type) { + case EDGE_PRE_DEPENDS: return get_status(status_num, 3) == search_name_hashtable("installed"); + case EDGE_DEPENDS: return get_status(status_num, 1) == search_name_hashtable("install"); + } + return 0; +} + +int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count) +{ + int *conflicts = NULL; + int conflicts_num = 0; + int i = deb_start; + int j; + + /* Check for conflicts + * TODO: TEST if conflicts with other packages to be installed + * + * Add install packages and the packages they provide + * to the list of files to check conflicts for + */ + + /* Create array of package numbers to check against + * installed package for conflicts*/ + while (deb_file[i] != NULL) { + const unsigned int package_num = deb_file[i]->package; + conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1)); + conflicts[conflicts_num] = package_num; + conflicts_num++; + /* add provides to conflicts list */ + for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) { + if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) { + const int conflicts_package_num = search_package_hashtable( + package_hashtable[package_num]->edge[j]->name, + package_hashtable[package_num]->edge[j]->version, + package_hashtable[package_num]->edge[j]->operator); + if (package_hashtable[conflicts_package_num] == NULL) { + /* create a new package */ + common_node_t *new_node = (common_node_t *) xmalloc(sizeof(common_node_t)); + new_node->name = package_hashtable[package_num]->edge[j]->name; + new_node->version = package_hashtable[package_num]->edge[j]->version; + new_node->num_of_edges = 0; + new_node->edge = NULL; + package_hashtable[conflicts_package_num] = new_node; + } + conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1)); + conflicts[conflicts_num] = conflicts_package_num; + conflicts_num++; + } + } + i++; + } + + /* Check conflicts */ + i = 0; + while (deb_file[i] != NULL) { + const common_node_t *package_node = package_hashtable[deb_file[i]->package]; + int status_num = 0; + status_num = search_status_hashtable(name_hashtable[package_node->name]); + + if (get_status(status_num, 3) == search_name_hashtable("installed")) { + i++; + continue; + } + + for (j = 0; j < package_node->num_of_edges; j++) { + const edge_t *package_edge = package_node->edge[j]; + + if (package_edge->type == EDGE_CONFLICTS) { + const unsigned int package_num = + search_package_hashtable(package_edge->name, + package_edge->version, + package_edge->operator); + int result = 0; + if (package_hashtable[package_num] != NULL) { + status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]); + + if (get_status(status_num, 1) == search_name_hashtable("install")) { + result = test_version(package_hashtable[deb_file[i]->package]->version, + package_edge->version, package_edge->operator); + } + } + + if (result) { + bb_error_msg_and_die("Package %s conflicts with %s", + name_hashtable[package_node->name], + name_hashtable[package_edge->name]); + } + } + } + i++; + } + + + /* Check dependendcies */ + for (i = 0; i < PACKAGE_HASH_PRIME; i++) { + int status_num = 0; + int number_of_alternatives = 0; + const edge_t * root_of_alternatives = NULL; + const common_node_t *package_node = package_hashtable[i]; + + /* If the package node does not exist then this + * package is a virtual one. In which case there are + * no dependencies to check. + */ + if ( package_node == NULL ) continue; + + status_num = search_status_hashtable(name_hashtable[package_node->name]); + + /* If there is no status then this package is a + * virtual one provided by something else. In which + * case there are no dependencies to check. + */ + if ( status_hashtable[status_num] == NULL ) continue; + + /* If we don't want this package installed then we may + * as well ignore it's dependencies. + */ + if (get_status(status_num, 1) != search_name_hashtable("install")) { + continue; + } + +#if 0 + /* This might be needed so we don't complain about + * things which are broken but unrelated to the + * packages that are currently being installed + */ + if (state_status == search_name_hashtable("installed")) + continue; +#endif + + /* This code is tested only for EDGE_DEPENDS, since I + * have no suitable pre-depends available. There is no + * reason that it shouldn't work though :-) + */ + for (j = 0; j < package_node->num_of_edges; j++) { + const edge_t *package_edge = package_node->edge[j]; + unsigned int package_num; + + if ( package_edge->type == EDGE_OR_PRE_DEPENDS || + package_edge->type == EDGE_OR_DEPENDS ) { /* start an EDGE_OR_ list */ + number_of_alternatives = package_edge->version; + root_of_alternatives = package_edge; + continue; + } else if ( number_of_alternatives == 0 ) { /* not in the middle of an EDGE_OR_ list */ + number_of_alternatives = 1; + root_of_alternatives = NULL; + } + + package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator); + + if (package_edge->type == EDGE_PRE_DEPENDS || + package_edge->type == EDGE_DEPENDS ) { + int result=1; + status_num = 0; + + /* If we are inside an alternative then check + * this edge is the right type. + * + * EDGE_DEPENDS == OR_DEPENDS -1 + * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1 + */ + if ( root_of_alternatives && package_edge->type != root_of_alternatives->type - 1) + bb_error_msg_and_die("Fatal error. Package dependencies corrupt: %d != %d - 1 \n", + package_edge->type, root_of_alternatives->type); + + if (package_hashtable[package_num] != NULL) + result = !package_satisfies_dependency(package_num, package_edge->type); + + if (result) { /* check for other package which provide what we are looking for */ + int provider = -1; + + while ( (provider = search_for_provides(package_edge->name, provider) ) > -1 ) { + if ( package_hashtable[provider] == NULL ) { + printf("Have a provider but no package information for it\n"); + continue; + } + result = !package_satisfies_dependency(provider, package_edge->type); + + if ( result == 0 ) + break; + } + } + + /* It must be already installed, or to be installed */ + number_of_alternatives--; + if (result && number_of_alternatives == 0) { + if ( root_of_alternatives ) + bb_error_msg_and_die( + "Package %s %sdepends on %s, " + "which cannot be satisfied", + name_hashtable[package_node->name], + package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "", + name_hashtable[root_of_alternatives->name]); + else + bb_error_msg_and_die( + "Package %s %sdepends on %s, which %s\n", + name_hashtable[package_node->name], + package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "", + name_hashtable[package_edge->name], + describe_status(status_num)); + } else if ( result == 0 && number_of_alternatives ) { + /* we've found a package which + * satisfies the dependency, + * so skip over the rest of + * the alternatives. + */ + j += number_of_alternatives; + number_of_alternatives = 0; + } + } + } + } + free(conflicts); + return(TRUE); +} + +char **create_list(const char *filename) +{ + FILE *list_stream; + char **file_list = NULL; + char *line = NULL; + int count = 0; + + /* don't use [xw]fopen here, handle error ourself */ + list_stream = fopen(filename, "r"); + if (list_stream == NULL) { + return(NULL); + } + + while ((line = bb_get_chomped_line_from_file(list_stream)) != NULL) { + file_list = xrealloc(file_list, sizeof(char *) * (count + 2)); + file_list[count] = line; + count++; + } + fclose(list_stream); + + if (count == 0) { + return(NULL); + } else { + file_list[count] = NULL; + return(file_list); + } +} + +/* maybe i should try and hook this into remove_file.c somehow */ +int remove_file_array(char **remove_names, char **exclude_names) +{ + struct stat path_stat; + int match_flag; + int remove_flag = FALSE; + int i,j; + + if (remove_names == NULL) { + return(FALSE); + } + for (i = 0; remove_names[i] != NULL; i++) { + match_flag = FALSE; + if (exclude_names != NULL) { + for (j = 0; exclude_names[j] != 0; j++) { + if (strcmp(remove_names[i], exclude_names[j]) == 0) { + match_flag = TRUE; + break; + } + } + } + if (!match_flag) { + if (lstat(remove_names[i], &path_stat) < 0) { + continue; + } + if (S_ISDIR(path_stat.st_mode)) { + if (rmdir(remove_names[i]) != -1) { + remove_flag = TRUE; + } + } else { + if (unlink(remove_names[i]) != -1) { + remove_flag = TRUE; + } + } + } + } + return(remove_flag); +} + +int run_package_script(const char *package_name, const char *script_type) +{ + struct stat path_stat; + char *script_path; + int result; + + script_path = xmalloc(strlen(package_name) + strlen(script_type) + 21); + sprintf(script_path, "/var/lib/dpkg/info/%s.%s", package_name, script_type); + + /* If the file doesnt exist is isnt a fatal */ + if (lstat(script_path, &path_stat) < 0) { + result = EXIT_SUCCESS; + } else { + result = system(script_path); + } + free(script_path); + return(result); +} + +const char *all_control_files[] = {"preinst", "postinst", "prerm", "postrm", + "list", "md5sums", "shlibs", "conffiles", "config", "templates", NULL }; + +char **all_control_list(const char *package_name) +{ + unsigned short i = 0; + char **remove_files; + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = malloc(sizeof(all_control_files)); + while (all_control_files[i]) { + remove_files[i] = xmalloc(strlen(package_name) + strlen(all_control_files[i]) + 21); + sprintf(remove_files[i], "/var/lib/dpkg/info/%s.%s", package_name, all_control_files[i]); + i++; + } + remove_files[sizeof(all_control_files)/sizeof(char*) - 1] = NULL; + + return(remove_files); +} + +void free_array(char **array) +{ + + if (array) { + unsigned short i = 0; + while (array[i]) { + free(array[i]); + i++; + } + free(array); + } +} + +/* This function lists information on the installed packages. It loops through + * the status_hashtable to retrieve the info. This results in smaller code than + * scanning the status file. The resulting list, however, is unsorted. + */ +void list_packages(void) +{ + int i; + + printf(" Name Version\n"); + printf("+++-==============-==============\n"); + + /* go through status hash, dereference package hash and finally strings */ + for (i=0; istatus]; + name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name]; + vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version]; + + /* get abbreviation for status field 1 */ + s1 = stat_str[0] == 'i' ? 'i' : 'r'; + + /* get abbreviation for status field 2 */ + for (j=0, spccnt=0; stat_str[j] && spccnt<2; j++) { + if (stat_str[j] == ' ') spccnt++; + } + s2 = stat_str[j]; + + /* print out the line formatted like Debian dpkg */ + printf("%c%c %-14s %s\n", s1, s2, name_str, vers_str); + } + } +} + +void remove_package(const unsigned int package_num, int noisy) +{ + const char *package_name = name_hashtable[package_hashtable[package_num]->name]; + const char *package_version = name_hashtable[package_hashtable[package_num]->version]; + const unsigned int status_num = search_status_hashtable(package_name); + const int package_name_length = strlen(package_name); + char **remove_files; + char **exclude_files; + char list_name[package_name_length + 25]; + char conffile_name[package_name_length + 30]; + int return_value; + + if ( noisy ) + printf("Removing %s (%s) ...\n", package_name, package_version); + + /* run prerm script */ + return_value = run_package_script(package_name, "prerm"); + if (return_value == -1) { + bb_error_msg_and_die("script failed, prerm failure"); + } + + /* Create a list of files to remove, and a separate list of those to keep */ + sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name); + remove_files = create_list(list_name); + + sprintf(conffile_name, "/var/lib/dpkg/info/%s.conffiles", package_name); + exclude_files = create_list(conffile_name); + + /* Some directories cant be removed straight away, so do multiple passes */ + while (remove_file_array(remove_files, exclude_files)); + free_array(exclude_files); + free_array(remove_files); + + /* Create a list of files in /var/lib/dpkg/info/.* to keep */ + exclude_files = xmalloc(sizeof(char*) * 3); + exclude_files[0] = bb_xstrdup(conffile_name); + exclude_files[1] = xmalloc(package_name_length + 27); + sprintf(exclude_files[1], "/var/lib/dpkg/info/%s.postrm", package_name); + exclude_files[2] = NULL; + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = all_control_list(package_name); + + remove_file_array(remove_files, exclude_files); + free_array(remove_files); + free_array(exclude_files); + + /* rename .conffile to .list */ + rename(conffile_name, list_name); + + /* Change package status */ + set_status(status_num, "config-files", 3); +} + +void purge_package(const unsigned int package_num) +{ + const char *package_name = name_hashtable[package_hashtable[package_num]->name]; + const char *package_version = name_hashtable[package_hashtable[package_num]->version]; + const unsigned int status_num = search_status_hashtable(package_name); + char **remove_files; + char **exclude_files; + char list_name[strlen(package_name) + 25]; + + printf("Purging %s (%s) ...\n", package_name, package_version); + + /* run prerm script */ + if (run_package_script(package_name, "prerm") != 0) { + bb_error_msg_and_die("script failed, prerm failure"); + } + + /* Create a list of files to remove */ + sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name); + remove_files = create_list(list_name); + + exclude_files = xmalloc(sizeof(char*)); + exclude_files[0] = NULL; + + /* Some directories cant be removed straight away, so do multiple passes */ + while (remove_file_array(remove_files, exclude_files)); + free_array(remove_files); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = all_control_list(package_name); + remove_file_array(remove_files, exclude_files); + free_array(remove_files); + free(exclude_files); + + /* run postrm script */ + if (run_package_script(package_name, "postrm") == -1) { + bb_error_msg_and_die("postrm fialure.. set status to what?"); + } + + /* Change package status */ + set_status(status_num, "not-installed", 3); +} + +static archive_handle_t *init_archive_deb_ar(const char *filename) +{ + archive_handle_t *ar_handle; + + /* Setup an ar archive handle that refers to the gzip sub archive */ + ar_handle = init_handle(); + ar_handle->filter = filter_accept_list_reassign; + ar_handle->src_fd = bb_xopen(filename, O_RDONLY); + + return(ar_handle); +} + +static void init_archive_deb_control(archive_handle_t *ar_handle) +{ + archive_handle_t *tar_handle; + + /* Setup the tar archive handle */ + tar_handle = init_handle(); + tar_handle->src_fd = ar_handle->src_fd; + + /* We don't care about data.tar.* or debian-binary, just control.tar.* */ +#ifdef CONFIG_FEATURE_DEB_TAR_GZ + ar_handle->accept = llist_add_to(NULL, "control.tar.gz"); +#endif +#ifdef CONFIG_FEATURE_DEB_TAR_BZ2 + ar_handle->accept = llist_add_to(ar_handle->accept, "control.tar.bz2"); +#endif + + /* Assign the tar handle as a subarchive of the ar handle */ + ar_handle->sub_archive = tar_handle; + + return; +} + +static void init_archive_deb_data(archive_handle_t *ar_handle) +{ + archive_handle_t *tar_handle; + + /* Setup the tar archive handle */ + tar_handle = init_handle(); + tar_handle->src_fd = ar_handle->src_fd; + + /* We don't care about control.tar.* or debian-binary, just data.tar.* */ +#ifdef CONFIG_FEATURE_DEB_TAR_GZ + ar_handle->accept = llist_add_to(NULL, "data.tar.gz"); +#endif +#ifdef CONFIG_FEATURE_DEB_TAR_BZ2 + ar_handle->accept = llist_add_to(ar_handle->accept, "data.tar.bz2"); +#endif + + /* Assign the tar handle as a subarchive of the ar handle */ + ar_handle->sub_archive = tar_handle; + + return; +} + +static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept) +{ + ar_handle->sub_archive->action_data = data_extract_to_buffer; + ar_handle->sub_archive->accept = myaccept; + + unpack_ar_archive(ar_handle); + close(ar_handle->src_fd); + + return(ar_handle->sub_archive->buffer); +} + +static void data_extract_all_prefix(archive_handle_t *archive_handle) +{ + char *name_ptr = archive_handle->file_header->name; + + name_ptr += strspn(name_ptr, "./"); + if (name_ptr[0] != '\0') { + archive_handle->file_header->name = xmalloc(strlen(archive_handle->buffer) + 2 + strlen(name_ptr)); + strcpy(archive_handle->file_header->name, archive_handle->buffer); + strcat(archive_handle->file_header->name, name_ptr); + data_extract_all(archive_handle); + } + return; +} + +static void unpack_package(deb_file_t *deb_file) +{ + const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; + const unsigned int status_num = search_status_hashtable(package_name); + const unsigned int status_package_num = status_hashtable[status_num]->package; + char *info_prefix; + archive_handle_t *archive_handle; + FILE *out_stream; + llist_t *accept_list = NULL; + int i = 0; + + /* If existing version, remove it first */ + if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) { + /* Package is already installed, remove old version first */ + printf("Preparing to replace %s %s (using %s) ...\n", package_name, + name_hashtable[package_hashtable[status_package_num]->version], + deb_file->filename); + remove_package(status_package_num, 0); + } else { + printf("Unpacking %s (from %s) ...\n", package_name, deb_file->filename); + } + + /* Extract control.tar.gz to /var/lib/dpkg/info/.filename */ + info_prefix = (char *) xmalloc(strlen(package_name) + 20 + 4 + 2); + sprintf(info_prefix, "/var/lib/dpkg/info/%s.", package_name); + archive_handle = init_archive_deb_ar(deb_file->filename); + init_archive_deb_control(archive_handle); + + while(all_control_files[i]) { + char *c = (char *) xmalloc(3 + bb_strlen(all_control_files[i])); + sprintf(c, "./%s", all_control_files[i]); + accept_list= llist_add_to(accept_list, c); + i++; + } + archive_handle->sub_archive->accept = accept_list; + archive_handle->sub_archive->filter = filter_accept_list; + archive_handle->sub_archive->action_data = data_extract_all_prefix; + archive_handle->sub_archive->buffer = info_prefix; + archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL; + unpack_ar_archive(archive_handle); + + /* Run the preinst prior to extracting */ + if (run_package_script(package_name, "preinst") != 0) { + /* when preinst returns exit code != 0 then quit installation process */ + bb_error_msg_and_die("subprocess pre-installation script returned error."); + } + + /* Extract data.tar.gz to the root directory */ + archive_handle = init_archive_deb_ar(deb_file->filename); + init_archive_deb_data(archive_handle); + archive_handle->sub_archive->action_data = data_extract_all_prefix; + archive_handle->sub_archive->buffer = "/"; + archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL; + unpack_ar_archive(archive_handle); + + /* Create the list file */ + strcat(info_prefix, "list"); + out_stream = bb_xfopen(info_prefix, "w"); + while (archive_handle->sub_archive->passed) { + /* the leading . has been stripped by data_extract_all_prefix already */ + fputs(archive_handle->sub_archive->passed->data, out_stream); + fputc('\n', out_stream); + archive_handle->sub_archive->passed = archive_handle->sub_archive->passed->link; + } + fclose(out_stream); + + /* change status */ + set_status(status_num, "install", 1); + set_status(status_num, "unpacked", 3); + + free(info_prefix); +} + +void configure_package(deb_file_t *deb_file) +{ + const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; + const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version]; + const int status_num = search_status_hashtable(package_name); + + printf("Setting up %s (%s) ...\n", package_name, package_version); + + /* Run the postinst script */ + if (run_package_script(package_name, "postinst") != 0) { + /* TODO: handle failure gracefully */ + bb_error_msg_and_die("postrm failure.. set status to what?"); + } + /* Change status to reflect success */ + set_status(status_num, "install", 1); + set_status(status_num, "installed", 3); +} + +int dpkg_main(int argc, char **argv) +{ + deb_file_t **deb_file = NULL; + status_node_t *status_node; + int opt; + int package_num; + int dpkg_opt = 0; + int deb_count = 0; + int state_status; + int status_num; + int i; + + while ((opt = getopt(argc, argv, "CF:ilPru")) != -1) { + switch (opt) { + case 'C': // equivalent to --configure in official dpkg + dpkg_opt |= dpkg_opt_configure; + dpkg_opt |= dpkg_opt_package_name; + break; + case 'F': // equivalent to --force in official dpkg + if (strcmp(optarg, "depends") == 0) { + dpkg_opt |= dpkg_opt_force_ignore_depends; + } + break; + case 'i': + dpkg_opt |= dpkg_opt_install; + dpkg_opt |= dpkg_opt_filename; + break; + case 'l': + dpkg_opt |= dpkg_opt_list_installed; + break; + case 'P': + dpkg_opt |= dpkg_opt_purge; + dpkg_opt |= dpkg_opt_package_name; + break; + case 'r': + dpkg_opt |= dpkg_opt_remove; + dpkg_opt |= dpkg_opt_package_name; + break; + case 'u': /* Equivalent to --unpack in official dpkg */ + dpkg_opt |= dpkg_opt_unpack; + dpkg_opt |= dpkg_opt_filename; + break; + default: + bb_show_usage(); + } + } + /* check for non-otion argument if expected */ + if ((dpkg_opt == 0) || ((argc == optind) && !(dpkg_opt && dpkg_opt_list_installed))) { + bb_show_usage(); + } + +/* puts("(Reading database ... xxxxx files and directories installed.)"); */ + index_status_file("/var/lib/dpkg/status"); + + /* if the list action was given print the installed packages and exit */ + if (dpkg_opt & dpkg_opt_list_installed) { + list_packages(); + return(EXIT_SUCCESS); + } + + /* Read arguments and store relevant info in structs */ + while (optind < argc) { + /* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */ + deb_file = xrealloc(deb_file, sizeof(deb_file_t *) * (deb_count + 2)); + deb_file[deb_count] = (deb_file_t *) xmalloc(sizeof(deb_file_t)); + if (dpkg_opt & dpkg_opt_filename) { + archive_handle_t *archive_handle; + llist_t *control_list = NULL; + + /* Extract the control file */ + control_list = llist_add_to(NULL, "./control"); + archive_handle = init_archive_deb_ar(argv[optind]); + init_archive_deb_control(archive_handle); + deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list); + if (deb_file[deb_count]->control_file == NULL) { + bb_error_msg_and_die("Couldnt extract control file"); + } + deb_file[deb_count]->filename = bb_xstrdup(argv[optind]); + package_num = fill_package_struct(deb_file[deb_count]->control_file); + + if (package_num == -1) { + bb_error_msg("Invalid control file in %s", argv[optind]); + continue; + } + deb_file[deb_count]->package = (unsigned int) package_num; + + /* Add the package to the status hashtable */ + if ((dpkg_opt & dpkg_opt_unpack) || (dpkg_opt & dpkg_opt_install)) { + /* Try and find a currently installed version of this package */ + status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + /* If no previous entry was found initialise a new entry */ + if ((status_hashtable[status_num] == NULL) || + (status_hashtable[status_num]->status == 0)) { + status_node = (status_node_t *) xmalloc(sizeof(status_node_t)); + status_node->package = deb_file[deb_count]->package; + /* reinstreq isnt changed to "ok" until the package control info + * is written to the status file*/ + status_node->status = search_name_hashtable("install reinstreq not-installed"); + status_hashtable[status_num] = status_node; + } else { + set_status(status_num, "install", 1); + set_status(status_num, "reinstreq", 2); + } + } + } + else if (dpkg_opt & dpkg_opt_package_name) { + deb_file[deb_count]->filename = NULL; + deb_file[deb_count]->control_file = NULL; + deb_file[deb_count]->package = search_package_hashtable( + search_name_hashtable(argv[optind]), + search_name_hashtable("ANY"), VER_ANY); + if (package_hashtable[deb_file[deb_count]->package] == NULL) { + bb_error_msg_and_die("Package %s is uninstalled or unknown\n", argv[optind]); + } + package_num = deb_file[deb_count]->package; + status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]); + state_status = get_status(status_num, 3); + + /* check package status is "installed" */ + if (dpkg_opt & dpkg_opt_remove) { + if ((strcmp(name_hashtable[state_status], "not-installed") == 0) || + (strcmp(name_hashtable[state_status], "config-files") == 0)) { + bb_error_msg_and_die("%s is already removed.", name_hashtable[package_hashtable[package_num]->name]); + } + set_status(status_num, "deinstall", 1); + } + else if (dpkg_opt & dpkg_opt_purge) { + /* if package status is "conf-files" then its ok */ + if (strcmp(name_hashtable[state_status], "not-installed") == 0) { + bb_error_msg_and_die("%s is already purged.", name_hashtable[package_hashtable[package_num]->name]); + } + set_status(status_num, "purge", 1); + } + } + deb_count++; + optind++; + } + deb_file[deb_count] = NULL; + + /* Check that the deb file arguments are installable */ + if ((dpkg_opt & dpkg_opt_force_ignore_depends) != dpkg_opt_force_ignore_depends) { + if (!check_deps(deb_file, 0, deb_count)) { + bb_error_msg_and_die("Dependency check failed"); + } + } + + /* TODO: install or remove packages in the correct dependency order */ + for (i = 0; i < deb_count; i++) { + /* Remove or purge packages */ + if (dpkg_opt & dpkg_opt_remove) { + remove_package(deb_file[i]->package, 1); + } + else if (dpkg_opt & dpkg_opt_purge) { + purge_package(deb_file[i]->package); + } + else if (dpkg_opt & dpkg_opt_unpack) { + unpack_package(deb_file[i]); + } + else if (dpkg_opt & dpkg_opt_install) { + unpack_package(deb_file[i]); + /* package is configured in second pass below */ + } + else if (dpkg_opt & dpkg_opt_configure) { + configure_package(deb_file[i]); + } + } + /* configure installed packages */ + if (dpkg_opt & dpkg_opt_install) { + for (i = 0; i < deb_count; i++) + configure_package(deb_file[i]); + } + + write_status_file(deb_file); + + for (i = 0; i < deb_count; i++) { + free(deb_file[i]->control_file); + free(deb_file[i]->filename); + free(deb_file[i]); + } + + free(deb_file); + + for (i = 0; i < NAME_HASH_PRIME; i++) { + free(name_hashtable[i]); + } + + for (i = 0; i < PACKAGE_HASH_PRIME; i++) { + if (package_hashtable[i] != NULL) { + free_package(package_hashtable[i]); + } + } + + for (i = 0; i < STATUS_HASH_PRIME; i++) { + free(status_hashtable[i]); + } + + return(EXIT_SUCCESS); +} + diff --git a/busybox/archival/dpkg_deb.c b/busybox/archival/dpkg_deb.c new file mode 100644 index 000000000..5aa9881d5 --- /dev/null +++ b/busybox/archival/dpkg_deb.c @@ -0,0 +1,112 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#include +#include +#include +#include +#include + +#include "unarchive.h" +#include "busybox.h" + +#define DPKG_DEB_OPT_CONTENTS 1 +#define DPKG_DEB_OPT_CONTROL 2 +#define DPKG_DEB_OPT_FIELD 4 +#define DPKG_DEB_OPT_EXTRACT 8 +#define DPKG_DEB_OPT_EXTRACT_VERBOSE 16 + +extern int dpkg_deb_main(int argc, char **argv) +{ + archive_handle_t *ar_archive; + archive_handle_t *tar_archive; + llist_t *control_tar_llist = NULL; + unsigned long opt; + char *extract_dir = NULL; + short argcount = 1; + + /* Setup the tar archive handle */ + tar_archive = init_handle(); + + /* Setup an ar archive handle that refers to the gzip sub archive */ + ar_archive = init_handle(); + ar_archive->sub_archive = tar_archive; + ar_archive->filter = filter_accept_list_reassign; + +#ifdef CONFIG_FEATURE_DEB_TAR_GZ + ar_archive->accept = llist_add_to(NULL, "data.tar.gz"); + control_tar_llist = llist_add_to(NULL, "control.tar.gz"); +#endif + +#ifdef CONFIG_FEATURE_DEB_TAR_BZ2 + ar_archive->accept = llist_add_to(ar_archive->accept, "data.tar.bz2"); + control_tar_llist = llist_add_to(control_tar_llist, "control.tar.bz2"); +#endif + + bb_opt_complementaly = "c~efXx:e~cfXx:f~ceXx:X~cefx:x~cefX"; + opt = bb_getopt_ulflags(argc, argv, "cefXx"); + + if (opt & DPKG_DEB_OPT_CONTENTS) { + tar_archive->action_header = header_verbose_list; + } + if (opt & DPKG_DEB_OPT_CONTROL) { + ar_archive->accept = control_tar_llist; + tar_archive->action_data = data_extract_all; + if (optind + 1 == argc) { + extract_dir = "./DEBIAN"; + } else { + argcount++; + } + } + if (opt & DPKG_DEB_OPT_FIELD) { + /* Print the entire control file + * it should accept a second argument which specifies a + * specific field to print */ + ar_archive->accept = control_tar_llist; + tar_archive->accept = llist_add_to(NULL, "./control");; + tar_archive->filter = filter_accept_list; + tar_archive->action_data = data_extract_to_stdout; + } + if (opt & DPKG_DEB_OPT_EXTRACT) { + tar_archive->action_header = header_list; + } + if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) { + tar_archive->action_data = data_extract_all; + argcount = 2; + } + + if ((optind + argcount != argc) || (opt & 0x80000000UL)) { + bb_show_usage(); + } + + tar_archive->src_fd = ar_archive->src_fd = bb_xopen(argv[optind++], O_RDONLY); + + /* Workout where to extract the files */ + /* 2nd argument is a dir name */ + if (argv[optind]) { + extract_dir = argv[optind]; + } + if (extract_dir) { + mkdir(extract_dir, 0777); + chdir(extract_dir); + } + unpack_ar_archive(ar_archive); + + /* Cleanup */ + close (ar_archive->src_fd); + + return(EXIT_SUCCESS); +} diff --git a/busybox/archival/gunzip.c b/busybox/archival/gunzip.c new file mode 100644 index 000000000..beb7bd12e --- /dev/null +++ b/busybox/archival/gunzip.c @@ -0,0 +1,198 @@ +/* vi: set sw=4 ts=4: */ +/* + * Gzip implementation for busybox + * + * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly. + * + * Originally adjusted for busybox by Sven Rudolph + * 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 +#include + +#include "busybox.h" +#include "unarchive.h" + +#define GUNZIP_OPT_STDOUT 1 +#define GUNZIP_OPT_FORCE 2 +#define GUNZIP_OPT_TEST 4 +#define GUNZIP_OPT_DECOMPRESS 8 + +extern int gunzip_main(int argc, char **argv) +{ + char status = EXIT_SUCCESS; + unsigned long opt; + + opt = bb_getopt_ulflags(argc, argv, "cftd"); + /* if called as zcat */ + if (strcmp(bb_applet_name, "zcat") == 0) { + opt |= GUNZIP_OPT_STDOUT; + } + + do { + struct stat stat_buf; + const char *old_path = argv[optind]; + const char *delete_path = NULL; + char *new_path = NULL; + int src_fd; + int dst_fd; + + optind++; + + if (old_path == NULL || strcmp(old_path, "-") == 0) { + src_fd = STDIN_FILENO; + opt |= GUNZIP_OPT_STDOUT; + } else { + src_fd = bb_xopen(old_path, O_RDONLY); + + /* Get the time stamp on the input file. */ + if (stat(old_path, &stat_buf) < 0) { + bb_error_msg_and_die("Couldn't stat file %s", old_path); + } + } + + /* Check that the input is sane. */ + if (isatty(src_fd) && ((opt & GUNZIP_OPT_FORCE) == 0)) { + bb_error_msg_and_die + ("compressed data not read from terminal. Use -f to force it."); + } + + /* Set output filename and number */ + if (opt & GUNZIP_OPT_TEST) { + dst_fd = bb_xopen("/dev/null", O_WRONLY); /* why does test use filenum 2 ? */ + } else if (opt & GUNZIP_OPT_STDOUT) { + dst_fd = STDOUT_FILENO; + } else { + char *extension; + + new_path = bb_xstrdup(old_path); + + extension = strrchr(new_path, '.'); +#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS + if (extension && (strcmp(extension, ".Z") == 0)) { + *extension = '\0'; + } else +#endif + if (extension && (strcmp(extension, ".gz") == 0)) { + *extension = '\0'; + } else if (extension && (strcmp(extension, ".tgz") == 0)) { + extension[2] = 'a'; + extension[3] = 'r'; + } else { + bb_error_msg_and_die("Invalid extension"); + } + + /* Open output file */ + dst_fd = bb_xopen(new_path, O_WRONLY | O_CREAT); + + /* Set permissions on the file */ + chmod(new_path, stat_buf.st_mode); + + /* If unzip succeeds remove the old file */ + delete_path = old_path; + } + + /* do the decompression, and cleanup */ + if (bb_xread_char(src_fd) == 0x1f) { + unsigned char magic2; + + magic2 = bb_xread_char(src_fd); +#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS + if (magic2 == 0x9d) { + status = uncompress(src_fd, dst_fd); + } else +#endif + if (magic2 == 0x8b) { + check_header_gzip(src_fd); + status = inflate_gunzip(src_fd, dst_fd); + if (status != 0) { + bb_error_msg_and_die("Error inflating"); + } + } else { + bb_error_msg_and_die("Invalid magic"); + } + } else { + bb_error_msg_and_die("Invalid magic"); + } + + if ((status != EXIT_SUCCESS) && (new_path)) { + /* Unzip failed, remove new path instead of old path */ + delete_path = new_path; + } + + if (dst_fd != STDOUT_FILENO) { + close(dst_fd); + } + if (src_fd != STDIN_FILENO) { + close(src_fd); + } + + /* delete_path will be NULL if in test mode or from stdin */ + if (delete_path && (unlink(delete_path) == -1)) { + bb_error_msg_and_die("Couldn't remove %s", delete_path); + } + + free(new_path); + + } while (optind < argc); + + return status; +} diff --git a/busybox/archival/gzip.c b/busybox/archival/gzip.c new file mode 100644 index 000000000..d494aa30e --- /dev/null +++ b/busybox/archival/gzip.c @@ -0,0 +1,2548 @@ +/* vi: set sw=4 ts=4: */ +/* + * Gzip implementation for busybox + * + * Based on GNU gzip Copyright (C) 1992-1993 Jean-loup Gailly. + * + * Originally adjusted for busybox by Charles P. Wright + * "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 + +/* To save memory for 16 bit systems, some arrays are overlaid between + * the various modules: + * deflate: prev+head window d_buf l_buf outbuf + * unlzw: tab_prefix tab_suffix stack inbuf outbuf + * For compression, input is done in window[]. For decompression, output + * is done in window except for unlzw. + */ + +#ifndef INBUFSIZ +# ifdef SMALL_MEM +# define INBUFSIZ 0x2000 /* input buffer size */ +# else +# define INBUFSIZ 0x8000 /* input buffer size */ +# endif +#endif +#define INBUF_EXTRA 64 /* required by unlzw() */ + +#ifndef OUTBUFSIZ +# ifdef SMALL_MEM +# define OUTBUFSIZ 8192 /* output buffer size */ +# else +# define OUTBUFSIZ 16384 /* output buffer size */ +# endif +#endif +#define OUTBUF_EXTRA 2048 /* required by unlzw() */ + +#ifndef DIST_BUFSIZE +# ifdef SMALL_MEM +# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */ +# else +# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ +# endif +#endif + +#ifdef DYN_ALLOC +# define DECLARE(type, array, size) static type * array +# define ALLOC(type, array, size) { \ + array = (type*)xcalloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \ + } +# define FREE(array) {free(array), array=NULL;} +#else +# define DECLARE(type, array, size) static type array[size] +# define ALLOC(type, array, size) +# define FREE(array) +#endif + +#define tab_suffix window +#define tab_prefix prev /* hash link (see deflate.c) */ +#define head (prev+WSIZE) /* hash head (see deflate.c) */ + +static long bytes_in; /* number of input bytes */ + +#define isize bytes_in +/* for compatibility with old zip sources (to be cleaned) */ + +typedef int file_t; /* Do not use stdio */ + +#define NO_FILE (-1) /* in memory compression */ + + +#define PACK_MAGIC "\037\036" /* Magic header for packed files */ +#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ +#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ +#define LZH_MAGIC "\037\240" /* Magic header for SCO LZH Compress files */ +#define PKZIP_MAGIC "\120\113\003\004" /* Magic header for pkzip files */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* internal file attribute */ +#define UNKNOWN 0xffff +#define BINARY 0 +#define ASCII 1 + +#ifndef WSIZE +# define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +/* put_byte is used for the compressed output */ +#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ + flush_outbuf();} + + +/* Output a 32 bit value to the bit stream, lsb first */ +#if 0 +#define put_long(n) { \ + put_short((n) & 0xffff); \ + put_short(((ulg)(n)) >> 16); \ +} +#endif + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) bb_error_msg(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#define WARN(msg) {if (!quiet) fprintf msg ; \ + if (exit_code == OK) exit_code = WARNING;} + +#ifndef MAX_PATH_LEN +# define MAX_PATH_LEN 1024 /* max pathname length */ +#endif + + + /* from zip.c: */ +static int zip(int in, int out); +static int file_read(char *buf, unsigned size); + + /* from gzip.c */ +static RETSIGTYPE abort_gzip(void); + + /* from deflate.c */ +static void lm_init(ush * flags); +static ulg deflate(void); + + /* from trees.c */ +static void ct_init(ush * attr, int *methodp); +static int ct_tally(int dist, int lc); +static ulg flush_block(char *buf, ulg stored_len, int eof); + + /* from bits.c */ +static void bi_init(file_t zipfile); +static void send_bits(int value, int length); +static unsigned bi_reverse(unsigned value, int length); +static void bi_windup(void); +static void copy_block(char *buf, unsigned len, int header); +static int (*read_buf) (char *buf, unsigned size); + + /* from util.c: */ +static void flush_outbuf(void); + +/* lzw.h -- define the lzw functions. + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if !defined(OF) && defined(lint) +# include "gzip.h" +#endif + +#ifndef BITS +# define BITS 16 +#endif +#define INIT_BITS 9 /* Initial number of bits per code */ + +#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ +/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. + * It's a pity that old uncompress does not check bit 0x20. That makes + * extension of the format actually undesirable because old compress + * would just crash on the new format instead of giving a meaningful + * error message. It does check the number of bits, but it's more + * helpful to say "unsupported format, get a new version" than + * "can only handle 16 bits". + */ + +/* tailor.h -- target dependent definitions + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* The target dependent definitions should be defined here only. + * The target dependent functions should be defined in tailor.c. + */ + + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef PATH_SEP +# define PATH_SEP '/' +#endif + +#ifndef OPTIONS_VAR +# define OPTIONS_VAR "GZIP" +#endif + +#ifndef Z_SUFFIX +# define Z_SUFFIX ".gz" +#endif + +#ifdef MAX_EXT_CHARS +# define MAX_SUFFIX MAX_EXT_CHARS +#else +# define MAX_SUFFIX 30 +#endif + + /* global buffers */ + +DECLARE(uch, inbuf, INBUFSIZ + INBUF_EXTRA); +DECLARE(uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA); +DECLARE(ush, d_buf, DIST_BUFSIZE); +DECLARE(uch, window, 2L * WSIZE); +DECLARE(ush, tab_prefix, 1L << BITS); + +static int foreground; /* set if program run in foreground */ +static int method = DEFLATED; /* compression method */ +static int exit_code = OK; /* program exit code */ +static int part_nb; /* number of parts in .gz file */ +static long time_stamp; /* original time stamp (modification time) */ +static long ifile_size; /* input file size, -1 for devices (debug only) */ +static char z_suffix[MAX_SUFFIX + 1]; /* default suffix (can be set with --suffix) */ +static int z_len; /* strlen(z_suffix) */ + +static int ifd; /* input file descriptor */ +static int ofd; /* output file descriptor */ +static unsigned insize; /* valid bytes in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + + +/* Output a 16 bit value, lsb first */ +static void put_short(ush w) +{ + if (outcnt < OUTBUFSIZ - 2) { + outbuf[outcnt++] = (uch) ((w) & 0xff); + outbuf[outcnt++] = (uch) ((ush) (w) >> 8); + } else { + put_byte((uch) ((w) & 0xff)); + put_byte((uch) ((ush) (w) >> 8)); + } +} + +/* ======================================================================== + * Signal and error handler. + */ +static void abort_gzip() +{ + exit(ERROR); +} + +/* =========================================================================== + * Clear input and output buffers + */ +static void clear_bufs(void) +{ + outcnt = 0; + insize = 0; + bytes_in = 0L; +} + +static void write_bb_error_msg(void) +{ + fputc('\n', stderr); + bb_perror_nomsg(); + abort_gzip(); +} + +/* =========================================================================== + * Does the same as write(), but also handles partial pipe writes and checks + * for error return. + */ +static void write_buf(int fd, void *buf, unsigned cnt) +{ + unsigned n; + + while ((n = write(fd, buf, cnt)) != cnt) { + if (n == (unsigned) (-1)) { + write_bb_error_msg(); + } + cnt -= n; + buf = (void *) ((char *) buf + n); + } +} + +/* =========================================================================== + * Run a set of bytes through the crc shift register. If s is a NULL + * pointer, then initialize the crc shift register contents instead. + * Return the current crc in either case. + */ +static ulg updcrc(uch * s, unsigned n) +{ + static ulg crc = (ulg) 0xffffffffL; /* shift register contents */ + register ulg c; /* temporary variable */ + static unsigned long crc_32_tab[256]; + + if (crc_32_tab[1] == 0x00000000L) { + unsigned long csr; /* crc shift register */ + const unsigned long e = 0xedb88320L; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* Compute table of CRC's. */ + for (i = 1; i < 256; i++) { + csr = i; + /* The idea to initialize the register with the byte instead of + * zero was stolen from Haruhiko Okumura's ar002 + */ + for (k = 8; k; k--) + csr = csr & 1 ? (csr >> 1) ^ e : csr >> 1; + crc_32_tab[i] = csr; + } + } + + if (s == NULL) { + c = 0xffffffffL; + } else { + c = crc; + if (n) + do { + c = crc_32_tab[((int) c ^ (*s++)) & 0xff] ^ (c >> 8); + } while (--n); + } + crc = c; + return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */ +} + +/* bits.c -- output variable-length bit strings + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + + +/* + * PURPOSE + * + * Output variable-length bit strings. Compression can be done + * to a file or to memory. (The latter is not supported in this version.) + * + * DISCUSSION + * + * The PKZIP "deflate" file format interprets compressed file data + * as a sequence of bits. Multi-bit strings in the file may cross + * byte boundaries without restriction. + * + * The first bit of each byte is the low-order bit. + * + * The routines in this file allow a variable-length bit value to + * be output right-to-left (useful for literal values). For + * left-to-right output (useful for code strings from the tree routines), + * the bits must have been reversed first with bi_reverse(). + * + * For in-memory compression, the compressed bit stream goes directly + * into the requested output buffer. The input data is read in blocks + * by the mem_read() function. The buffer is limited to 64K on 16 bit + * machines. + * + * INTERFACE + * + * void bi_init (FILE *zipfile) + * Initialize the bit string routines. + * + * void send_bits (int value, int length) + * Write out a bit string, taking the source bits right to + * left. + * + * int bi_reverse (int value, int length) + * Reverse the bits of a bit string, taking the source bits left to + * right and emitting them right to left. + * + * void bi_windup (void) + * Write out any remaining bits in an incomplete byte. + * + * void copy_block(char *buf, unsigned len, int header) + * Copy a stored block to the zip file, storing first the length and + * its one's complement if requested. + * + */ + +/* =========================================================================== + * Local data used by the "bit string" routines. + */ + +static file_t zfile; /* output gzip file */ + +static unsigned short bi_buf; + +/* Output buffer. bits are inserted starting at the bottom (least significant + * bits). + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +static int bi_valid; + +/* Current input function. Set to mem_read for in-memory compression */ + +#ifdef DEBUG +ulg bits_sent; /* bit length of the compressed data */ +#endif + +/* =========================================================================== + * Initialize the bit string routines. + */ +static void bi_init(file_t zipfile) +{ + zfile = zipfile; + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = 0L; +#endif + + /* Set the defaults for file compression. They are set by memcompress + * for in-memory compression. + */ + if (zfile != NO_FILE) { + read_buf = file_read; + } +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +static void send_bits(int value, int length) +{ +#ifdef DEBUG + Tracev((stderr, " l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + bits_sent += (ulg) length; +#endif + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (bi_valid > (int) Buf_size - length) { + bi_buf |= (value << bi_valid); + put_short(bi_buf); + bi_buf = (ush) value >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } else { + bi_buf |= value << bi_valid; + bi_valid += length; + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +static unsigned bi_reverse(unsigned code, int len) +{ + register unsigned res = 0; + + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +static void bi_windup() +{ + if (bi_valid > 8) { + put_short(bi_buf); + } else if (bi_valid > 0) { + put_byte(bi_buf); + } + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = (bits_sent + 7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +static void copy_block(char *buf, unsigned len, int header) +{ + bi_windup(); /* align on byte boundary */ + + if (header) { + put_short((ush) len); + put_short((ush) ~ len); +#ifdef DEBUG + bits_sent += 2 * 16; +#endif + } +#ifdef DEBUG + bits_sent += (ulg) len << 3; +#endif + while (len--) { + put_byte(*buf++); + } +} + +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* + * PURPOSE + * + * Identify new text as repetitions of old text within a fixed- + * length sliding window trailing behind the new text. + * + * DISCUSSION + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many info-zippers for bug reports and testing. + * + * REFERENCES + * + * APPNOTE.TXT documentation file in PKZIP 1.93a distribution. + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + * INTERFACE + * + * void lm_init (int pack_level, ush *flags) + * Initialize the "longest match" routines for a new file + * + * ulg deflate (void) + * Processes a new input file and return its compressed length. Sets + * the compressed length, crc, deflate flags and internal file + * attributes. + */ + + +/* =========================================================================== + * Configuration parameters + */ + +/* Compile with MEDIUM_MEM to reduce the memory requirements or + * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the + * entire input file can be held in memory (not possible on 16 bit systems). + * Warning: defining these symbols affects HASH_BITS (see below) and thus + * affects the compression ratio. The compressed output + * is still correct, and might even be smaller in some cases. + */ + +#ifdef SMALL_MEM +# define HASH_BITS 13 /* Number of bits used to hash strings */ +#endif +#ifdef MEDIUM_MEM +# define HASH_BITS 14 +#endif +#ifndef HASH_BITS +# define HASH_BITS 15 + /* For portability to 16 bit machines, do not use values above 15. */ +#endif + +/* To save space (see unlzw.c), we overlay prev+head with tab_prefix and + * window with tab_suffix. Check that we can do this: + */ +#if (WSIZE<<1) > (1< BITS-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) { + bb_error_msg(" start %d, match %d, length %d", start, match, length); + bb_error_msg("invalid match"); + } + if (verbose > 1) { + bb_error_msg("\\[%d,%d]", start - match, length); + do { + putc(window[start++], stderr); + } while (--length != 0); + } +} +#else +# define check_match(start, match, length) +#endif + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ +static void fill_window() +{ + register unsigned n, m; + unsigned more = + (unsigned) (window_size - (ulg) lookahead - (ulg) strstart); + /* Amount of free space at the end of the window. */ + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned) EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (strstart >= WSIZE + MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + Assert(window_size == (ulg) 2 * WSIZE, "no sliding with BIG_MEM"); + + memcpy((char *) window, (char *) window + WSIZE, (unsigned) WSIZE); + match_start -= WSIZE; + strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + block_start -= (long) WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = head[n]; + head[n] = (Pos) (m >= WSIZE ? m - WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = prev[n]; + prev[n] = (Pos) (m >= WSIZE ? m - WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + /* At this point, more >= 2 */ + if (!eofile) { + n = read_buf((char *) window + strstart + lookahead, more); + if (n == 0 || n == (unsigned) EOF) { + eofile = 1; + } else { + lookahead += n; + } + } +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(eof) \ + flush_block(block_start >= 0L ? (char*)&window[(unsigned)block_start] : \ + (char*)NULL, (long)strstart - block_start, (eof)) + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +static ulg deflate() +{ + IPos hash_head; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH - 1; /* length of best match */ + + /* Process the input block. */ + while (lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + prev_length = match_length, prev_match = match_start; + match_length = MIN_MATCH - 1; + + if (hash_head != NIL && prev_length < max_lazy_match && + strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length = longest_match(hash_head); + /* longest_match() sets match_start */ + if (match_length > lookahead) + match_length = lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && strstart - match_start > TOO_FAR) { + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (prev_length >= MIN_MATCH && match_length <= prev_length) { + + check_match(strstart - 1, prev_match, prev_length); + + flush = + ct_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + lookahead -= prev_length - 1; + prev_length -= 2; + do { + strstart++; + INSERT_STRING(strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH - 1; + strstart++; + if (flush) + FLUSH_BLOCK(0), block_start = strstart; + + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr, "%c", window[strstart - 1])); + if (ct_tally(0, window[strstart - 1])) { + FLUSH_BLOCK(0), block_start = strstart; + } + strstart++; + lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + strstart++; + lookahead--; + } + Assert(strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (lookahead < MIN_LOOKAHEAD && !eofile) + fill_window(); + } + if (match_available) + ct_tally(0, window[strstart - 1]); + + return FLUSH_BLOCK(1); /* eof */ +} + +/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the license_msg below and the file COPYING for the software license. + * See the file algorithm.doc for the compression algorithms and file formats. + */ + +/* Compress files with zip algorithm and 'compress' interface. + * See usage() and help() functions below for all options. + * Outputs: + * file.gz: compressed file with same mode, owner, and utimes + * or stdout with -c option or if stdin used as input. + * If the output file name had to be truncated, the original name is kept + * in the compressed file. + */ + + /* configuration */ + +typedef struct dirent dir_type; + +typedef RETSIGTYPE(*sig_type) (int); + +/* ======================================================================== */ +int gzip_main(int argc, char **argv) +{ + int result; + int inFileNum; + int outFileNum; + struct stat statBuf; + char *delFileName; + int tostdout = 0; + int force = 0; + int opt; + + while ((opt = getopt(argc, argv, "cf123456789dq")) != -1) { + switch (opt) { + case 'c': + tostdout = 1; + break; + case 'f': + force = 1; + break; + /* Ignore 1-9 (compression level) options */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case 'q': + break; +#ifdef CONFIG_GUNZIP + case 'd': + optind = 1; + return gunzip_main(argc, argv); +#endif + default: + bb_show_usage(); + } + } + + foreground = signal(SIGINT, SIG_IGN) != SIG_IGN; + if (foreground) { + (void) signal(SIGINT, (sig_type) abort_gzip); + } +#ifdef SIGTERM + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { + (void) signal(SIGTERM, (sig_type) abort_gzip); + } +#endif +#ifdef SIGHUP + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { + (void) signal(SIGHUP, (sig_type) abort_gzip); + } +#endif + + strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix) - 1); + z_len = strlen(z_suffix); + + /* Allocate all global buffers (for DYN_ALLOC option) */ + ALLOC(uch, inbuf, INBUFSIZ + INBUF_EXTRA); + ALLOC(uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA); + ALLOC(ush, d_buf, DIST_BUFSIZE); + ALLOC(uch, window, 2L * WSIZE); + ALLOC(ush, tab_prefix, 1L << BITS); + + clear_bufs(); + part_nb = 0; + + if (optind == argc) { + time_stamp = 0; + ifile_size = -1L; + zip(STDIN_FILENO, STDOUT_FILENO); + } else { + int i; + + for (i = optind; i < argc; i++) { + char *path = NULL; + + if (strcmp(argv[i], "-") == 0) { + time_stamp = 0; + ifile_size = -1L; + inFileNum = STDIN_FILENO; + outFileNum = STDOUT_FILENO; + } else { + inFileNum = open(argv[i], O_RDONLY); + if (inFileNum < 0 || fstat(inFileNum, &statBuf) < 0) + bb_perror_msg_and_die("%s", argv[i]); + time_stamp = statBuf.st_ctime; + ifile_size = statBuf.st_size; + + if (!tostdout) { + path = xmalloc(strlen(argv[i]) + 4); + strcpy(path, argv[i]); + strcat(path, ".gz"); + + /* Open output file */ +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) + outFileNum = + open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW); +#else + outFileNum = open(path, O_RDWR | O_CREAT | O_EXCL); +#endif + if (outFileNum < 0) { + bb_perror_msg("%s", path); + free(path); + continue; + } + + /* Set permissions on the file */ + fchmod(outFileNum, statBuf.st_mode); + } else + outFileNum = STDOUT_FILENO; + } + + if (path == NULL && isatty(outFileNum) && force == 0) { + bb_error_msg + ("compressed data not written to a terminal. Use -f to force compression."); + free(path); + continue; + } + + result = zip(inFileNum, outFileNum); + + if (path != NULL) { + close(inFileNum); + close(outFileNum); + + /* Delete the original file */ + if (result == OK) + delFileName = argv[i]; + else + delFileName = path; + + if (unlink(delFileName) < 0) + bb_perror_msg("%s", delFileName); + } + + free(path); + } + } + + return (exit_code); +} + +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* + * PURPOSE + * + * Encode various sets of source values using variable-length + * binary code trees. + * + * DISCUSSION + * + * The PKZIP "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in the ZIP file in a compressed form + * which is itself a Huffman encoding of the lengths of + * all the code strings (in ascending order by source values). + * The actual code strings are reconstructed from the lengths in + * the UNZIP process, as described in the "application note" + * (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program. + * + * REFERENCES + * + * Lynch, Thomas J. + * Data Compression: Techniques and Applications, pp. 53-55. + * Lifetime Learning Publications, 1985. ISBN 0-534-03418-7. + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + * + * INTERFACE + * + * void ct_init (ush *attr, int *methodp) + * Allocate the match buffer, initialize the various tables and save + * the location of the internal file attribute (ascii/binary) and + * method (DEFLATE/STORE) + * + * void ct_tally (int dist, int lc); + * Save the match info and tally the frequency counts. + * + * long flush_block (char *buf, ulg stored_len, int eof) + * Determine the best encoding for the current block: dynamic trees, + * static trees or store, and output the encoded block to the zip + * file. Returns the total compressed length for the file so far. + * + */ + +/* =========================================================================== + * Constants + */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +typedef uch extra_bits_t; + +/* extra bits for each length code */ +static const extra_bits_t extra_lbits[LENGTH_CODES] + = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, + 4, 4, 5, 5, 5, 5, 0 +}; + +/* extra bits for each distance code */ +static const extra_bits_t extra_dbits[D_CODES] + = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 10, 10, 11, 11, 12, 12, 13, 13 +}; + +/* extra bits for each bit length code */ +static const extra_bits_t extra_blbits[BL_CODES] += { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 }; + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#ifndef LIT_BUFSIZE +# ifdef SMALL_MEM +# define LIT_BUFSIZE 0x2000 +# else +# ifdef MEDIUM_MEM +# define LIT_BUFSIZE 0x4000 +# else +# define LIT_BUFSIZE 0x8000 +# endif +# endif +#endif +#ifndef DIST_BUFSIZE +# define DIST_BUFSIZE LIT_BUFSIZE +#endif +/* Sizes of match buffers for literals/lengths and distances. There are + * 4 reasons for limiting LIT_BUFSIZE to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input data is + * still in the window so we can still emit a stored block even when input + * comes from standard input. (This can also be done for all blocks if + * LIT_BUFSIZE is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting trees + * more frequently. + * - I can't count above 4 + * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save + * memory at the expense of compression). Some optimizations would be possible + * if we rely on DIST_BUFSIZE == LIT_BUFSIZE. + */ +#if LIT_BUFSIZE > INBUFSIZ +#error cannot overlay l_buf and inbuf +#endif +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* =========================================================================== + * Local data + */ + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +static ct_data dyn_ltree[HEAP_SIZE]; /* literal and length tree */ +static ct_data dyn_dtree[2 * D_CODES + 1]; /* distance tree */ + +static ct_data static_ltree[L_CODES + 2]; + +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see ct_init + * below). + */ + +static ct_data static_dtree[D_CODES]; + +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +static ct_data bl_tree[2 * BL_CODES + 1]; + +/* Huffman tree for the bit lengths */ + +typedef struct tree_desc { + ct_data *dyn_tree; /* the dynamic tree */ + ct_data *static_tree; /* corresponding static tree or NULL */ + const extra_bits_t *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ + int max_code; /* largest code with non zero frequency */ +} tree_desc; + +static tree_desc l_desc = + { dyn_ltree, static_ltree, extra_lbits, LITERALS + 1, L_CODES, + MAX_BITS, 0 +}; + +static tree_desc d_desc = + { dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0 }; + +static tree_desc bl_desc = + { bl_tree, (ct_data *) 0, extra_blbits, 0, BL_CODES, MAX_BL_BITS, + 0 +}; + + +static ush bl_count[MAX_BITS + 1]; + +/* number of codes at each bit length for an optimal tree */ + +static const uch bl_order[BL_CODES] += { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +static int heap[2 * L_CODES + 1]; /* heap used to build the Huffman trees */ +static int heap_len; /* number of elements in the heap */ +static int heap_max; /* element of largest frequency */ + +/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + +static uch depth[2 * L_CODES + 1]; + +/* Depth of each subtree used as tie breaker for trees of equal frequency */ + +static uch length_code[MAX_MATCH - MIN_MATCH + 1]; + +/* length code for each normalized match length (0 == MIN_MATCH) */ + +static uch dist_code[512]; + +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +static int base_length[LENGTH_CODES]; + +/* First normalized length for each code (0 = MIN_MATCH) */ + +static int base_dist[D_CODES]; + +/* First normalized distance for each code (0 = distance of 1) */ + +#define l_buf inbuf +/* DECLARE(uch, l_buf, LIT_BUFSIZE); buffer for literals or lengths */ + +/* DECLARE(ush, d_buf, DIST_BUFSIZE); buffer for distances */ + +static uch flag_buf[(LIT_BUFSIZE / 8)]; + +/* flag_buf is a bit array distinguishing literals from lengths in + * l_buf, thus indicating the presence or absence of a distance. + */ + +static unsigned last_lit; /* running index in l_buf */ +static unsigned last_dist; /* running index in d_buf */ +static unsigned last_flags; /* running index in flag_buf */ +static uch flags; /* current flags not yet saved in flag_buf */ +static uch flag_bit; /* current bit used in flags */ + +/* bits are filled in flags starting at bit 0 (least significant). + * Note: these flags are overkill in the current code since we don't + * take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + */ + +static ulg opt_len; /* bit length of current block with optimal trees */ +static ulg static_len; /* bit length of current block with static trees */ + +static ulg compressed_len; /* total bit length of compressed file */ + + +static ush *file_type; /* pointer to UNKNOWN, BINARY or ASCII */ +static int *file_method; /* pointer to DEFLATE or STORE */ + +/* =========================================================================== + * Local (static) routines in this file. + */ + +static void init_block(void); +static void pqdownheap(ct_data * tree, int k); +static void gen_bitlen(tree_desc * desc); +static void gen_codes(ct_data * tree, int max_code); +static void build_tree(tree_desc * desc); +static void scan_tree(ct_data * tree, int max_code); +static void send_tree(ct_data * tree, int max_code); +static int build_bl_tree(void); +static void send_all_trees(int lcodes, int dcodes, int blcodes); +static void compress_block(ct_data * ltree, ct_data * dtree); +static void set_file_type(void); + + +#ifndef DEBUG +# define send_code(c, tree) send_bits(tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(c, tree) \ + { if (verbose>1) bb_error_msg("\ncd %3d ",(c)); \ + send_bits(tree[c].Code, tree[c].Len); } +#endif + +#define d_code(dist) \ + ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +static void ct_init(ush * attr, int *methodp) +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + file_type = attr; + file_method = methodp; + compressed_len = 0L; + + if (static_dtree[0].Len != 0) + return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES - 1; code++) { + base_length[code] = length; + for (n = 0; n < (1 << extra_lbits[code]); n++) { + length_code[length++] = (uch) code; + } + } + Assert(length == 256, "ct_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + length_code[length - 1] = (uch) code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1 << extra_dbits[code]); n++) { + dist_code[dist++] = (uch) code; + } + } + Assert(dist == 256, "ct_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for (; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { + dist_code[256 + dist++] = (uch) code; + } + } + Assert(dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) + bl_count[bits] = 0; + n = 0; + while (n <= 143) + static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) + static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) + static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) + static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *) static_ltree, L_CODES + 1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(); +} + +/* =========================================================================== + * Initialize a new block. + */ +static void init_block() +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) + dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) + dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) + bl_tree[n].Freq = 0; + + dyn_ltree[END_BLOCK].Freq = 1; + opt_len = static_len = 0L; + last_lit = last_dist = last_flags = 0; + flags = 0; + flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = heap[SMALLEST]; \ + heap[SMALLEST] = heap[heap_len--]; \ + pqdownheap(tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +static void pqdownheap(ct_data * tree, int k) +{ + int v = heap[k]; + int j = k << 1; /* left son of k */ + + while (j <= heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < heap_len && smaller(tree, heap[j + 1], heap[j])) + j++; + + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, heap[j])) + break; + + /* Exchange v with the smallest son */ + heap[k] = heap[j]; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +static void gen_bitlen(tree_desc * desc) +{ + ct_data *tree = desc->dyn_tree; + const extra_bits_t *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) + bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[heap[heap_max]].Len = 0; /* root of the heap */ + + for (h = heap_max + 1; h < HEAP_SIZE; h++) { + n = heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) + bits = max_length, overflow++; + tree[n].Len = (ush) bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) + continue; /* not a leaf node */ + + bl_count[bits]++; + xbits = 0; + if (n >= base) + xbits = extra[n - base]; + f = tree[n].Freq; + opt_len += (ulg) f *(bits + xbits); + + if (stree) + static_len += (ulg) f *(stree[n].Len + xbits); + } + if (overflow == 0) + return; + + Trace((stderr, "\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length - 1; + while (bl_count[bits] == 0) + bits--; + bl_count[bits]--; /* move one leaf down the tree */ + bl_count[bits + 1] += 2; /* move one overflow item as its brother */ + bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = bl_count[bits]; + while (n != 0) { + m = heap[--h]; + if (m > max_code) + continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len, + bits)); + opt_len += + ((long) bits - (long) tree[m].Len) * (long) tree[m].Freq; + tree[m].Len = (ush) bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +static void gen_codes(ct_data * tree, int max_code) +{ + ush next_code[MAX_BITS + 1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits - 1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert(code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, + "inconsistent bit counts"); + Tracev((stderr, "\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + + if (len == 0) + continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracec(tree != static_ltree, + (stderr, "\nn %3d %c l %2d c %4x (%x) ", n, + (isgraph(n) ? n : ' '), len, tree[n].Code, + next_code[len] - 1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +static void build_tree(tree_desc * desc) +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + heap_len = 0, heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + heap[++heap_len] = max_code = n; + depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (heap_len < 2) { + int new = heap[++heap_len] = (max_code < 2 ? ++max_code : 0); + + tree[new].Freq = 1; + depth[new] = 0; + opt_len--; + if (stree) + static_len -= stree[new].Len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = heap_len / 2; n >= 1; n--) + pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = heap[SMALLEST]; /* m = node of next least frequency */ + + heap[--heap_max] = n; /* keep the nodes sorted by frequency */ + heap[--heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + depth[node] = (uch) (MAX(depth[n], depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush) node; +#ifdef DUMP_BL_TREE + if (tree == bl_tree) { + bb_error_msg("\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + heap[SMALLEST] = node++; + pqdownheap(tree, SMALLEST); + + } while (heap_len >= 2); + + heap[--heap_max] = heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen((tree_desc *) desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes((ct_data *) tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +static void scan_tree(ct_data * tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) + max_count = 138, min_count = 3; + tree[max_code + 1].Len = (ush) 0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) + bl_tree[curlen].Freq++; + bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + bl_tree[REPZ_3_10].Freq++; + } else { + bl_tree[REPZ_11_138].Freq++; + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +static void send_tree(ct_data * tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + +/* tree[max_code+1].Len = -1; *//* guard already set */ + if (nextlen == 0) + max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { + send_code(curlen, bl_tree); + } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(curlen, bl_tree); + count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + + } else if (count <= 10) { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + + } else { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +static const int build_bl_tree() +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree((ct_data *) dyn_ltree, l_desc.max_code); + scan_tree((ct_data *) dyn_dtree, d_desc.max_code); + + /* Build the bit length tree: */ + build_tree((tree_desc *) (&bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (bl_tree[bl_order[max_blindex]].Len != 0) + break; + } + /* Update opt_len to include the bit length tree and counts */ + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", opt_len, static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +static void send_all_trees(int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + Assert(lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert(lcodes <= L_CODES && dcodes <= D_CODES + && blcodes <= BL_CODES, "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(lcodes - 257, 5); /* not +255 as stated in appnote.txt */ + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", bits_sent)); + + send_tree((ct_data *) dyn_ltree, lcodes - 1); /* send the literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", bits_sent)); + + send_tree((ct_data *) dyn_dtree, dcodes - 1); /* send the distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", bits_sent)); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +static ulg flush_block(char *buf, ulg stored_len, int eof) +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + flag_buf[last_flags] = flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*file_type == (ush) UNKNOWN) + set_file_type(); + + /* Construct the literal and distance trees */ + build_tree((tree_desc *) (&l_desc)); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len)); + + build_tree((tree_desc *) (&d_desc)); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", opt_len, static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + Trace((stderr, + "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, opt_len, static_lenb, static_len, stored_len, + last_lit, last_dist)); + + if (static_lenb <= opt_lenb) + opt_lenb = static_lenb; + + /* If compression failed and this is the first and last block, + * and if the zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ + if (stored_len <= opt_lenb && eof && compressed_len == 0L && seekable()) { + /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ + if (buf == (char *) 0) + bb_error_msg("block vanished"); + + copy_block(buf, (unsigned) stored_len, 0); /* without header */ + compressed_len = stored_len << 3; + *file_method = STORED; + + } else if (stored_len + 4 <= opt_lenb && buf != (char *) 0) { + /* 4: two words for the lengths */ + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits((STORED_BLOCK << 1) + eof, 3); /* send block type */ + compressed_len = (compressed_len + 3 + 7) & ~7L; + compressed_len += (stored_len + 4) << 3; + + copy_block(buf, (unsigned) stored_len, 1); /* with header */ + + } else if (static_lenb == opt_lenb) { + send_bits((STATIC_TREES << 1) + eof, 3); + compress_block((ct_data *) static_ltree, (ct_data *) static_dtree); + compressed_len += 3 + static_len; + } else { + send_bits((DYN_TREES << 1) + eof, 3); + send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1, + max_blindex + 1); + compress_block((ct_data *) dyn_ltree, (ct_data *) dyn_dtree); + compressed_len += 3 + opt_len; + } + Assert(compressed_len == bits_sent, "bad compressed size"); + init_block(); + + if (eof) { + bi_windup(); + compressed_len += 7; /* align on byte boundary */ + } + Tracev((stderr, "\ncomprlen %lu(%lu) ", compressed_len >> 3, + compressed_len - 7 * eof)); + + return compressed_len >> 3; +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +static int ct_tally(int dist, int lc) +{ + l_buf[last_lit++] = (uch) lc; + if (dist == 0) { + /* lc is the unmatched char */ + dyn_ltree[lc].Freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush) dist < (ush) MAX_DIST && + (ush) lc <= (ush) (MAX_MATCH - MIN_MATCH) && + (ush) d_code(dist) < (ush) D_CODES, "ct_tally: bad match"); + + dyn_ltree[length_code[lc] + LITERALS + 1].Freq++; + dyn_dtree[d_code(dist)].Freq++; + + d_buf[last_dist++] = (ush) dist; + flags |= flag_bit; + } + flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((last_lit & 7) == 0) { + flag_buf[last_flags++] = flags; + flags = 0, flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if ((last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg) last_lit * 8L; + ulg in_length = (ulg) strstart - block_start; + int dcode; + + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += + (ulg) dyn_dtree[dcode].Freq * (5L + extra_dbits[dcode]); + } + out_length >>= 3; + Trace((stderr, + "\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + last_lit, last_dist, in_length, out_length, + 100L - out_length * 100L / in_length)); + if (last_dist < last_lit / 2 && out_length < in_length / 2) + return 1; + } + return (last_lit == LIT_BUFSIZE - 1 || last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +static void compress_block(ct_data * ltree, ct_data * dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (last_lit != 0) + do { + if ((lx & 7) == 0) + flag = flag_buf[fx++]; + lc = l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr, " '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = length_code[lc]; + send_code(code + LITERALS + 1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(lc, extra); /* send the extra length bits */ + } + dist = d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + Assert(code < D_CODES, "bad d_code"); + + send_code(code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < last_lit); + + send_code(END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +static void set_file_type() +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + + while (n < 7) + bin_freq += dyn_ltree[n++].Freq; + while (n < 128) + ascii_freq += dyn_ltree[n++].Freq; + while (n < LITERALS) + bin_freq += dyn_ltree[n++].Freq; + *file_type = bin_freq > (ascii_freq >> 2) ? BINARY : ASCII; + if (*file_type == BINARY && translate_eol) { + bb_error_msg("-l used on binary file"); + } +} + +/* zip.c -- compress files to the gzip or pkzip format + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + + +static ulg crc; /* crc on uncompressed file data */ +static long header_bytes; /* number of bytes in gzip header */ + +static void put_long(ulg n) +{ + put_short((n) & 0xffff); + put_short(((ulg) (n)) >> 16); +} + +/* put_header_byte is used for the compressed output + * - for the initial 4 bytes that can't overflow the buffer. + */ +#define put_header_byte(c) {outbuf[outcnt++]=(uch)(c);} + +/* =========================================================================== + * Deflate in to out. + * IN assertions: the input and output buffers are cleared. + * The variables time_stamp and save_orig_name are initialized. + */ +static int zip(int in, int out) +{ + uch my_flags = 0; /* general purpose bit flags */ + ush attr = 0; /* ascii/binary flag */ + ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ + + ifd = in; + ofd = out; + outcnt = 0; + + /* Write the header to the gzip file. See algorithm.doc for the format */ + + + method = DEFLATED; + put_header_byte(GZIP_MAGIC[0]); /* magic header */ + put_header_byte(GZIP_MAGIC[1]); + put_header_byte(DEFLATED); /* compression method */ + + put_header_byte(my_flags); /* general flags */ + put_long(time_stamp); + + /* Write deflated file to zip file */ + crc = updcrc(0, 0); + + bi_init(out); + ct_init(&attr, &method); + lm_init(&deflate_flags); + + put_byte((uch) deflate_flags); /* extra flags */ + put_byte(OS_CODE); /* OS identifier */ + + header_bytes = (long) outcnt; + + (void) deflate(); + + /* Write the crc and uncompressed size */ + put_long(crc); + put_long(isize); + header_bytes += 2 * sizeof(long); + + flush_outbuf(); + return OK; +} + + +/* =========================================================================== + * Read a new buffer from the current input file, perform end-of-line + * translation, and update the crc and input file size. + * IN assertion: size >= 2 (for end-of-line translation) + */ +static int file_read(char *buf, unsigned size) +{ + unsigned len; + + Assert(insize == 0, "inbuf not empty"); + + len = read(ifd, buf, size); + if (len == (unsigned) (-1) || len == 0) + return (int) len; + + crc = updcrc((uch *) buf, len); + isize += (ulg) len; + return (int) len; +} + +/* =========================================================================== + * Write the output buffer outbuf[0..outcnt-1] and update bytes_out. + * (used for the compressed data only) + */ +static void flush_outbuf() +{ + if (outcnt == 0) + return; + + write_buf(ofd, (char *) outbuf, outcnt); + outcnt = 0; +} diff --git a/busybox/archival/libunarchive/Makefile b/busybox/archival/libunarchive/Makefile new file mode 100644 index 000000000..e985fa49f --- /dev/null +++ b/busybox/archival/libunarchive/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=../.. +top_builddir=../.. +srcdir=$(top_srcdir)/archival/libunarchive +LIBUNARCHIVE_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/archival/libunarchive/Makefile.in b/busybox/archival/libunarchive/Makefile.in new file mode 100644 index 000000000..809b0e10e --- /dev/null +++ b/busybox/archival/libunarchive/Makefile.in @@ -0,0 +1,84 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +LIBUNARCHIVE_AR:=libunarchive.a +ifndef $(LIBUNARCHIVE_DIR) +LIBUNARCHIVE_DIR:=$(top_builddir)/archival/libunarchive/ +endif +srcdir=$(top_srcdir)/archvial/libunarchive + +LIBUNARCHIVE-y:= \ +\ + data_skip.o \ + data_extract_all.o \ + data_extract_to_stdout.o \ + data_extract_to_buffer.o \ +\ + filter_accept_all.o \ + filter_accept_list.o \ + filter_accept_reject_list.o \ +\ + header_skip.o \ + header_list.o \ + header_verbose_list.o \ +\ + archive_xread_all.o \ + archive_xread_all_eof.o \ +\ + seek_by_char.o \ + seek_by_jump.o \ +\ + data_align.o \ + find_list_entry.o \ + open_transformer.o \ + init_handle.o + +GUNZIP_FILES:= check_header_gzip.o decompress_unzip.o +DPKG_FILES:= \ + get_header_ar.o \ + unpack_ar_archive.o \ + get_header_tar.o \ + filter_accept_list_reassign.o + +LIBUNARCHIVE-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o +LIBUNARCHIVE-$(CONFIG_BUNZIP2) += decompress_bunzip2.o +LIBUNARCHIVE-$(CONFIG_CPIO) += get_header_cpio.o +LIBUNARCHIVE-$(CONFIG_DPKG) += $(DPKG_FILES) +LIBUNARCHIVE-$(CONFIG_DPKG_DEB) += $(DPKG_FILES) +LIBUNARCHIVE-$(CONFIG_FEATURE_DEB_TAR_GZ) += $(GUNZIP_FILES) get_header_tar_gz.o +LIBUNARCHIVE-$(CONFIG_FEATURE_DEB_TAR_BZ2) += decompress_bunzip2.o get_header_tar_bz2.o +LIBUNARCHIVE-$(CONFIG_GUNZIP) += $(GUNZIP_FILES) +LIBUNARCHIVE-$(CONFIG_FEATURE_GUNZIP_UNCOMPRESS) += decompress_uncompress.o +LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += $(GUNZIP_FILES) get_header_cpio.o +LIBUNARCHIVE-$(CONFIG_RPM) += $(GUNZIP_FILES) get_header_cpio.o +LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o +LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_BZIP2) += decompress_bunzip2.o get_header_tar_bz2.o +LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_GZIP) += $(GUNZIP_FILES) get_header_tar_gz.o +LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_COMPRESS) += decompress_uncompress.o +LIBUNARCHIVE-$(CONFIG_UNCOMPRESS) += decompress_uncompress.o +LIBUNARCHIVE-$(CONFIG_UNZIP) += $(GUNZIP_FILES) + +libraries-y+=$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR) + +$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR): $(patsubst %,$(LIBUNARCHIVE_DIR)%, $(LIBUNARCHIVE-y)) + $(AR) -ro $@ $(patsubst %,$(LIBUNARCHIVE_DIR)%, $(LIBUNARCHIVE-y)) + +$(LIBUNARCHIVA_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/archival/libunarchive/archive_xread_all.c b/busybox/archival/libunarchive/archive_xread_all.c new file mode 100644 index 000000000..ba9ade2d5 --- /dev/null +++ b/busybox/archival/libunarchive/archive_xread_all.c @@ -0,0 +1,32 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include "unarchive.h" +#include "libbb.h" + +extern void archive_xread_all(const archive_handle_t *archive_handle, void *buf, const size_t count) +{ + ssize_t size; + + size = bb_full_read(archive_handle->src_fd, buf, count); + if (size != count) { + bb_error_msg_and_die("Short read"); + } + return; +} diff --git a/busybox/archival/libunarchive/archive_xread_all_eof.c b/busybox/archival/libunarchive/archive_xread_all_eof.c new file mode 100644 index 000000000..8084e3524 --- /dev/null +++ b/busybox/archival/libunarchive/archive_xread_all_eof.c @@ -0,0 +1,32 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include "unarchive.h" +#include "libbb.h" + +extern ssize_t archive_xread_all_eof(archive_handle_t *archive_handle, unsigned char *buf, size_t count) +{ + ssize_t size; + + size = bb_full_read(archive_handle->src_fd, buf, count); + if ((size != 0) && (size != count)) { + bb_perror_msg_and_die("Short read, read %d of %d", size, count); + } + return(size); +} diff --git a/busybox/archival/libunarchive/check_header_gzip.c b/busybox/archival/libunarchive/check_header_gzip.c new file mode 100644 index 000000000..13832c240 --- /dev/null +++ b/busybox/archival/libunarchive/check_header_gzip.c @@ -0,0 +1,57 @@ +#include +#include +#include "libbb.h" + +extern void check_header_gzip(int src_fd) +{ + union { + unsigned char raw[8]; + struct { + unsigned char method; + unsigned char flags; + unsigned int mtime; + unsigned char xtra_flags; + unsigned char os_flags; + } formated; + } header; + + bb_xread_all(src_fd, header.raw, 8); + + /* Check the compression method */ + if (header.formated.method != 8) { + bb_error_msg_and_die("Unknown compression method %d", + header.formated.method); + } + + if (header.formated.flags & 0x04) { + /* bit 2 set: extra field present */ + unsigned char extra_short; + + extra_short = bb_xread_char(src_fd) + (bb_xread_char(src_fd) << 8); + while (extra_short > 0) { + /* Ignore extra field */ + bb_xread_char(src_fd); + extra_short--; + } + } + + /* Discard original name if any */ + if (header.formated.flags & 0x08) { + /* bit 3 set: original file name present */ + while(bb_xread_char(src_fd) != 0); + } + + /* Discard file comment if any */ + if (header.formated.flags & 0x10) { + /* bit 4 set: file comment present */ + while(bb_xread_char(src_fd) != 0); + } + + /* Read the header checksum */ + if (header.formated.flags & 0x02) { + bb_xread_char(src_fd); + bb_xread_char(src_fd); + } + + return; +} diff --git a/busybox/archival/libunarchive/data_align.c b/busybox/archival/libunarchive/data_align.c new file mode 100644 index 000000000..1d433957d --- /dev/null +++ b/busybox/archival/libunarchive/data_align.c @@ -0,0 +1,33 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include +#include + +#include "libbb.h" +#include "unarchive.h" + +extern void data_align(archive_handle_t *archive_handle, const unsigned short boundary) +{ + const unsigned short skip_amount = (boundary - (archive_handle->offset % boundary)) % boundary; + + archive_handle->seek(archive_handle, skip_amount); + archive_handle->offset += skip_amount; + + return; +} diff --git a/busybox/archival/libunarchive/data_extract_all.c b/busybox/archival/libunarchive/data_extract_all.c new file mode 100644 index 000000000..d10d665f6 --- /dev/null +++ b/busybox/archival/libunarchive/data_extract_all.c @@ -0,0 +1,124 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "libbb.h" +#include "unarchive.h" + +extern void data_extract_all(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + int dst_fd; + int res; + + if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) { + char *name = bb_xstrdup(file_header->name); + bb_make_directory (dirname(name), -1, FILEUTILS_RECUR); + free(name); + } + + /* Check if the file already exists */ + if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) { + /* Remove the existing entry if it exists */ + if (((file_header->mode & S_IFMT) != S_IFDIR) && (unlink(file_header->name) == -1) && (errno != ENOENT)) { + bb_perror_msg_and_die("Couldnt remove old file"); + } + } + else if (archive_handle->flags & ARCHIVE_EXTRACT_NEWER) { + /* Remove the existing entry if its older than the extracted entry */ + struct stat statbuf; + if (lstat(file_header->name, &statbuf) == -1) { + if (errno != ENOENT) { + bb_perror_msg_and_die("Couldnt stat old file"); + } + } + else if (statbuf.st_mtime <= file_header->mtime) { + if (!(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + bb_error_msg("%s not created: newer or same age file exists", file_header->name); + } + data_skip(archive_handle); + return; + } + else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) { + bb_perror_msg_and_die("Couldnt remove old file %s", file_header->name); + } + } + + /* Handle hard links separately + * We identified hard links as regular files of size 0 with a symlink */ + if (S_ISREG(file_header->mode) && (file_header->link_name) && (file_header->size == 0)) { + /* hard link */ + res = link(file_header->link_name, file_header->name); + if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + bb_perror_msg("Couldnt create hard link"); + } + } else { + /* Create the filesystem entry */ + switch(file_header->mode & S_IFMT) { + case S_IFREG: { + /* Regular file */ + dst_fd = bb_xopen(file_header->name, O_WRONLY | O_CREAT | O_EXCL); + bb_copyfd_size(archive_handle->src_fd, dst_fd, file_header->size); + close(dst_fd); + break; + } + case S_IFDIR: + res = mkdir(file_header->name, file_header->mode); + if ((errno != EISDIR) && (res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + bb_perror_msg("extract_archive: %s", file_header->name); + } + break; + case S_IFLNK: + /* Symlink */ + res = symlink(file_header->link_name, file_header->name); + if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + bb_perror_msg("Cannot create symlink from %s to '%s'", file_header->name, file_header->link_name); + } + break; + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + res = mknod(file_header->name, file_header->mode, file_header->device); + if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + bb_perror_msg("Cannot create node %s", file_header->name); + } + break; + default: + bb_error_msg_and_die("Unrecognised file type"); + } + } + + lchown(file_header->name, file_header->uid, file_header->gid); + if ((file_header->mode & S_IFMT) != S_IFLNK) { + chmod(file_header->name, file_header->mode); + } + + if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) { + struct utimbuf t; + t.actime = t.modtime = file_header->mtime; + utime(file_header->name, &t); + } +} diff --git a/busybox/archival/libunarchive/data_extract_to_buffer.c b/busybox/archival/libunarchive/data_extract_to_buffer.c new file mode 100644 index 000000000..db5521bcb --- /dev/null +++ b/busybox/archival/libunarchive/data_extract_to_buffer.c @@ -0,0 +1,28 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "libbb.h" +#include "unarchive.h" + +extern void data_extract_to_buffer(archive_handle_t *archive_handle) +{ + const unsigned int size = archive_handle->file_header->size; + + archive_handle->buffer = xmalloc(size + 1); + + archive_xread_all(archive_handle, archive_handle->buffer, size); + archive_handle->buffer[size] = '\0'; +} diff --git a/busybox/archival/libunarchive/data_extract_to_stdout.c b/busybox/archival/libunarchive/data_extract_to_stdout.c new file mode 100644 index 000000000..df2bca6ef --- /dev/null +++ b/busybox/archival/libunarchive/data_extract_to_stdout.c @@ -0,0 +1,23 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "unarchive.h" +#include + +extern void data_extract_to_stdout(archive_handle_t *archive_handle) +{ + bb_copyfd_size(archive_handle->src_fd, STDOUT_FILENO, archive_handle->file_header->size); +} diff --git a/busybox/archival/libunarchive/data_skip.c b/busybox/archival/libunarchive/data_skip.c new file mode 100644 index 000000000..b82c9065b --- /dev/null +++ b/busybox/archival/libunarchive/data_skip.c @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include "unarchive.h" +#include "libbb.h" + +extern void data_skip(archive_handle_t *archive_handle) +{ + archive_handle->seek(archive_handle, archive_handle->file_header->size); +} diff --git a/busybox/archival/libunarchive/decompress_bunzip2.c b/busybox/archival/libunarchive/decompress_bunzip2.c new file mode 100644 index 000000000..259a47776 --- /dev/null +++ b/busybox/archival/libunarchive/decompress_bunzip2.c @@ -0,0 +1,611 @@ +/* vi: set sw=4 ts=4: */ +/* Small bzip2 deflate implementation, by Rob Landley (rob@landley.net). + + Based on bzip2 decompression code by Julian R Seward (jseward@acm.org), + which also acknowledges contributions by Mike Burrows, David Wheeler, + Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten, + Robert Sedgewick, and Jon L. Bentley. + + This code is licensed under the LGPLv2: + LGPL (http://www.gnu.org/copyleft/lgpl.html +*/ + +/* + Size and speed optimizations by Manuel Novoa III (mjn3@codepoet.org). + + More efficient reading of Huffman codes, a streamlined read_bunzip() + function, and various other tweaks. In (limited) tests, approximately + 20% faster than bzcat on x86 and about 10% faster on arm. + + Note that about 2/3 of the time is spent in read_unzip() reversing + the Burrows-Wheeler transformation. Much of that time is delay + resulting from cache misses. + + I would ask that anyone benefiting from this work, especially those + using it in commercial products, consider making a donation to my local + non-profit hospice organization in the name of the woman I loved, who + passed away Feb. 12, 2003. + + In memory of Toni W. Hagan + + Hospice of Acadiana, Inc. + 2600 Johnston St., Suite 200 + Lafayette, LA 70503-3240 + + Phone (337) 232-1234 or 1-800-738-2226 + Fax (337) 232-1297 + + http://www.hospiceacadiana.com/ + + Manuel + */ + +#include +#include +#include +#include +#include +#include + +#include "libbb.h" + +/* Constants for Huffman coding */ +#define MAX_GROUPS 6 +#define GROUP_SIZE 50 /* 64 would have been more efficient */ +#define MAX_HUFCODE_BITS 20 /* Longest Huffman code allowed */ +#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */ +#define SYMBOL_RUNA 0 +#define SYMBOL_RUNB 1 + +/* Status return values */ +#define RETVAL_OK 0 +#define RETVAL_LAST_BLOCK (-1) +#define RETVAL_NOT_BZIP_DATA (-2) +#define RETVAL_UNEXPECTED_INPUT_EOF (-3) +#define RETVAL_UNEXPECTED_OUTPUT_EOF (-4) +#define RETVAL_DATA_ERROR (-5) +#define RETVAL_OUT_OF_MEMORY (-6) +#define RETVAL_OBSOLETE_INPUT (-7) + +/* Other housekeeping constants */ +#define IOBUF_SIZE 4096 + +/* This is what we know about each Huffman coding group */ +struct group_data { + /* We have an extra slot at the end of limit[] for a sentinal value. */ + int limit[MAX_HUFCODE_BITS+1],base[MAX_HUFCODE_BITS],permute[MAX_SYMBOLS]; + int minLen, maxLen; +}; + +/* Structure holding all the housekeeping data, including IO buffers and + memory that persists between calls to bunzip */ +typedef struct { + /* State for interrupting output loop */ + int writeCopies,writePos,writeRunCountdown,writeCount,writeCurrent; + /* I/O tracking data (file handles, buffers, positions, etc.) */ + int in_fd,out_fd,inbufCount,inbufPos /*,outbufPos*/; + unsigned char *inbuf /*,*outbuf*/; + unsigned int inbufBitCount, inbufBits; + /* The CRC values stored in the block header and calculated from the data */ + unsigned int crc32Table[256],headerCRC, totalCRC, writeCRC; + /* Intermediate buffer and its size (in bytes) */ + unsigned int *dbuf, dbufSize; + /* These things are a bit too big to go on the stack */ + unsigned char selectors[32768]; /* nSelectors=15 bits */ + struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */ + /* For I/O error handling */ + jmp_buf jmpbuf; +} bunzip_data; + +/* Return the next nnn bits of input. All reads from the compressed input + are done through this function. All reads are big endian */ +static unsigned int get_bits(bunzip_data *bd, char bits_wanted) +{ + unsigned int bits=0; + + /* If we need to get more data from the byte buffer, do so. (Loop getting + one byte at a time to enforce endianness and avoid unaligned access.) */ + while (bd->inbufBitCountinbufPos==bd->inbufCount) { + if((bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)) <= 0) + longjmp(bd->jmpbuf,RETVAL_UNEXPECTED_INPUT_EOF); + bd->inbufPos=0; + } + /* Avoid 32-bit overflow (dump bit buffer to top of output) */ + if(bd->inbufBitCount>=24) { + bits=bd->inbufBits&((1<inbufBitCount)-1); + bits_wanted-=bd->inbufBitCount; + bits<<=bits_wanted; + bd->inbufBitCount=0; + } + /* Grab next 8 bits of input from buffer. */ + bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++]; + bd->inbufBitCount+=8; + } + /* Calculate result */ + bd->inbufBitCount-=bits_wanted; + bits|=(bd->inbufBits>>bd->inbufBitCount)&((1<dbuf; + dbufSize=bd->dbufSize; + selectors=bd->selectors; + /* Reset longjmp I/O error handling */ + i=setjmp(bd->jmpbuf); + if(i) return i; + /* Read in header signature and CRC, then validate signature. + (last block signature means CRC is for whole file, return now) */ + i = get_bits(bd,24); + j = get_bits(bd,24); + bd->headerCRC=get_bits(bd,32); + if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK; + if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA; + /* We can add support for blockRandomised if anybody complains. There was + some code for this in busybox 1.0.0-pre3, but nobody ever noticed that + it didn't actually work. */ + if(get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT; + if((origPtr=get_bits(bd,24)) > dbufSize) return RETVAL_DATA_ERROR; + /* mapping table: if some byte values are never used (encoding things + like ascii text), the compression code removes the gaps to have fewer + symbols to deal with, and writes a sparse bitfield indicating which + values were present. We make a translation table to convert the symbols + back to the corresponding bytes. */ + t=get_bits(bd, 16); + symTotal=0; + for (i=0;i<16;i++) { + if(t&(1<<(15-i))) { + k=get_bits(bd,16); + for(j=0;j<16;j++) + if(k&(1<<(15-j))) symToByte[symTotal++]=(16*i)+j; + } + } + /* How many different Huffman coding groups does this block use? */ + groupCount=get_bits(bd,3); + if (groupCount<2 || groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR; + /* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding + group. Read in the group selector list, which is stored as MTF encoded + bit runs. (MTF=Move To Front, as each value is used it's moved to the + start of the list.) */ + if(!(nSelectors=get_bits(bd, 15))) return RETVAL_DATA_ERROR; + for(i=0; i=groupCount) return RETVAL_DATA_ERROR; + /* Decode MTF to get the next selector */ + uc = mtfSymbol[j]; + for(;j;j--) mtfSymbol[j] = mtfSymbol[j-1]; + mtfSymbol[0]=selectors[i]=uc; + } + /* Read the Huffman coding tables for each group, which code for symTotal + literal symbols, plus two run symbols (RUNA, RUNB) */ + symCount=symTotal+2; + for (j=0; j (MAX_HUFCODE_BITS-1)) + return RETVAL_DATA_ERROR; + /* If first bit is 0, stop. Else second bit indicates whether + to increment or decrement the value. Optimization: grab 2 + bits and unget the second if the first was 0. */ + k = get_bits(bd,2); + if (k < 2) { + bd->inbufBitCount++; + break; + } + /* Add one if second bit 1, else subtract 1. Avoids if/else */ + t+=(((k+1)&2)-1); + } + /* Correct for the initial -1, to get the final symbol length */ + length[i]=t+1; + } + /* Find largest and smallest lengths in this group */ + minLen=maxLen=length[0]; + for(i = 1; i < symCount; i++) { + if(length[i] > maxLen) maxLen = length[i]; + else if(length[i] < minLen) minLen = length[i]; + } + /* Calculate permute[], base[], and limit[] tables from length[]. + * + * permute[] is the lookup table for converting Huffman coded symbols + * into decoded symbols. base[] is the amount to subtract from the + * value of a Huffman symbol of a given length when using permute[]. + * + * limit[] indicates the largest numerical value a symbol with a given + * number of bits can have. This is how the Huffman codes can vary in + * length: each code with a value>limit[length] needs another bit. + */ + hufGroup=bd->groups+j; + hufGroup->minLen = minLen; + hufGroup->maxLen = maxLen; + /* Note that minLen can't be smaller than 1, so we adjust the base + and limit array pointers so we're not always wasting the first + entry. We do this again when using them (during symbol decoding).*/ + base=hufGroup->base-1; + limit=hufGroup->limit-1; + /* Calculate permute[]. Concurently, initialize temp[] and limit[]. */ + pp=0; + for(i=minLen;i<=maxLen;i++) { + temp[i]=limit[i]=0; + for(t=0;tpermute[pp++] = t; + } + /* Count symbols coded for at each bit length */ + for (i=0;ilimit[length] comparison. */ + limit[i]= (pp << (maxLen - i)) - 1; + pp<<=1; + base[i+1]=pp-(t+=temp[i]); + } + limit[maxLen+1] = INT_MAX; /* Sentinal value for reading next sym. */ + limit[maxLen]=pp+temp[maxLen]-1; + base[minLen]=0; + } + /* We've finished reading and digesting the block header. Now read this + block's Huffman coded symbols from the file and undo the Huffman coding + and run length encoding, saving the result into dbuf[dbufCount++]=uc */ + + /* Initialize symbol occurrence counters and symbol Move To Front table */ + for(i=0;i<256;i++) { + byteCount[i] = 0; + mtfSymbol[i]=(unsigned char)i; + } + /* Loop through compressed symbols. */ + runPos=dbufCount=symCount=selector=0; + for(;;) { + /* Determine which Huffman coding group to use. */ + if(!(symCount--)) { + symCount=GROUP_SIZE-1; + if(selector>=nSelectors) return RETVAL_DATA_ERROR; + hufGroup=bd->groups+selectors[selector++]; + base=hufGroup->base-1; + limit=hufGroup->limit-1; + } + /* Read next Huffman-coded symbol. */ + /* Note: It is far cheaper to read maxLen bits and back up than it is + to read minLen bits and then an additional bit at a time, testing + as we go. Because there is a trailing last block (with file CRC), + there is no danger of the overread causing an unexpected EOF for a + valid compressed file. As a further optimization, we do the read + inline (falling back to a call to get_bits if the buffer runs + dry). The following (up to got_huff_bits:) is equivalent to + j=get_bits(bd,hufGroup->maxLen); + */ + while (bd->inbufBitCountmaxLen) { + if(bd->inbufPos==bd->inbufCount) { + j = get_bits(bd,hufGroup->maxLen); + goto got_huff_bits; + } + bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++]; + bd->inbufBitCount+=8; + }; + bd->inbufBitCount-=hufGroup->maxLen; + j = (bd->inbufBits>>bd->inbufBitCount)&((1<maxLen)-1); +got_huff_bits: + /* Figure how how many bits are in next symbol and unget extras */ + i=hufGroup->minLen; + while(j>limit[i]) ++i; + bd->inbufBitCount += (hufGroup->maxLen - i); + /* Huffman decode value to get nextSym (with bounds checking) */ + if ((i > hufGroup->maxLen) + || (((unsigned)(j=(j>>(hufGroup->maxLen-i))-base[i])) + >= MAX_SYMBOLS)) + return RETVAL_DATA_ERROR; + nextSym = hufGroup->permute[j]; + /* We have now decoded the symbol, which indicates either a new literal + byte, or a repeated run of the most recent literal byte. First, + check if nextSym indicates a repeated run, and if so loop collecting + how many times to repeat the last literal. */ + if (((unsigned)nextSym) <= SYMBOL_RUNB) { /* RUNA or RUNB */ + /* If this is the start of a new run, zero out counter */ + if(!runPos) { + runPos = 1; + t = 0; + } + /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at + each bit position, add 1 or 2 instead. For example, + 1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2. + You can make any bit pattern that way using 1 less symbol than + the basic or 0/1 method (except all bits 0, which would use no + symbols, but a run of length 0 doesn't mean anything in this + context). Thus space is saved. */ + t += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */ + runPos <<= 1; + continue; + } + /* When we hit the first non-run symbol after a run, we now know + how many times to repeat the last literal, so append that many + copies to our buffer of decoded symbols (dbuf) now. (The last + literal used is the one at the head of the mtfSymbol array.) */ + if(runPos) { + runPos=0; + if(dbufCount+t>=dbufSize) return RETVAL_DATA_ERROR; + + uc = symToByte[mtfSymbol[0]]; + byteCount[uc] += t; + while(t--) dbuf[dbufCount++]=uc; + } + /* Is this the terminating symbol? */ + if(nextSym>symTotal) break; + /* At this point, nextSym indicates a new literal character. Subtract + one to get the position in the MTF array at which this literal is + currently to be found. (Note that the result can't be -1 or 0, + because 0 and 1 are RUNA and RUNB. But another instance of the + first symbol in the mtf array, position 0, would have been handled + as part of a run above. Therefore 1 unused mtf position minus + 2 non-literal nextSym values equals -1.) */ + if(dbufCount>=dbufSize) return RETVAL_DATA_ERROR; + i = nextSym - 1; + uc = mtfSymbol[i]; + /* Adjust the MTF array. Since we typically expect to move only a + * small number of symbols, and are bound by 256 in any case, using + * memmove here would typically be bigger and slower due to function + * call overhead and other assorted setup costs. */ + do { + mtfSymbol[i] = mtfSymbol[i-1]; + } while (--i); + mtfSymbol[0] = uc; + uc=symToByte[uc]; + /* We have our literal byte. Save it into dbuf. */ + byteCount[uc]++; + dbuf[dbufCount++] = (unsigned int)uc; + } + /* At this point, we've read all the Huffman-coded symbols (and repeated + runs) for this block from the input stream, and decoded them into the + intermediate buffer. There are dbufCount many decoded bytes in dbuf[]. + Now undo the Burrows-Wheeler transform on dbuf. + See http://dogma.net/markn/articles/bwt/bwt.htm + */ + /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */ + j=0; + for(i=0;i<256;i++) { + k=j+byteCount[i]; + byteCount[i] = j; + j=k; + } + /* Figure out what order dbuf would be in if we sorted it. */ + for (i=0;i=dbufCount) return RETVAL_DATA_ERROR; + bd->writePos=dbuf[origPtr]; + bd->writeCurrent=(unsigned char)(bd->writePos&0xff); + bd->writePos>>=8; + bd->writeRunCountdown=5; + } + bd->writeCount=dbufCount; + + return RETVAL_OK; +} + +/* Undo burrows-wheeler transform on intermediate buffer to produce output. + If start_bunzip was initialized with out_fd=-1, then up to len bytes of + data are written to outbuf. Return value is number of bytes written or + error (all errors are negative numbers). If out_fd!=-1, outbuf and len + are ignored, data is written to out_fd and return is RETVAL_OK or error. +*/ + +static int read_bunzip(bunzip_data *bd, char *outbuf, int len) +{ + const unsigned int *dbuf; + int pos,current,previous,gotcount; + + /* If last read was short due to end of file, return last block now */ + if(bd->writeCount<0) return bd->writeCount; + + gotcount = 0; + dbuf=bd->dbuf; + pos=bd->writePos; + current=bd->writeCurrent; + + /* We will always have pending decoded data to write into the output + buffer unless this is the very first call (in which case we haven't + Huffman-decoded a block into the intermediate buffer yet). */ + + if (bd->writeCopies) { + /* Inside the loop, writeCopies means extra copies (beyond 1) */ + --bd->writeCopies; + /* Loop outputting bytes */ + for(;;) { + /* If the output buffer is full, snapshot state and return */ + if(gotcount >= len) { + bd->writePos=pos; + bd->writeCurrent=current; + bd->writeCopies++; + return len; + } + /* Write next byte into output buffer, updating CRC */ + outbuf[gotcount++] = current; + bd->writeCRC=(((bd->writeCRC)<<8) + ^bd->crc32Table[((bd->writeCRC)>>24)^current]); + /* Loop now if we're outputting multiple copies of this byte */ + if (bd->writeCopies) { + --bd->writeCopies; + continue; + } +decode_next_byte: + if (!bd->writeCount--) break; + /* Follow sequence vector to undo Burrows-Wheeler transform */ + previous=current; + pos=dbuf[pos]; + current=pos&0xff; + pos>>=8; + /* After 3 consecutive copies of the same byte, the 4th is a repeat + count. We count down from 4 instead + * of counting up because testing for non-zero is faster */ + if(--bd->writeRunCountdown) { + if(current!=previous) bd->writeRunCountdown=4; + } else { + /* We have a repeated run, this byte indicates the count */ + bd->writeCopies=current; + current=previous; + bd->writeRunCountdown=5; + /* Sometimes there are just 3 bytes (run length 0) */ + if(!bd->writeCopies) goto decode_next_byte; + /* Subtract the 1 copy we'd output anyway to get extras */ + --bd->writeCopies; + } + } + /* Decompression of this block completed successfully */ + bd->writeCRC=~bd->writeCRC; + bd->totalCRC=((bd->totalCRC<<1) | (bd->totalCRC>>31)) ^ bd->writeCRC; + /* If this block had a CRC error, force file level CRC error. */ + if(bd->writeCRC!=bd->headerCRC) { + bd->totalCRC=bd->headerCRC+1; + return RETVAL_LAST_BLOCK; + } + } + + /* Refill the intermediate buffer by Huffman-decoding next block of input */ + /* (previous is just a convenient unused temp variable here) */ + previous=get_next_block(bd); + if(previous) { + bd->writeCount=previous; + return (previous!=RETVAL_LAST_BLOCK) ? previous : gotcount; + } + bd->writeCRC=0xffffffffUL; + pos=bd->writePos; + current=bd->writeCurrent; + goto decode_next_byte; +} + +/* Allocate the structure, read file header. If in_fd==-1, inbuf must contain + a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are + ignored, and data is read from file handle into temporary buffer. */ +static int start_bunzip(bunzip_data **bdp, int in_fd, char *inbuf, int len) +{ + bunzip_data *bd; + unsigned int i,j,c; + const unsigned int BZh0=(((unsigned int)'B')<<24)+(((unsigned int)'Z')<<16) + +(((unsigned int)'h')<<8)+(unsigned int)'0'; + + /* Figure out how much data to allocate */ + i=sizeof(bunzip_data); + if(in_fd!=-1) i+=IOBUF_SIZE; + /* Allocate bunzip_data. Most fields initialize to zero. */ + bd=*bdp=xmalloc(i); + memset(bd,0,sizeof(bunzip_data)); + /* Setup input buffer */ + if(-1==(bd->in_fd=in_fd)) { + bd->inbuf=inbuf; + bd->inbufCount=len; + } else bd->inbuf=(unsigned char *)(bd+1); + /* Init the CRC32 table (big endian) */ + for(i=0;i<256;i++) { + c=i<<24; + for(j=8;j;j--) + c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1); + bd->crc32Table[i]=c; + } + /* Setup for I/O error handling via longjmp */ + i=setjmp(bd->jmpbuf); + if(i) return i; + + /* Ensure that file starts with "BZh['1'-'9']." */ + i = get_bits(bd,32); + if (((unsigned int)(i-BZh0-1)) >= 9) return RETVAL_NOT_BZIP_DATA; + + /* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of + uncompressed data. Allocate intermediate buffer for block. */ + bd->dbufSize=100000*(i-BZh0); + + bd->dbuf=xmalloc(bd->dbufSize * sizeof(int)); + return RETVAL_OK; +} + +/* Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data, + not end of file.) */ +extern int uncompressStream(int src_fd, int dst_fd) +{ + char *outbuf; + bunzip_data *bd; + int i; + + outbuf=xmalloc(IOBUF_SIZE); + if(!(i=start_bunzip(&bd,src_fd,0,0))) { + for(;;) { + if((i=read_bunzip(bd,outbuf,IOBUF_SIZE)) <= 0) break; + if(i!=write(dst_fd,outbuf,i)) { + i=RETVAL_UNEXPECTED_OUTPUT_EOF; + break; + } + } + } + /* Check CRC and release memory */ + if(i==RETVAL_LAST_BLOCK) { + if (bd->headerCRC!=bd->totalCRC) { + bb_error_msg("Data integrity error when decompressing."); + } else { + i=RETVAL_OK; + } + } + else if (i==RETVAL_UNEXPECTED_OUTPUT_EOF) { + bb_error_msg("Compressed file ends unexpectedly"); + } else { + bb_error_msg("Decompression failed"); + } + if(bd->dbuf) free(bd->dbuf); + free(bd); + free(outbuf); + + return i; +} + +#ifdef TESTING + +static char * const bunzip_errors[]={NULL,"Bad file checksum","Not bzip data", + "Unexpected input EOF","Unexpected output EOF","Data error", + "Out of memory","Obsolete (pre 0.9.5) bzip format not supported."}; + +/* Dumb little test thing, decompress stdin to stdout */ +int main(int argc, char *argv[]) +{ + int i=uncompressStream(0,1); + char c; + + if(i) fprintf(stderr,"%s\n", bunzip_errors[-i]); + else if(read(0,&c,1)) fprintf(stderr,"Trailing garbage ignored\n"); + return -i; +} +#endif diff --git a/busybox/archival/libunarchive/decompress_uncompress.c b/busybox/archival/libunarchive/decompress_uncompress.c new file mode 100644 index 000000000..e39872cbe --- /dev/null +++ b/busybox/archival/libunarchive/decompress_uncompress.c @@ -0,0 +1,293 @@ +#include "config.h" +#include "libbb.h" + +/* uncompress for busybox -- (c) 2002 Robert Griebl + * + * based on the original compress42.c source + * (see disclaimer below) + */ + + +/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992. + * + * Authors: + * Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * Dave Mack (csu@alembic.acs.com) + * Peter Jannesen, Network Communication Systems + * (peter@ncs.nl) + * + * marc@suse.de : a small security fix for a buffer overflow + * + * [... History snipped ...] + * + */ +#include +#include +#include + +/* Default input buffer size */ +#define IBUFSIZ 2048 + +/* Default output buffer size */ +#define OBUFSIZ 2048 + +/* Defines for third byte of header */ +#define MAGIC_1 (char_type)'\037' /* First byte of compressed file */ +#define MAGIC_2 (char_type)'\235' /* Second byte of compressed file */ +#define BIT_MASK 0x1f /* Mask for 'number of compresssion bits' */ + /* Masks 0x20 and 0x40 are free. */ + /* I think 0x20 should mean that there is */ + /* a fourth header byte (for expansion). */ +#define BLOCK_MODE 0x80 /* Block compresssion if table is full and */ + /* compression rate is dropping flush tables */ + /* the next two codes should not be changed lightly, as they must not */ + /* lie within the contiguous general code space. */ +#define FIRST 257 /* first free entry */ +#define CLEAR 256 /* table clear output code */ + +#define INIT_BITS 9 /* initial number of bits/code */ + + +/* machine variants which require cc -Dmachine: pdp11, z8000, DOS */ +#define FAST + +#define HBITS 17 /* 50% occupancy */ +#define HSIZE (1< BITS) { + bb_error_msg("compressed with %d bits, can only handle %d bits", maxbits, + BITS); + return -1; + } + + maxcode = MAXCODE(n_bits = INIT_BITS) - 1; + bitmask = (1 << n_bits) - 1; + oldcode = -1; + finchar = 0; + outpos = 0; + posbits = 0 << 3; + + free_ent = ((block_mode) ? FIRST : 256); + + /* As above, initialize the first 256 entries in the table. */ + clear_tab_prefixof(); + + for (code = 255; code >= 0; --code) { + tab_suffixof(code) = (unsigned char) code; + } + + do { + resetbuf:; + { + int i; + int e; + int o; + + e = insize - (o = (posbits >> 3)); + + for (i = 0; i < e; ++i) + inbuf[i] = inbuf[i + o]; + + insize = e; + posbits = 0; + } + + if (insize < (int) sizeof(inbuf) - IBUFSIZ) { + rsize = safe_read(fd_in, inbuf + insize, IBUFSIZ); + insize += rsize; + } + + inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 : + (insize << 3) - (n_bits - 1)); + + while (inbits > posbits) { + if (free_ent > maxcode) { + posbits = + ((posbits - 1) + + ((n_bits << 3) - + (posbits - 1 + (n_bits << 3)) % (n_bits << 3))); + ++n_bits; + if (n_bits == maxbits) { + maxcode = maxmaxcode; + } else { + maxcode = MAXCODE(n_bits) - 1; + } + bitmask = (1 << n_bits) - 1; + goto resetbuf; + } + { + unsigned char *p = &inbuf[posbits >> 3]; + + code = + ((((long) (p[0])) | ((long) (p[1]) << 8) | + ((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask; + } + posbits += n_bits; + + + if (oldcode == -1) { + outbuf[outpos++] = (unsigned char) (finchar = + (int) (oldcode = code)); + continue; + } + + if (code == CLEAR && block_mode) { + clear_tab_prefixof(); + free_ent = FIRST - 1; + posbits = + ((posbits - 1) + + ((n_bits << 3) - + (posbits - 1 + (n_bits << 3)) % (n_bits << 3))); + maxcode = MAXCODE(n_bits = INIT_BITS) - 1; + bitmask = (1 << n_bits) - 1; + goto resetbuf; + } + + incode = code; + stackp = de_stack; + + /* Special case for KwKwK string. */ + if (code >= free_ent) { + if (code > free_ent) { + unsigned char *p; + + posbits -= n_bits; + p = &inbuf[posbits >> 3]; + + bb_error_msg + ("insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)", + insize, posbits, p[-1], p[0], p[1], p[2], p[3], + (posbits & 07)); + bb_error_msg("uncompress: corrupt input"); + return -1; + } + + *--stackp = (unsigned char) finchar; + code = oldcode; + } + + /* Generate output characters in reverse order */ + while ((long int) code >= (long int) 256) { + *--stackp = tab_suffixof(code); + code = tab_prefixof(code); + } + + *--stackp = (unsigned char) (finchar = tab_suffixof(code)); + + /* And put them out in forward order */ + { + int i; + + if (outpos + (i = (de_stack - stackp)) >= OBUFSIZ) { + do { + if (i > OBUFSIZ - outpos) { + i = OBUFSIZ - outpos; + } + + if (i > 0) { + memcpy(outbuf + outpos, stackp, i); + outpos += i; + } + + if (outpos >= OBUFSIZ) { + write(fd_out, outbuf, outpos); + outpos = 0; + } + stackp += i; + } while ((i = (de_stack - stackp)) > 0); + } else { + memcpy(outbuf + outpos, stackp, i); + outpos += i; + } + } + + /* Generate the new entry. */ + if ((code = free_ent) < maxmaxcode) { + tab_prefixof(code) = (unsigned short) oldcode; + tab_suffixof(code) = (unsigned char) finchar; + free_ent = code + 1; + } + + /* Remember previous code. */ + oldcode = incode; + } + + } while (rsize > 0); + + if (outpos > 0) { + write(fd_out, outbuf, outpos); + } + + return 0; +} diff --git a/busybox/archival/libunarchive/decompress_unzip.c b/busybox/archival/libunarchive/decompress_unzip.c new file mode 100644 index 000000000..e8cf54bff --- /dev/null +++ b/busybox/archival/libunarchive/decompress_unzip.c @@ -0,0 +1,982 @@ +/* vi: set sw=4 ts=4: */ +/* + * gunzip implementation for busybox + * + * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly. + * + * Originally adjusted for busybox by Sven Rudolph + * 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 + * + * read_gz interface + associated hacking by Laurence Anderson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the license_msg below and the file COPYING for the software license. + * See the file algorithm.doc for the compression algorithms and file formats. + */ + +#if 0 +static char *license_msg[] = { + " Copyright (C) 1992-1993 Jean-loup Gailly", + " This program is free software; you can redistribute it and/or modify", + " it under the terms of the GNU General Public License as published by", + " the Free Software Foundation; either version 2, or (at your option)", + " any later version.", + "", + " This program is distributed in the hope that it will be useful,", + " but WITHOUT ANY WARRANTY; without even the implied warranty of", + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", + " GNU General Public License for more details.", + "", + " You should have received a copy of the GNU General Public License", + " along with this program; if not, write to the Free Software", + " Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.", + 0 +}; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "busybox.h" +#include "unarchive.h" + +typedef struct huft_s { + unsigned char e; /* number of extra bits or operation */ + unsigned char b; /* number of bits in this code or subcode */ + union { + unsigned short n; /* literal, length base, or distance base */ + struct huft_s *t; /* pointer to next level of table */ + } v; +} huft_t; + +static int gunzip_src_fd; +unsigned int gunzip_bytes_out; /* number of output bytes */ +static unsigned int gunzip_outbuf_count; /* bytes in output buffer */ + +/* gunzip_window size--must be a power of two, and + * at least 32K for zip's deflate method */ +static const int gunzip_wsize = 0x8000; +static unsigned char *gunzip_window; + +static unsigned int *gunzip_crc_table; +unsigned int gunzip_crc; + +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ +#define BMAX 16 /* maximum bit length of any code (16 for explode) */ +#define N_MAX 288 /* maximum number of codes in any set */ + +/* bitbuffer */ +static unsigned int gunzip_bb; /* bit buffer */ +static unsigned char gunzip_bk; /* bits in bit buffer */ + +/* These control the size of the bytebuffer */ +static unsigned int bytebuffer_max = 0x8000; +static unsigned char *bytebuffer = NULL; +static unsigned int bytebuffer_offset = 0; +static unsigned int bytebuffer_size = 0; + +static const unsigned short mask_bits[] = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +/* Copy lengths for literal codes 257..285 */ +static const unsigned short cplens[] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +}; + +/* note: see note #13 above about the 258 in this list. */ +/* Extra bits for literal codes 257..285 */ +static const unsigned char cplext[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 5, 0, 99, 99 +}; /* 99==invalid */ + +/* Copy offsets for distance codes 0..29 */ +static const unsigned short cpdist[] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 +}; + +/* Extra bits for distance codes */ +static const unsigned char cpdext[] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; + +/* Tables for deflate from PKZIP's appnote.txt. */ +/* Order of the bit length code lengths */ +static const unsigned char border[] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current, const unsigned int required) +{ + while (*current < required) { + if (bytebuffer_offset >= bytebuffer_size) { + /* Leave the first 4 bytes empty so we can always unwind the bitbuffer + * to the front of the bytebuffer, leave 4 bytes free at end of tail + * so we can easily top up buffer in check_trailer_gzip() */ + bytebuffer_size = 4 + bb_xread(gunzip_src_fd, &bytebuffer[4], bytebuffer_max - 8); + bytebuffer_offset = 4; + } + bitbuffer |= ((unsigned int) bytebuffer[bytebuffer_offset]) << *current; + bytebuffer_offset++; + *current += 8; + } + return(bitbuffer); +} + +static void make_gunzip_crc_table(void) +{ + const unsigned int poly = 0xedb88320; /* polynomial exclusive-or pattern */ + unsigned short i; /* counter for all possible eight bit values */ + + /* initial shift register value */ + gunzip_crc = 0xffffffffL; + gunzip_crc_table = (unsigned int *) malloc(256 * sizeof(unsigned int)); + + /* Compute and print table of CRC's, five per line */ + for (i = 0; i < 256; i++) { + unsigned int table_entry; /* crc shift register */ + unsigned char k; /* byte being shifted into crc apparatus */ + + table_entry = i; + /* The idea to initialize the register with the byte instead of + * zero was stolen from Haruhiko Okumura's ar002 + */ + for (k = 8; k; k--) { + if (table_entry & 1) { + table_entry = (table_entry >> 1) ^ poly; + } else { + table_entry >>= 1; + } + } + gunzip_crc_table[i] = table_entry; + } +} + +/* + * Free the malloc'ed tables built by huft_build(), which makes a linked + * list of the tables it made, with the links in a dummy first entry of + * each table. + * t: table to free + */ +static int huft_free(huft_t * t) +{ + huft_t *p; + huft_t *q; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != (huft_t *) NULL) { + q = (--p)->v.t; + free((char *) p); + p = q; + } + return 0; +} + +/* Given a list of code lengths and a maximum table size, make a set of + * tables to decode that set of codes. Return zero on success, one if + * the given code set is incomplete (the tables are still built in this + * case), two if the input is invalid (all zero length codes or an + * oversubscribed set of lengths), and three if not enough memory. + * + * b: code lengths in bits (all assumed <= BMAX) + * n: number of codes (assumed <= N_MAX) + * s: number of simple-valued codes (0..s-1) + * d: list of base values for non-simple codes + * e: list of extra bits for non-simple codes + * t: result: starting table + * m: maximum lookup bits, returns actual + */ +static int huft_build(unsigned int *b, const unsigned int n, + const unsigned int s, const unsigned short *d, + const unsigned char *e, huft_t ** t, int *m) +{ + unsigned a; /* counter for codes of length k */ + unsigned c[BMAX + 1]; /* bit length count table */ + unsigned f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register unsigned i; /* counter, current code */ + register unsigned j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register unsigned *p; /* pointer into c[], b[], or v[] */ + register huft_t *q; /* points to current table */ + huft_t r; /* table entry for structure assignment */ + huft_t *u[BMAX]; /* table stack */ + unsigned v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + unsigned x[BMAX + 1]; /* bit offsets, then code stack */ + unsigned *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + unsigned z; /* number of entries in current table */ + + /* Generate counts for each bit length */ + memset((void *) (c), 0, sizeof(c)); + p = b; + i = n; + do { + c[*p]++; /* assume all entries <= BMAX */ + p++; /* Can't combine with above line (Solaris bug) */ + } while (--i); + if (c[0] == n) { /* null input--all zero length codes */ + *t = (huft_t *) NULL; + *m = 0; + return 0; + } + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) { + if (c[j]) { + break; + } + } + k = j; /* minimum code length */ + if ((unsigned) l < j) { + l = j; + } + for (i = BMAX; i; i--) { + if (c[i]) { + break; + } + } + g = i; /* maximum code length */ + if ((unsigned) l > i) { + l = i; + } + *m = l; + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) { + if ((y -= c[j]) < 0) { + return 2; /* bad input: more codes than bits */ + } + } + if ((y -= c[i]) < 0) { + return 2; + } + c[i] += y; + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; + xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + /* Make a table of values in order of bit lengths */ + p = b; + i = 0; + do { + if ((j = *p++) != 0) { + v[x[j]++] = i; + } + } while (++i < n); + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (huft_t *) NULL; /* just to keep compilers happy */ + q = (huft_t *) NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) { + a = c[k]; + while (a--) { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (unsigned) l ? l : z; /* upper limit on table size */ + if ((f = 1 << (j = k - w)) > a + 1) { /* try a k-w bit table *//* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + while (++j < z) { /* try smaller tables up to z bits */ + if ((f <<= 1) <= *++xp) { + break; /* enough codes to use up j bits */ + } + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t)); + + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->v.t)) = NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) { + x[h] = i; /* save pattern for backing up */ + r.b = (unsigned char) l; /* bits to dump before this table */ + r.e = (unsigned char) (16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h - 1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.b = (unsigned char) (k - w); + if (p >= v + n) { + r.e = 99; /* out of values--invalid code */ + } else if (*p < s) { + r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is end-of-block code */ + r.v.n = (unsigned short) (*p); /* simple code is just the value */ + p++; /* one compiler does not like *p++ */ + } else { + r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) { + q[j] = r; + } + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) { + i ^= j; + } + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) { + h--; /* don't need to update q */ + w -= l; + } + } + } + /* Return true (1) if we were given an incomplete table */ + return y != 0 && g != 1; +} + +/* + * inflate (decompress) the codes in a deflated (compressed) block. + * Return an error code or zero if it all goes ok. + * + * tl, td: literal/length and distance decoder tables + * bl, bd: number of bits decoded by tl[] and td[] + */ +static int inflate_codes(huft_t * my_tl, huft_t * my_td, const unsigned int my_bl, const unsigned int my_bd, int setup) +{ + static unsigned int e; /* table entry flag/number of extra bits */ + static unsigned int n, d; /* length and index for copy */ + static unsigned int w; /* current gunzip_window position */ + static huft_t *t; /* pointer to table entry */ + static unsigned int ml, md; /* masks for bl and bd bits */ + static unsigned int b; /* bit buffer */ + static unsigned int k; /* number of bits in bit buffer */ + static huft_t *tl, *td; + static unsigned int bl, bd; + static int resumeCopy = 0; + + if (setup) { // 1st time we are called, copy in variables + tl = my_tl; + td = my_td; + bl = my_bl; + bd = my_bd; + /* make local copies of globals */ + b = gunzip_bb; /* initialize bit buffer */ + k = gunzip_bk; + w = gunzip_outbuf_count; /* initialize gunzip_window position */ + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + return 0; // Don't actually do anything the first time + } + + if (resumeCopy) goto do_copy; + + while (1) { /* do until end of block */ + b = fill_bitbuffer(b, &k, bl); + if ((e = (t = tl + ((unsigned) b & ml))->e) > 16) + do { + if (e == 99) { + bb_error_msg_and_die("inflate_codes error 1");; + } + b >>= t->b; + k -= t->b; + e -= 16; + b = fill_bitbuffer(b, &k, e); + } while ((e = + (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); + b >>= t->b; + k -= t->b; + if (e == 16) { /* then it's a literal */ + gunzip_window[w++] = (unsigned char) t->v.n; + if (w == gunzip_wsize) { + gunzip_outbuf_count = (w); + //flush_gunzip_window(); + w = 0; + return 1; // We have a block to read + } + } else { /* it's an EOB or a length */ + + /* exit if end of block */ + if (e == 15) { + break; + } + + /* get length of block to copy */ + b = fill_bitbuffer(b, &k, e); + n = t->v.n + ((unsigned) b & mask_bits[e]); + b >>= e; + k -= e; + + /* decode distance of block to copy */ + b = fill_bitbuffer(b, &k, bd); + if ((e = (t = td + ((unsigned) b & md))->e) > 16) + do { + if (e == 99) + bb_error_msg_and_die("inflate_codes error 2");; + b >>= t->b; + k -= t->b; + e -= 16; + b = fill_bitbuffer(b, &k, e); + } while ((e = + (t = + t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); + b >>= t->b; + k -= t->b; + b = fill_bitbuffer(b, &k, e); + d = w - t->v.n - ((unsigned) b & mask_bits[e]); + b >>= e; + k -= e; + + /* do the copy */ +do_copy: do { + n -= (e = + (e = + gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e); + /* copy to new buffer to prevent possible overwrite */ + if (w - d >= e) { /* (this test assumes unsigned comparison) */ + memcpy(gunzip_window + w, gunzip_window + d, e); + w += e; + d += e; + } else { + /* do it slow to avoid memcpy() overlap */ + /* !NOMEMCPY */ + do { + gunzip_window[w++] = gunzip_window[d++]; + } while (--e); + } + if (w == gunzip_wsize) { + gunzip_outbuf_count = (w); + if (n) resumeCopy = 1; + else resumeCopy = 0; + //flush_gunzip_window(); + w = 0; + return 1; + } + } while (n); + resumeCopy = 0; + } + } + + /* restore the globals from the locals */ + gunzip_outbuf_count = w; /* restore global gunzip_window pointer */ + gunzip_bb = b; /* restore global bit buffer */ + gunzip_bk = k; + + /* normally just after call to inflate_codes, but save code by putting it here */ + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + + /* done */ + return 0; +} + +static int inflate_stored(int my_n, int my_b_stored, int my_k_stored, int setup) +{ + static int n, b_stored, k_stored, w; + if (setup) { + n = my_n; + b_stored = my_b_stored; + k_stored = my_k_stored; + w = gunzip_outbuf_count; /* initialize gunzip_window position */ + return 0; // Don't do anything first time + } + + /* read and output the compressed data */ + while (n--) { + b_stored = fill_bitbuffer(b_stored, &k_stored, 8); + gunzip_window[w++] = (unsigned char) b_stored; + if (w == (unsigned int) gunzip_wsize) { + gunzip_outbuf_count = (w); + //flush_gunzip_window(); + w = 0; + b_stored >>= 8; + k_stored -= 8; + return 1; // We have a block + } + b_stored >>= 8; + k_stored -= 8; + } + + /* restore the globals from the locals */ + gunzip_outbuf_count = w; /* restore global gunzip_window pointer */ + gunzip_bb = b_stored; /* restore global bit buffer */ + gunzip_bk = k_stored; + return 0; // Finished +} + +/* + * decompress an inflated block + * e: last block flag + * + * GLOBAL VARIABLES: bb, kk, + */ + // Return values: -1 = inflate_stored, -2 = inflate_codes +static int inflate_block(int *e) +{ + unsigned t; /* block type */ + register unsigned int b; /* bit buffer */ + unsigned int k; /* number of bits in bit buffer */ + + /* make local bit buffer */ + + b = gunzip_bb; + k = gunzip_bk; + + /* read in last block bit */ + b = fill_bitbuffer(b, &k, 1); + *e = (int) b & 1; + b >>= 1; + k -= 1; + + /* read in block type */ + b = fill_bitbuffer(b, &k, 2); + t = (unsigned) b & 3; + b >>= 2; + k -= 2; + + /* restore the global bit buffer */ + gunzip_bb = b; + gunzip_bk = k; + + /* inflate that block type */ + switch (t) { + case 0: /* Inflate stored */ + { + unsigned int n; /* number of bytes in block */ + unsigned int b_stored; /* bit buffer */ + unsigned int k_stored; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b_stored = gunzip_bb; /* initialize bit buffer */ + k_stored = gunzip_bk; + + /* go to byte boundary */ + n = k_stored & 7; + b_stored >>= n; + k_stored -= n; + + /* get the length and its complement */ + b_stored = fill_bitbuffer(b_stored, &k_stored, 16); + n = ((unsigned) b_stored & 0xffff); + b_stored >>= 16; + k_stored -= 16; + + b_stored = fill_bitbuffer(b_stored, &k_stored, 16); + if (n != (unsigned) ((~b_stored) & 0xffff)) { + return 1; /* error in compressed data */ + } + b_stored >>= 16; + k_stored -= 16; + + inflate_stored(n, b_stored, k_stored, 1); // Setup inflate_stored + return -1; + } + case 1: /* Inflate fixed + * decompress an inflated type 1 (fixed Huffman codes) block. We should + * either replace this with a custom decoder, or at least precompute the + * Huffman tables. + */ + { + int i; /* temporary variable */ + huft_t *tl; /* literal/length code table */ + huft_t *td; /* distance code table */ + unsigned int bl; /* lookup bits for tl */ + unsigned int bd; /* lookup bits for td */ + unsigned int l[288]; /* length list for huft_build */ + + /* set up literal table */ + for (i = 0; i < 144; i++) { + l[i] = 8; + } + for (; i < 256; i++) { + l[i] = 9; + } + for (; i < 280; i++) { + l[i] = 7; + } + for (; i < 288; i++) { /* make a complete, but wrong code set */ + l[i] = 8; + } + bl = 7; + if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0) { + return i; + } + + /* set up distance table */ + for (i = 0; i < 30; i++) { /* make an incomplete code set */ + l[i] = 5; + } + bd = 5; + if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1) { + huft_free(tl); + return i; + } + + /* decompress until an end-of-block code */ + inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes + + /* huft_free code moved into inflate_codes */ + + return -2; + } + case 2: /* Inflate dynamic */ + { + const int dbits = 6; /* bits in base distance lookup table */ + const int lbits = 9; /* bits in base literal/length lookup table */ + + huft_t *tl; /* literal/length code table */ + huft_t *td; /* distance code table */ + unsigned int i; /* temporary variables */ + unsigned int j; + unsigned int l; /* last length */ + unsigned int m; /* mask for bit lengths table */ + unsigned int n; /* number of lengths to get */ + unsigned int bl; /* lookup bits for tl */ + unsigned int bd; /* lookup bits for td */ + unsigned int nb; /* number of bit length codes */ + unsigned int nl; /* number of literal/length codes */ + unsigned int nd; /* number of distance codes */ + + unsigned int ll[286 + 30]; /* literal/length and distance code lengths */ + unsigned int b_dynamic; /* bit buffer */ + unsigned int k_dynamic; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b_dynamic = gunzip_bb; + k_dynamic = gunzip_bk; + + /* read in table lengths */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5); + nl = 257 + ((unsigned int) b_dynamic & 0x1f); /* number of literal/length codes */ + + b_dynamic >>= 5; + k_dynamic -= 5; + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5); + nd = 1 + ((unsigned int) b_dynamic & 0x1f); /* number of distance codes */ + + b_dynamic >>= 5; + k_dynamic -= 5; + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4); + nb = 4 + ((unsigned int) b_dynamic & 0xf); /* number of bit length codes */ + + b_dynamic >>= 4; + k_dynamic -= 4; + if (nl > 286 || nd > 30) { + return 1; /* bad lengths */ + } + + /* read in bit-length-code lengths */ + for (j = 0; j < nb; j++) { + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3); + ll[border[j]] = (unsigned int) b_dynamic & 7; + b_dynamic >>= 3; + k_dynamic -= 3; + } + for (; j < 19; j++) { + ll[border[j]] = 0; + } + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl); + if (i != 0) { + if (i == 1) { + huft_free(tl); + } + return i; /* incomplete code set */ + } + + /* read in literal and distance code lengths */ + n = nl + nd; + m = mask_bits[bl]; + i = l = 0; + while ((unsigned int) i < n) { + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl); + j = (td = tl + ((unsigned int) b_dynamic & m))->b; + b_dynamic >>= j; + k_dynamic -= j; + j = td->v.n; + if (j < 16) { /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + } else if (j == 16) { /* repeat last length 3 to 6 times */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2); + j = 3 + ((unsigned int) b_dynamic & 3); + b_dynamic >>= 2; + k_dynamic -= 2; + if ((unsigned int) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = l; + } + } else if (j == 17) { /* 3 to 10 zero length codes */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3); + j = 3 + ((unsigned int) b_dynamic & 7); + b_dynamic >>= 3; + k_dynamic -= 3; + if ((unsigned int) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } else { /* j == 18: 11 to 138 zero length codes */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7); + j = 11 + ((unsigned int) b_dynamic & 0x7f); + b_dynamic >>= 7; + k_dynamic -= 7; + if ((unsigned int) i + j > n) { + return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } + } + + /* free decoding table for trees */ + huft_free(tl); + + /* restore the global bit buffer */ + gunzip_bb = b_dynamic; + gunzip_bk = k_dynamic; + + /* build the decoding tables for literal/length and distance codes */ + bl = lbits; + + if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) { + if (i == 1) { + bb_error_msg_and_die("Incomplete literal tree"); + huft_free(tl); + } + return i; /* incomplete code set */ + } + + bd = dbits; + if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) { + if (i == 1) { + bb_error_msg_and_die("incomplete distance tree"); + huft_free(td); + } + huft_free(tl); + return i; /* incomplete code set */ + } + + /* decompress until an end-of-block code */ + inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes + + /* huft_free code moved into inflate_codes */ + + return -2; + } + default: + /* bad block type */ + bb_error_msg_and_die("bad block type %d\n", t); + } +} + +static void calculate_gunzip_crc(void) +{ + int n; + for (n = 0; n < gunzip_outbuf_count; n++) { + gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8); + } + gunzip_bytes_out += gunzip_outbuf_count; +} + +static int inflate_get_next_window(void) +{ + static int method = -1; // Method == -1 for stored, -2 for codes + static int e = 0; + static int needAnotherBlock = 1; + + gunzip_outbuf_count = 0; + + while(1) { + int ret; + + if (needAnotherBlock) { + if(e) { + calculate_gunzip_crc(); + e = 0; + needAnotherBlock = 1; + return 0; + } // Last block + method = inflate_block(&e); + needAnotherBlock = 0; + } + + switch (method) { + case -1: ret = inflate_stored(0,0,0,0); + break; + case -2: ret = inflate_codes(0,0,0,0,0); + break; + default: bb_error_msg_and_die("inflate error %d", method); + } + + if (ret == 1) { + calculate_gunzip_crc(); + return 1; // More data left + } else needAnotherBlock = 1; // End of that block + } + /* Doesnt get here */ +} + +/* Initialise bytebuffer, be careful not to overfill the buffer */ +extern void inflate_init(unsigned int bufsize) +{ + /* Set the bytebuffer size, default is same as gunzip_wsize */ + bytebuffer_max = bufsize + 8; + bytebuffer_offset = 4; + bytebuffer_size = 0; +} + +extern int inflate_unzip(int in, int out) +{ + ssize_t nwrote; + typedef void (*sig_type) (int); + + /* Allocate all global buffers (for DYN_ALLOC option) */ + gunzip_window = xmalloc(gunzip_wsize); + gunzip_outbuf_count = 0; + gunzip_bytes_out = 0; + gunzip_src_fd = in; + + /* initialize gunzip_window, bit buffer */ + gunzip_bk = 0; + gunzip_bb = 0; + + /* Create the crc table */ + make_gunzip_crc_table(); + + /* Allocate space for buffer */ + bytebuffer = xmalloc(bytebuffer_max); + + while(1) { + int ret = inflate_get_next_window(); + nwrote = bb_full_write(out, gunzip_window, gunzip_outbuf_count); + if (nwrote == -1) { + bb_perror_msg("write"); + return -1; + } + if (ret == 0) break; + } + + /* Cleanup */ + free(gunzip_window); + free(gunzip_crc_table); + + /* Store unused bytes in a global buffer so calling applets can access it */ + if (gunzip_bk >= 8) { + /* Undo too much lookahead. The next read will be byte aligned + * so we can discard unused bits in the last meaningful byte. */ + bytebuffer_offset--; + bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff; + gunzip_bb >>= 8; + gunzip_bk -= 8; + } + return 0; +} + +extern int inflate_gunzip(int in, int out) +{ + unsigned int stored_crc = 0; + unsigned char count; + + inflate_unzip(in, out); + + /* top up the input buffer with the rest of the trailer */ + count = bytebuffer_size - bytebuffer_offset; + if (count < 8) { + bb_xread_all(in, &bytebuffer[bytebuffer_size], 8 - count); + bytebuffer_size += 8 - count; + } + for (count = 0; count != 4; count++) { + stored_crc |= (bytebuffer[bytebuffer_offset] << (count * 8)); + bytebuffer_offset++; + } + + /* Validate decompression - crc */ + if (stored_crc != (gunzip_crc ^ 0xffffffffL)) { + bb_error_msg("crc error"); + } + + /* Validate decompression - size */ + if (gunzip_bytes_out != + (bytebuffer[bytebuffer_offset] | (bytebuffer[bytebuffer_offset+1] << 8) | + (bytebuffer[bytebuffer_offset+2] << 16) | (bytebuffer[bytebuffer_offset+3] << 24))) { + bb_error_msg("Incorrect length"); + } + + return 0; +} diff --git a/busybox/archival/libunarchive/filter_accept_all.c b/busybox/archival/libunarchive/filter_accept_all.c new file mode 100644 index 000000000..baf9e4b71 --- /dev/null +++ b/busybox/archival/libunarchive/filter_accept_all.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "unarchive.h" + +/* Accept any non-null name, its not really a filter at all */ +extern char filter_accept_all(archive_handle_t *archive_handle) +{ + if (archive_handle->file_header->name) { + return(EXIT_SUCCESS); + } else { + return(EXIT_FAILURE); + } +} diff --git a/busybox/archival/libunarchive/filter_accept_list.c b/busybox/archival/libunarchive/filter_accept_list.c new file mode 100644 index 000000000..e1c4827bf --- /dev/null +++ b/busybox/archival/libunarchive/filter_accept_list.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "unarchive.h" + +/* + * Accept names that are in the accept list, ignoring reject list. + */ +extern char filter_accept_list(archive_handle_t *archive_handle) +{ + if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) { + return(EXIT_SUCCESS); + } else { + return(EXIT_FAILURE); + } +} diff --git a/busybox/archival/libunarchive/filter_accept_list_reassign.c b/busybox/archival/libunarchive/filter_accept_list_reassign.c new file mode 100644 index 000000000..d0436549b --- /dev/null +++ b/busybox/archival/libunarchive/filter_accept_list_reassign.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "libbb.h" +#include "unarchive.h" + +/* + * Reassign the subarchive metadata parser based on the filename extension + * e.g. if its a .tar.gz modify archive_handle->sub_archive to process a .tar.gz + * or if its a .tar.bz2 make archive_handle->sub_archive handle that + */ +extern char filter_accept_list_reassign(archive_handle_t *archive_handle) +{ + /* Check the file entry is in the accept list */ + if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) { + const char *name_ptr; + + /* Extract the last 2 extensions */ + name_ptr = strrchr(archive_handle->file_header->name, '.'); + + /* Modify the subarchive handler based on the extension */ +#ifdef CONFIG_FEATURE_DEB_TAR_GZ + if (strcmp(name_ptr, ".gz") == 0) { + archive_handle->action_data_subarchive = get_header_tar_gz; + return(EXIT_SUCCESS); + } +#endif +#ifdef CONFIG_FEATURE_DEB_TAR_BZ2 + if (strcmp(name_ptr, ".bz2") == 0) { + archive_handle->action_data_subarchive = get_header_tar_bz2; + return(EXIT_SUCCESS); + } +#endif + } + return(EXIT_FAILURE); +} diff --git a/busybox/archival/libunarchive/filter_accept_reject_list.c b/busybox/archival/libunarchive/filter_accept_reject_list.c new file mode 100644 index 000000000..657f7a0bd --- /dev/null +++ b/busybox/archival/libunarchive/filter_accept_reject_list.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "unarchive.h" + +/* + * Accept names that are in the accept list and not in the reject list + */ +extern char filter_accept_reject_list(archive_handle_t *archive_handle) +{ + const char *key = archive_handle->file_header->name; + const llist_t *accept_entry = find_list_entry(archive_handle->accept, key); + const llist_t *reject_entry = find_list_entry(archive_handle->reject, key); + + /* If the key is in a reject list fail */ + if (reject_entry) { + return(EXIT_FAILURE); + } + + /* Fail if an accept list was specified and the key wasnt in there */ + if (archive_handle->accept && (accept_entry == NULL)) { + return(EXIT_FAILURE); + } + + /* Accepted */ + return(EXIT_SUCCESS); +} diff --git a/busybox/archival/libunarchive/find_list_entry.c b/busybox/archival/libunarchive/find_list_entry.c new file mode 100644 index 000000000..7ed9e332f --- /dev/null +++ b/busybox/archival/libunarchive/find_list_entry.c @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "unarchive.h" + +extern const llist_t *find_list_entry(const llist_t *list, const char *filename) +{ + while (list) { + if (fnmatch(list->data, filename, 0) == 0) { + return(list); + } + list = list->link; + } + return(NULL); +} diff --git a/busybox/archival/libunarchive/get_header_ar.c b/busybox/archival/libunarchive/get_header_ar.c new file mode 100644 index 000000000..ebb6f8cbe --- /dev/null +++ b/busybox/archival/libunarchive/get_header_ar.c @@ -0,0 +1,126 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include "unarchive.h" +#include "libbb.h" + +extern char get_header_ar(archive_handle_t *archive_handle) +{ + file_header_t *typed = archive_handle->file_header; + union { + char raw[60]; + struct { + char name[16]; + char date[12]; + char uid[6]; + char gid[6]; + char mode[8]; + char size[10]; + char magic[2]; + } formated; + } ar; +#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES + static char *ar_long_names; + static unsigned int ar_long_name_size; +#endif + + /* dont use bb_xread as we want to handle the error ourself */ + if (read(archive_handle->src_fd, ar.raw, 60) != 60) { + /* End Of File */ + return(EXIT_FAILURE); + } + + /* ar header starts on an even byte (2 byte aligned) + * '\n' is used for padding + */ + if (ar.raw[0] == '\n') { + /* fix up the header, we started reading 1 byte too early */ + memmove(ar.raw, &ar.raw[1], 59); + ar.raw[59] = bb_xread_char(archive_handle->src_fd); + archive_handle->offset++; + } + archive_handle->offset += 60; + + /* align the headers based on the header magic */ + if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) { + bb_error_msg_and_die("Invalid ar header"); + } + + typed->mode = strtol(ar.formated.mode, NULL, 8); + typed->mtime = atoi(ar.formated.date); + typed->uid = atoi(ar.formated.uid); + typed->gid = atoi(ar.formated.gid); + typed->size = atoi(ar.formated.size); + + /* long filenames have '/' as the first character */ + if (ar.formated.name[0] == '/') { +#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES + if (ar.formated.name[1] == '/') { + /* If the second char is a '/' then this entries data section + * stores long filename for multiple entries, they are stored + * in static variable long_names for use in future entries */ + ar_long_name_size = typed->size; + ar_long_names = xmalloc(ar_long_name_size); + bb_xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size); + archive_handle->offset += ar_long_name_size; + /* This ar entries data section only contained filenames for other records + * they are stored in the static ar_long_names for future reference */ + return (get_header_ar(archive_handle)); /* Return next header */ + } else if (ar.formated.name[1] == ' ') { + /* This is the index of symbols in the file for compilers */ + data_skip(archive_handle); + archive_handle->offset += typed->size; + return (get_header_ar(archive_handle)); /* Return next header */ + } else { + /* The number after the '/' indicates the offset in the ar data section + (saved in variable long_name) that conatains the real filename */ + const unsigned int long_offset = atoi(&ar.formated.name[1]); + if (long_offset >= ar_long_name_size) { + bb_error_msg_and_die("Cant resolve long filename"); + } + typed->name = bb_xstrdup(ar_long_names + long_offset); + } +#else + bb_error_msg_and_die("long filenames not supported"); +#endif + } else { + /* short filenames */ + typed->name = bb_xstrndup(ar.formated.name, 16); + } + + typed->name[strcspn(typed->name, " /")] = '\0'; + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_header(typed); + if (archive_handle->sub_archive) { + while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS); + } else { + archive_handle->action_data(archive_handle); + } + } else { + data_skip(archive_handle); + } + + archive_handle->offset += typed->size; + /* Set the file pointer to the correct spot, we may have been reading a compressed file */ + lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET); + + return(EXIT_SUCCESS); +} diff --git a/busybox/archival/libunarchive/get_header_cpio.c b/busybox/archival/libunarchive/get_header_cpio.c new file mode 100644 index 000000000..11925c4e3 --- /dev/null +++ b/busybox/archival/libunarchive/get_header_cpio.c @@ -0,0 +1,161 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include /* major() and minor() */ +#include "unarchive.h" +#include "libbb.h" + +typedef struct hardlinks_s { + file_header_t *entry; + int inode; + struct hardlinks_s *next; +} hardlinks_t; + +extern char get_header_cpio(archive_handle_t *archive_handle) +{ + static hardlinks_t *saved_hardlinks = NULL; + static unsigned short pending_hardlinks = 0; + file_header_t *file_header = archive_handle->file_header; + char cpio_header[110]; + int namesize; + char dummy[16]; + int major, minor, nlink, inode; + + if (pending_hardlinks) { /* Deal with any pending hardlinks */ + hardlinks_t *tmp; + hardlinks_t *oldtmp; + + tmp = saved_hardlinks; + oldtmp = NULL; + + while (tmp) { + bb_error_msg_and_die("need to fix this\n"); + if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */ + file_header = tmp->entry; + if (oldtmp) { + oldtmp->next = tmp->next; /* Remove item from linked list */ + } else { + saved_hardlinks = tmp->next; + } + free(tmp); + continue; + } + oldtmp = tmp; + tmp = tmp->next; + } + pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */ + } + + /* There can be padding before archive header */ + data_align(archive_handle, 4); + + if (archive_xread_all_eof(archive_handle, cpio_header, 110) == 0) { + return(EXIT_FAILURE); + } + archive_handle->offset += 110; + + if ((strncmp(&cpio_header[0], "07070", 5) != 0) || ((cpio_header[5] != '1') && (cpio_header[5] != '2'))) { + bb_error_msg_and_die("Unsupported cpio format, use newc or crc"); + } + + { + unsigned long tmpsize; + sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c", + dummy, &inode, (unsigned int*)&file_header->mode, + (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid, + &nlink, &file_header->mtime, &tmpsize, + dummy, &major, &minor, &namesize, dummy); + file_header->size = tmpsize; + } + + file_header->name = (char *) xmalloc(namesize + 1); + archive_xread_all(archive_handle, file_header->name, namesize); /* Read in filename */ + file_header->name[namesize] = '\0'; + archive_handle->offset += namesize; + + /* Update offset amount and skip padding before file contents */ + data_align(archive_handle, 4); + + if (strcmp(file_header->name, "TRAILER!!!") == 0) { + printf("%d blocks\n", (int) (archive_handle->offset % 512 ? (archive_handle->offset / 512) + 1 : archive_handle->offset / 512)); /* Always round up */ + if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */ + hardlinks_t *tmp = saved_hardlinks; + hardlinks_t *oldtmp = NULL; + while (tmp) { + bb_error_msg("%s not created: cannot resolve hardlink", tmp->entry->name); + oldtmp = tmp; + tmp = tmp->next; + free (oldtmp->entry->name); + free (oldtmp->entry); + free (oldtmp); + } + saved_hardlinks = NULL; + pending_hardlinks = 0; + } + return(EXIT_FAILURE); + } + + if (S_ISLNK(file_header->mode)) { + file_header->link_name = (char *) xmalloc(file_header->size + 1); + archive_xread_all(archive_handle, file_header->link_name, file_header->size); + file_header->link_name[file_header->size] = '\0'; + archive_handle->offset += file_header->size; + file_header->size = 0; /* Stop possible seeks in future */ + } else { + file_header->link_name = NULL; + } + if (nlink > 1 && !S_ISDIR(file_header->mode)) { + if (file_header->size == 0) { /* Put file on a linked list for later */ + hardlinks_t *new = xmalloc(sizeof(hardlinks_t)); + new->next = saved_hardlinks; + new->inode = inode; + new->entry = file_header; + saved_hardlinks = new; + return(EXIT_SUCCESS); // Skip this one + } else { /* Found the file with data in */ + hardlinks_t *tmp = saved_hardlinks; + pending_hardlinks = 1; + while (tmp) { + if (tmp->inode == inode) { + tmp->entry->link_name = bb_xstrdup(file_header->name); + nlink--; + } + tmp = tmp->next; + } + if (nlink > 1) { + bb_error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?"); + } + } + } + file_header->device = makedev(major, minor); + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_data(archive_handle); + archive_handle->action_header(archive_handle->file_header); + } else { + data_skip(archive_handle); + } + + archive_handle->offset += file_header->size; + + free(file_header->link_name); + + return (EXIT_SUCCESS); +} diff --git a/busybox/archival/libunarchive/get_header_tar.c b/busybox/archival/libunarchive/get_header_tar.c new file mode 100644 index 000000000..1ad9ac5e5 --- /dev/null +++ b/busybox/archival/libunarchive/get_header_tar.c @@ -0,0 +1,215 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * FIXME: + * In privileged mode if uname and gname map to a uid and gid then use the + * mapped value instead of the uid/gid values in tar header + * + * References: + * GNU tar and star man pages, + * Opengroup's ustar interchange format, + * http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html + */ + +#include +#include +#include +#include "unarchive.h" +#include "libbb.h" + +#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS +static char *longname = NULL; +static char *linkname = NULL; +#endif + +extern char get_header_tar(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + union { + /* ustar header, Posix 1003.1 */ + unsigned char raw[512]; + struct { + char name[100]; /* 0-99 */ + char mode[8]; /* 100-107 */ + char uid[8]; /* 108-115 */ + char gid[8]; /* 116-123 */ + char size[12]; /* 124-135 */ + char mtime[12]; /* 136-147 */ + char chksum[8]; /* 148-155 */ + char typeflag; /* 156-156 */ + char linkname[100]; /* 157-256 */ + char magic[6]; /* 257-262 */ + char version[2]; /* 263-264 */ + char uname[32]; /* 265-296 */ + char gname[32]; /* 297-328 */ + char devmajor[8]; /* 329-336 */ + char devminor[8]; /* 337-344 */ + char prefix[155]; /* 345-499 */ + char padding[12]; /* 500-512 */ + } formated; + } tar; + long sum = 0; + long i; + + /* Align header */ + data_align(archive_handle, 512); + + if (bb_full_read(archive_handle->src_fd, tar.raw, 512) != 512) { + /* Assume end of file */ + return(EXIT_FAILURE); + } + archive_handle->offset += 512; + + /* If there is no filename its an empty header */ + if (tar.formated.name[0] == 0) { + return(EXIT_SUCCESS); + } + + /* Check header has valid magic, "ustar" is for the proper tar + * 0's are for the old tar format + */ + if (strncmp(tar.formated.magic, "ustar", 5) != 0) { +#ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY + if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) +#endif + bb_error_msg_and_die("Invalid tar magic"); + } + /* Do checksum on headers */ + for (i = 0; i < 148 ; i++) { + sum += tar.raw[i]; + } + sum += ' ' * 8; + for (i = 156; i < 512 ; i++) { + sum += tar.raw[i]; + } + if (sum != strtol(tar.formated.chksum, NULL, 8)) { + bb_error_msg("Invalid tar header checksum"); + return(EXIT_FAILURE); + } + +#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS + if (longname) { + file_header->name = longname; + longname = NULL; + } + else if (linkname) { + file_header->name = linkname; + linkname = NULL; + } else +#endif + if (tar.formated.prefix[0] == 0) { + file_header->name = strdup(tar.formated.name); + } else { + file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name); + } + + file_header->uid = strtol(tar.formated.uid, NULL, 8); + file_header->gid = strtol(tar.formated.gid, NULL, 8); + file_header->size = strtol(tar.formated.size, NULL, 8); + file_header->mtime = strtol(tar.formated.mtime, NULL, 8); + file_header->link_name = (tar.formated.linkname[0] != '\0') ? + bb_xstrdup(tar.formated.linkname) : NULL; + file_header->device = makedev(strtol(tar.formated.devmajor, NULL, 8), + strtol(tar.formated.devminor, NULL, 8)); + + /* Set bits 0-11 of the files mode */ + file_header->mode = 07777 & strtol(tar.formated.mode, NULL, 8); + + /* Set bits 12-15 of the files mode */ + switch (tar.formated.typeflag) { + /* busybox identifies hard links as being regular files with 0 size and a link name */ + case '1': + file_header->mode |= S_IFREG; + break; + case 'x': + case 'g': + bb_error_msg_and_die("pax is not tar"); + break; + case '7': + /* Reserved for high performance files, treat as normal file */ + case 0: + case '0': +#ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY + if (last_char_is(file_header->name, '/')) { + file_header->mode |= S_IFDIR; + } else +#endif + file_header->mode |= S_IFREG; + break; + case '2': + file_header->mode |= S_IFLNK; + break; + case '3': + file_header->mode |= S_IFCHR; + break; + case '4': + file_header->mode |= S_IFBLK; + break; + case '5': + file_header->mode |= S_IFDIR; + break; + case '6': + file_header->mode |= S_IFIFO; + break; +#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS + case 'L': { + longname = xmalloc(file_header->size + 1); + archive_xread_all(archive_handle, longname, file_header->size); + longname[file_header->size] = '\0'; + archive_handle->offset += file_header->size; + + return(get_header_tar(archive_handle)); + } + case 'K': { + linkname = xmalloc(file_header->size + 1); + archive_xread_all(archive_handle, linkname, file_header->size); + linkname[file_header->size] = '\0'; + archive_handle->offset += file_header->size; + + file_header->name = linkname; + return(get_header_tar(archive_handle)); + } + case 'D': /* GNU dump dir */ + case 'M': /* Continuation of multi volume archive*/ + case 'N': /* Old GNU for names > 100 characters */ + case 'S': /* Sparse file */ + case 'V': /* Volume header */ + bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag); +#endif + default: + bb_error_msg("Unknown typeflag: 0x%x", tar.formated.typeflag); + } + { /* Strip trailing '/' in directories */ + /* Must be done after mode is set as '/' is used to check if its a directory */ + char *tmp = last_char_is(file_header->name, '/'); + if (tmp) { + *tmp = '\0'; + } + } + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_header(archive_handle->file_header); + archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; + archive_handle->action_data(archive_handle); + archive_handle->passed = llist_add_to(archive_handle->passed, file_header->name); + } else { + data_skip(archive_handle); + } + archive_handle->offset += file_header->size; + + free(file_header->link_name); + + return(EXIT_SUCCESS); +} diff --git a/busybox/archival/libunarchive/get_header_tar_bz2.c b/busybox/archival/libunarchive/get_header_tar_bz2.c new file mode 100644 index 000000000..d49d6b96a --- /dev/null +++ b/busybox/archival/libunarchive/get_header_tar_bz2.c @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "libbb.h" +#include "unarchive.h" + +extern char get_header_tar_bz2(archive_handle_t *archive_handle) +{ + /* Cant lseek over pipe's */ + archive_handle->seek = seek_by_char; + + archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompressStream); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS); + + /* Can only do one file at a time */ + return(EXIT_FAILURE); +} diff --git a/busybox/archival/libunarchive/get_header_tar_gz.c b/busybox/archival/libunarchive/get_header_tar_gz.c new file mode 100644 index 000000000..9c708a951 --- /dev/null +++ b/busybox/archival/libunarchive/get_header_tar_gz.c @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "libbb.h" +#include "unarchive.h" + +extern char get_header_tar_gz(archive_handle_t *archive_handle) +{ + unsigned char magic[2]; + + /* Cant lseek over pipe's */ + archive_handle->seek = seek_by_char; + + archive_xread_all(archive_handle, &magic, 2); + if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) { + bb_error_msg_and_die("Invalid gzip magic"); + } + + check_header_gzip(archive_handle->src_fd); + + archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS); + + /* Can only do one file at a time */ + return(EXIT_FAILURE); +} diff --git a/busybox/archival/libunarchive/header_list.c b/busybox/archival/libunarchive/header_list.c new file mode 100644 index 000000000..5849a762e --- /dev/null +++ b/busybox/archival/libunarchive/header_list.c @@ -0,0 +1,7 @@ +#include +#include "unarchive.h" + +extern void header_list(const file_header_t *file_header) +{ + puts(file_header->name); +} diff --git a/busybox/archival/libunarchive/header_skip.c b/busybox/archival/libunarchive/header_skip.c new file mode 100644 index 000000000..4430178f8 --- /dev/null +++ b/busybox/archival/libunarchive/header_skip.c @@ -0,0 +1,7 @@ +#include +#include "unarchive.h" + +extern void header_skip(const file_header_t *file_header) +{ + return; +} diff --git a/busybox/archival/libunarchive/header_verbose_list.c b/busybox/archival/libunarchive/header_verbose_list.c new file mode 100644 index 000000000..6739dd393 --- /dev/null +++ b/busybox/archival/libunarchive/header_verbose_list.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include "libbb.h" +#include "unarchive.h" + +extern void header_verbose_list(const file_header_t *file_header) +{ + struct tm *mtime = localtime(&file_header->mtime); + + printf("%s %d/%d%10u %4u-%02u-%02u %02u:%02u:%02u %s", + bb_mode_string(file_header->mode), + file_header->uid, + file_header->gid, + (unsigned int) file_header->size, + 1900 + mtime->tm_year, + 1 + mtime->tm_mon, + mtime->tm_mday, + mtime->tm_hour, + mtime->tm_min, + mtime->tm_sec, + file_header->name); + + if (file_header->link_name) { + printf(" -> %s", file_header->link_name); + } + /* putchar isnt used anywhere else i dont think */ + puts(""); +} diff --git a/busybox/archival/libunarchive/init_handle.c b/busybox/archival/libunarchive/init_handle.c new file mode 100644 index 000000000..3cee84f67 --- /dev/null +++ b/busybox/archival/libunarchive/init_handle.c @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "libbb.h" +#include "unarchive.h" + +archive_handle_t *init_handle(void) +{ + archive_handle_t *archive_handle; + + /* Initialise default values */ + archive_handle = xmalloc(sizeof(archive_handle_t)); + memset(archive_handle, 0, sizeof(archive_handle_t)); + archive_handle->file_header = xmalloc(sizeof(file_header_t)); + archive_handle->action_header = header_skip; + archive_handle->action_data = data_skip; + archive_handle->filter = filter_accept_all; + archive_handle->seek = seek_by_jump; + + return(archive_handle); +} diff --git a/busybox/archival/libunarchive/open_transformer.c b/busybox/archival/libunarchive/open_transformer.c new file mode 100644 index 000000000..fb149fc0b --- /dev/null +++ b/busybox/archival/libunarchive/open_transformer.c @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "libbb.h" + +/* transformer(), more than meets the eye */ +extern int open_transformer(int src_fd, int (*transformer)(int src_fd, int dst_fd)) +{ + int fd_pipe[2]; + int pid; + + if (pipe(fd_pipe) != 0) { + bb_perror_msg_and_die("Can't create pipe"); + } + + pid = fork(); + if (pid == -1) { + bb_perror_msg_and_die("Fork failed"); + } + + if (pid == 0) { + /* child process */ + close(fd_pipe[0]); /* We don't wan't to read from the parent */ + transformer(src_fd, fd_pipe[1]); + close(fd_pipe[1]); /* Send EOF */ + close(src_fd); + exit(0); + /* notreached */ + } + + /* parent process */ + close(fd_pipe[1]); /* Don't want to write to the child */ + + return(fd_pipe[0]); +} diff --git a/busybox/archival/libunarchive/seek_by_char.c b/busybox/archival/libunarchive/seek_by_char.c new file mode 100644 index 000000000..a50d566f5 --- /dev/null +++ b/busybox/archival/libunarchive/seek_by_char.c @@ -0,0 +1,32 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "unarchive.h" +#include "busybox.h" + +/* If we are reading through a pipe(), or from stdin then we cant lseek, + * we must read and discard the data to skip over it. + * + * TODO: rename to seek_by_read + */ +extern void seek_by_char(const archive_handle_t *archive_handle, const unsigned int jump_size) +{ + if (jump_size) { + bb_copyfd_size(archive_handle->src_fd, -1, jump_size); + } +} diff --git a/busybox/archival/libunarchive/seek_by_jump.c b/busybox/archival/libunarchive/seek_by_jump.c new file mode 100644 index 000000000..578870d9b --- /dev/null +++ b/busybox/archival/libunarchive/seek_by_jump.c @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "libbb.h" +#include "unarchive.h" + +extern void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount) +{ + if (lseek(archive_handle->src_fd, (off_t) amount, SEEK_CUR) == (off_t) -1) { +#ifdef CONFIG_FEATURE_UNARCHIVE_TAPE + if (errno == ESPIPE) { + seek_by_char(archive_handle, amount); + } else +#endif + bb_perror_msg_and_die("Seek failure"); + } +} diff --git a/busybox/archival/libunarchive/unpack_ar_archive.c b/busybox/archival/libunarchive/unpack_ar_archive.c new file mode 100644 index 000000000..e8f113bcf --- /dev/null +++ b/busybox/archival/libunarchive/unpack_ar_archive.c @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include +#include "unarchive.h" +#include "busybox.h" + +extern void unpack_ar_archive(archive_handle_t *ar_archive) +{ + char magic[7]; + + archive_xread_all(ar_archive, magic, 7); + if (strncmp(magic, "!", 7) != 0) { + bb_error_msg_and_die("Invalid ar magic"); + } + ar_archive->offset += 7; + + while (get_header_ar(ar_archive) == EXIT_SUCCESS); +} diff --git a/busybox/archival/rpm.c b/busybox/archival/rpm.c new file mode 100644 index 000000000..30cdc93fb --- /dev/null +++ b/busybox/archival/rpm.c @@ -0,0 +1,349 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rpm applet for busybox + * + * Copyright (C) 2001,2002 by Laurence Anderson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include /* For ntohl & htonl function */ +#include /* For strncmp */ +#include /* For mmap */ +#include /* For ctime */ + +#include "busybox.h" +#include "unarchive.h" + +#define RPM_HEADER_MAGIC "\216\255\350" +#define RPM_CHAR_TYPE 1 +#define RPM_INT8_TYPE 2 +#define RPM_INT16_TYPE 3 +#define RPM_INT32_TYPE 4 +/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */ +#define RPM_STRING_TYPE 6 +#define RPM_BIN_TYPE 7 +#define RPM_STRING_ARRAY_TYPE 8 +#define RPM_I18NSTRING_TYPE 9 + +#define RPMTAG_NAME 1000 +#define RPMTAG_VERSION 1001 +#define RPMTAG_RELEASE 1002 +#define RPMTAG_SUMMARY 1004 +#define RPMTAG_DESCRIPTION 1005 +#define RPMTAG_BUILDTIME 1006 +#define RPMTAG_BUILDHOST 1007 +#define RPMTAG_SIZE 1009 +#define RPMTAG_VENDOR 1011 +#define RPMTAG_LICENSE 1014 +#define RPMTAG_PACKAGER 1015 +#define RPMTAG_GROUP 1016 +#define RPMTAG_URL 1020 +#define RPMTAG_PREIN 1023 +#define RPMTAG_POSTIN 1024 +#define RPMTAG_FILEFLAGS 1037 +#define RPMTAG_FILEUSERNAME 1039 +#define RPMTAG_FILEGROUPNAME 1040 +#define RPMTAG_SOURCERPM 1044 +#define RPMTAG_PREINPROG 1085 +#define RPMTAG_POSTINPROG 1086 +#define RPMTAG_PREFIXS 1098 +#define RPMTAG_DIRINDEXES 1116 +#define RPMTAG_BASENAMES 1117 +#define RPMTAG_DIRNAMES 1118 +#define RPMFILE_CONFIG (1 << 0) +#define RPMFILE_DOC (1 << 1) + +enum rpm_functions_e { + rpm_query = 1, + rpm_install = 2, + rpm_query_info = 4, + rpm_query_package = 8, + rpm_query_list = 16, + rpm_query_list_doc = 32, + rpm_query_list_config = 64 +}; + +typedef struct { + uint32_t tag; /* 4 byte tag */ + uint32_t type; /* 4 byte type */ + uint32_t offset; /* 4 byte offset */ + uint32_t count; /* 4 byte count */ +} rpm_index; + +static void *map; +static rpm_index **mytags; +static int tagcount; + +void extract_cpio_gz(int fd); +rpm_index **rpm_gettags(int fd, int *num_tags); +int bsearch_rpmtag(const void *key, const void *item); +char *rpm_getstring(int tag, int itemindex); +int rpm_getint(int tag, int itemindex); +int rpm_getcount(int tag); +void exec_script(int progtag, int datatag, char *prefix); +void fileaction_dobackup(char *filename, int fileref); +void fileaction_setowngrp(char *filename, int fileref); +void fileaction_list(char *filename, int itemno); +void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref)); + +int rpm_main(int argc, char **argv) +{ + int opt = 0, func = 0, rpm_fd, offset; + + while ((opt = getopt(argc, argv, "iqpldc")) != -1) { + switch (opt) { + case 'i': // First arg: Install mode, with q: Information + if (!func) func |= rpm_install; + else func |= rpm_query_info; + break; + case 'q': // First arg: Query mode + if (!func) func |= rpm_query; + else bb_show_usage(); + break; + case 'p': // Query a package + func |= rpm_query_package; + break; + case 'l': // List files in a package + func |= rpm_query_list; + break; + case 'd': // List doc files in a package (implies list) + func |= rpm_query_list; + func |= rpm_query_list_doc; + break; + case 'c': // List config files in a package (implies list) + func |= rpm_query_list; + func |= rpm_query_list_config; + break; + default: + bb_show_usage(); + } + } + + if (optind == argc) bb_show_usage(); + while (optind < argc) { + rpm_fd = bb_xopen(argv[optind], O_RDONLY); + mytags = rpm_gettags(rpm_fd, (int *) &tagcount); + offset = lseek(rpm_fd, 0, SEEK_CUR); + if (!mytags) { printf("Error reading rpm header\n"); exit(-1); } + map = mmap(0, offset > getpagesize() ? (offset + offset % getpagesize()) : getpagesize(), PROT_READ, MAP_SHARED, rpm_fd, 0); // Mimimum is one page + if (func & rpm_install) { + loop_through_files(RPMTAG_BASENAMES, fileaction_dobackup); /* Backup any config files */ + extract_cpio_gz(rpm_fd); // Extact the archive + loop_through_files(RPMTAG_BASENAMES, fileaction_setowngrp); /* Set the correct file uid/gid's */ + } else if (func & rpm_query && func & rpm_query_package) { + if (!((func & rpm_query_info) || (func & rpm_query_list))) { // If just a straight query, just give package name + printf("%s-%s-%s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_RELEASE, 0)); + } + if (func & rpm_query_info) { + /* Do the nice printout */ + time_t bdate_time; + struct tm *bdate; + char bdatestring[50]; + printf("Name : %-29sRelocations: %s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_PREFIXS, 0) ? rpm_getstring(RPMTAG_PREFIXS, 0) : "(not relocateable)"); + printf("Version : %-34sVendor: %s\n", rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_VENDOR, 0) ? rpm_getstring(RPMTAG_VENDOR, 0) : "(none)"); + bdate_time = rpm_getint(RPMTAG_BUILDTIME, 0); + bdate = localtime((time_t *) &bdate_time); + strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate); + printf("Release : %-30sBuild Date: %s\n", rpm_getstring(RPMTAG_RELEASE, 0), bdatestring); + printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstring(RPMTAG_BUILDHOST, 0)); + printf("Group : %-30sSource RPM: %s\n", rpm_getstring(RPMTAG_GROUP, 0), rpm_getstring(RPMTAG_SOURCERPM, 0)); + printf("Size : %-33dLicense: %s\n", rpm_getint(RPMTAG_SIZE, 0), rpm_getstring(RPMTAG_LICENSE, 0)); + printf("URL : %s\n", rpm_getstring(RPMTAG_URL, 0)); + printf("Summary : %s\n", rpm_getstring(RPMTAG_SUMMARY, 0)); + printf("Description :\n%s\n", rpm_getstring(RPMTAG_DESCRIPTION, 0)); + } + if (func & rpm_query_list) { + int count, it, flags; + count = rpm_getcount(RPMTAG_BASENAMES); + for (it = 0; it < count; it++) { + flags = rpm_getint(RPMTAG_FILEFLAGS, it); + switch ((func & rpm_query_list_doc) + (func & rpm_query_list_config)) + { + case rpm_query_list_doc: if (!(flags & RPMFILE_DOC)) continue; break; + case rpm_query_list_config: if (!(flags & RPMFILE_CONFIG)) continue; break; + case rpm_query_list_doc + rpm_query_list_config: if (!((flags & RPMFILE_CONFIG) || (flags & RPMFILE_DOC))) continue; break; + } + printf("%s%s\n", rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES, it)), rpm_getstring(RPMTAG_BASENAMES, it)); + } + } + } + optind++; + free (mytags); + } + return 0; +} + +void extract_cpio_gz(int fd) { + archive_handle_t *archive_handle; + unsigned char magic[2]; + + /* Initialise */ + archive_handle = init_handle(); + archive_handle->seek = seek_by_char; + //archive_handle->action_header = header_list; + archive_handle->action_data = data_extract_all; + archive_handle->flags |= ARCHIVE_PRESERVE_DATE; + archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS; + archive_handle->src_fd = fd; + archive_handle->offset = 0; + + bb_xread_all(archive_handle->src_fd, &magic, 2); + if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) { + bb_error_msg_and_die("Invalid gzip magic"); + } + check_header_gzip(archive_handle->src_fd); + chdir("/"); // Install RPM's to root + + archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip); + archive_handle->offset = 0; + while (get_header_cpio(archive_handle) == EXIT_SUCCESS); +} + + +rpm_index **rpm_gettags(int fd, int *num_tags) +{ + rpm_index **tags = calloc(200, sizeof(struct rpmtag *)); /* We should never need mode than 200, and realloc later */ + int pass, tagindex = 0; + lseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */ + + for (pass = 0; pass < 2; pass++) { /* 1st pass is the signature headers, 2nd is the main stuff */ + struct { + char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */ + uint8_t version; /* 1 byte version number */ + uint32_t reserved; /* 4 bytes reserved */ + uint32_t entries; /* Number of entries in header (4 bytes) */ + uint32_t size; /* Size of store (4 bytes) */ + } header; + rpm_index *tmpindex; + int storepos; + + read(fd, &header, sizeof(header)); + if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) return NULL; /* Invalid magic */ + if (header.version != 1) return NULL; /* This program only supports v1 headers */ + header.size = ntohl(header.size); + header.entries = ntohl(header.entries); + storepos = lseek(fd,0,SEEK_CUR) + header.entries * 16; + + while (header.entries--) { + tmpindex = tags[tagindex++] = malloc(sizeof(rpm_index)); + read(fd, tmpindex, sizeof(rpm_index)); + tmpindex->tag = ntohl(tmpindex->tag); tmpindex->type = ntohl(tmpindex->type); tmpindex->count = ntohl(tmpindex->count); + tmpindex->offset = storepos + ntohl(tmpindex->offset); + if (pass==0) tmpindex->tag -= 743; + } + lseek(fd, header.size, SEEK_CUR); /* Seek past store */ + if (pass==0) lseek(fd, (8 - (lseek(fd,0,SEEK_CUR) % 8)) % 8, SEEK_CUR); /* Skip padding to 8 byte boundary after reading signature headers */ + } + tags = realloc(tags, tagindex * sizeof(struct rpmtag *)); /* realloc tags to save space */ + *num_tags = tagindex; + return tags; /* All done, leave the file at the start of the gzipped cpio archive */ +} + +int bsearch_rpmtag(const void *key, const void *item) +{ + rpm_index **tmp = (rpm_index **) item; + return ((int) key - tmp[0]->tag); +} + +int rpm_getcount(int tag) +{ + rpm_index **found; + found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + if (!found) return 0; + else return found[0]->count; +} + +char *rpm_getstring(int tag, int itemindex) +{ + rpm_index **found; + found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + if (!found || itemindex >= found[0]->count) return NULL; + if (found[0]->type == RPM_STRING_TYPE || found[0]->type == RPM_I18NSTRING_TYPE || found[0]->type == RPM_STRING_ARRAY_TYPE) { + int n; + char *tmpstr = (char *) (map + found[0]->offset); + for (n=0; n < itemindex; n++) tmpstr = tmpstr + strlen(tmpstr) + 1; + return tmpstr; + } else return NULL; +} + +int rpm_getint(int tag, int itemindex) +{ + rpm_index **found; + int n, *tmpint; + found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + if (!found || itemindex >= found[0]->count) return -1; + tmpint = (int *) (map + found[0]->offset); + if (found[0]->type == RPM_INT32_TYPE) { + for (n=0; ntype == RPM_INT16_TYPE) { + for (n=0; ntype == RPM_INT8_TYPE) { + for (n=0; n +#include /* For ntohl & htonl function */ +#include +#include +#include +#include "busybox.h" +#include "unarchive.h" + +#define RPM_MAGIC "\355\253\356\333" +#define RPM_HEADER_MAGIC "\216\255\350" + +struct rpm_lead { + unsigned char magic[4]; + uint8_t major, minor; + uint16_t type; + uint16_t archnum; + char name[66]; + uint16_t osnum; + uint16_t signature_type; + char reserved[16]; +}; + +struct rpm_header { + char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */ + uint8_t version; /* 1 byte version number */ + uint32_t reserved; /* 4 bytes reserved */ + uint32_t entries; /* Number of entries in header (4 bytes) */ + uint32_t size; /* Size of store (4 bytes) */ +}; + +void skip_header(int rpm_fd) +{ + struct rpm_header header; + + bb_xread_all(rpm_fd, &header, sizeof(struct rpm_header)); + if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) { + bb_error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */ + } + if (header.version != 1) { + bb_error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */ + } + header.entries = ntohl(header.entries); + header.size = ntohl(header.size); + lseek (rpm_fd, 16 * header.entries, SEEK_CUR); /* Seek past index entries */ + lseek (rpm_fd, header.size, SEEK_CUR); /* Seek past store */ +} + +/* No getopt required */ +extern int rpm2cpio_main(int argc, char **argv) +{ + struct rpm_lead lead; + int rpm_fd; + unsigned char magic[2]; + + if (argc == 1) { + rpm_fd = STDIN_FILENO; + } else { + rpm_fd = bb_xopen(argv[1], O_RDONLY); + } + + bb_xread_all(rpm_fd, &lead, sizeof(struct rpm_lead)); + if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) { + bb_error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */ + } + + /* Skip the signature header */ + skip_header(rpm_fd); + lseek(rpm_fd, (8 - (lseek(rpm_fd, 0, SEEK_CUR) % 8)) % 8, SEEK_CUR); + + /* Skip the main header */ + skip_header(rpm_fd); + + bb_xread_all(rpm_fd, &magic, 2); + if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) { + bb_error_msg_and_die("Invalid gzip magic"); + } + + check_header_gzip(rpm_fd); + if (inflate_gunzip(rpm_fd, STDOUT_FILENO) != 0) { + bb_error_msg("Error inflating"); + } + + close(rpm_fd); + + return 0; +} diff --git a/busybox/archival/tar.c b/busybox/archival/tar.c new file mode 100644 index 000000000..950e21dd3 --- /dev/null +++ b/busybox/archival/tar.c @@ -0,0 +1,891 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tar implementation for busybox + * + * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg + * Glenn McGrath + * + * Note, that as of BusyBox-0.43, tar has been completely rewritten from the + * ground up. It still has remnants of the old code lying about, but it is + * very different now (i.e., cleaner, less global variables, etc.) + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * 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 /* major() and minor() */ +#include "unarchive.h" +#include "busybox.h" + +#ifdef CONFIG_FEATURE_TAR_CREATE + +/* Tar file constants */ +# define TAR_MAGIC "ustar" /* ustar and a null */ +# define TAR_VERSION " " /* Be compatable with GNU tar format */ + +static const int TAR_BLOCK_SIZE = 512; +static const int TAR_MAGIC_LEN = 6; +static const int TAR_VERSION_LEN = 2; + +/* POSIX tar Header Block, from POSIX 1003.1-1990 */ +enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */ +struct TarHeader { /* byte offset */ + char name[NAME_SIZE]; /* 0-99 */ + char mode[8]; /* 100-107 */ + char uid[8]; /* 108-115 */ + char gid[8]; /* 116-123 */ + char size[12]; /* 124-135 */ + char mtime[12]; /* 136-147 */ + char chksum[8]; /* 148-155 */ + char typeflag; /* 156-156 */ + char linkname[NAME_SIZE]; /* 157-256 */ + char magic[6]; /* 257-262 */ + char version[2]; /* 263-264 */ + char uname[32]; /* 265-296 */ + char gname[32]; /* 297-328 */ + char devmajor[8]; /* 329-336 */ + char devminor[8]; /* 337-344 */ + char prefix[155]; /* 345-499 */ + char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */ +}; +typedef struct TarHeader TarHeader; + +/* +** writeTarFile(), writeFileToTarball(), and writeTarHeader() are +** the only functions that deal with the HardLinkInfo structure. +** Even these functions use the xxxHardLinkInfo() functions. +*/ +typedef struct HardLinkInfo HardLinkInfo; +struct HardLinkInfo { + HardLinkInfo *next; /* Next entry in list */ + dev_t dev; /* Device number */ + ino_t ino; /* Inode number */ + short linkCount; /* (Hard) Link Count */ + char name[1]; /* Start of filename (must be last) */ +}; + +/* Some info to be carried along when creating a new tarball */ +struct TarBallInfo { + char *fileName; /* File name of the tarball */ + int tarFd; /* Open-for-write file descriptor + for the tarball */ + struct stat statBuf; /* Stat info for the tarball, letting + us know the inode and device that the + tarball lives, so we can avoid trying + to include the tarball into itself */ + int verboseFlag; /* Whether to print extra stuff or not */ + const llist_t *excludeList; /* List of files to not include */ + HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ + HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ +}; +typedef struct TarBallInfo TarBallInfo; + +/* A nice enum with all the possible tar file content types */ +enum TarFileType { + REGTYPE = '0', /* regular file */ + REGTYPE0 = '\0', /* regular file (ancient bug compat) */ + LNKTYPE = '1', /* hard link */ + SYMTYPE = '2', /* symbolic link */ + CHRTYPE = '3', /* character special */ + BLKTYPE = '4', /* block special */ + DIRTYPE = '5', /* directory */ + FIFOTYPE = '6', /* FIFO special */ + CONTTYPE = '7', /* reserved */ + GNULONGLINK = 'K', /* GNU long (>100 chars) link name */ + GNULONGNAME = 'L', /* GNU long (>100 chars) file name */ +}; +typedef enum TarFileType TarFileType; + +/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ +static inline void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr, + struct stat *statbuf, + const char *name) +{ + /* Note: hlInfoHeadPtr can never be NULL! */ + HardLinkInfo *hlInfo; + + hlInfo = (HardLinkInfo *) xmalloc(sizeof(HardLinkInfo) + strlen(name)); + hlInfo->next = *hlInfoHeadPtr; + *hlInfoHeadPtr = hlInfo; + hlInfo->dev = statbuf->st_dev; + hlInfo->ino = statbuf->st_ino; + hlInfo->linkCount = statbuf->st_nlink; + strcpy(hlInfo->name, name); +} + +static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr) +{ + HardLinkInfo *hlInfo = NULL; + HardLinkInfo *hlInfoNext = NULL; + + if (hlInfoHeadPtr) { + hlInfo = *hlInfoHeadPtr; + while (hlInfo) { + hlInfoNext = hlInfo->next; + free(hlInfo); + hlInfo = hlInfoNext; + } + *hlInfoHeadPtr = NULL; + } + return; +} + +/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ +static inline HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf) +{ + while (hlInfo) { + if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev)) + break; + hlInfo = hlInfo->next; + } + return (hlInfo); +} + +/* Put an octal string into the specified buffer. + * The number is zero and space padded and possibly null padded. + * Returns TRUE if successful. */ +static int putOctal(char *cp, int len, long value) +{ + int tempLength; + char tempBuffer[32]; + char *tempString = tempBuffer; + + /* Create a string of the specified length with an initial space, + * leading zeroes and the octal number, and a trailing null. */ + sprintf(tempString, "%0*lo", len - 1, value); + + /* If the string is too large, suppress the leading space. */ + tempLength = strlen(tempString) + 1; + if (tempLength > len) { + tempLength--; + tempString++; + } + + /* If the string is still too large, suppress the trailing null. */ + if (tempLength > len) + tempLength--; + + /* If the string is still too large, fail. */ + if (tempLength > len) + return FALSE; + + /* Copy the string to the field. */ + memcpy(cp, tempString, len); + + return TRUE; +} + +/* Write out a tar header for the specified file/directory/whatever */ +static inline int writeTarHeader(struct TarBallInfo *tbInfo, + const char *header_name, + const char *real_name, struct stat *statbuf) +{ + long chksum = 0; + struct TarHeader header; + const unsigned char *cp = (const unsigned char *) &header; + ssize_t size = sizeof(struct TarHeader); + + memset(&header, 0, size); + + strncpy(header.name, header_name, sizeof(header.name)); + + putOctal(header.mode, sizeof(header.mode), statbuf->st_mode); + putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); + putOctal(header.gid, sizeof(header.gid), statbuf->st_gid); + putOctal(header.size, sizeof(header.size), 0); /* Regular file size is handled later */ + putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime); + strncpy(header.magic, TAR_MAGIC TAR_VERSION, + TAR_MAGIC_LEN + TAR_VERSION_LEN); + + /* Enter the user and group names (default to root if it fails) */ + if (my_getpwuid(header.uname, statbuf->st_uid, sizeof(header.uname)) == NULL) + strcpy(header.uname, "root"); + if (my_getgrgid(header.gname, statbuf->st_gid, sizeof(header.gname)) == NULL) + strcpy(header.gname, "root"); + + if (tbInfo->hlInfo) { + /* This is a hard link */ + header.typeflag = LNKTYPE; + strncpy(header.linkname, tbInfo->hlInfo->name, + sizeof(header.linkname)); + } else if (S_ISLNK(statbuf->st_mode)) { + char *lpath = xreadlink(real_name); + + if (!lpath) /* Already printed err msg inside xreadlink() */ + return (FALSE); + header.typeflag = SYMTYPE; + strncpy(header.linkname, lpath, sizeof(header.linkname)); + free(lpath); + } else if (S_ISDIR(statbuf->st_mode)) { + header.typeflag = DIRTYPE; + strncat(header.name, "/", sizeof(header.name)); + } else if (S_ISCHR(statbuf->st_mode)) { + header.typeflag = CHRTYPE; + putOctal(header.devmajor, sizeof(header.devmajor), + major(statbuf->st_rdev)); + putOctal(header.devminor, sizeof(header.devminor), + minor(statbuf->st_rdev)); + } else if (S_ISBLK(statbuf->st_mode)) { + header.typeflag = BLKTYPE; + putOctal(header.devmajor, sizeof(header.devmajor), + major(statbuf->st_rdev)); + putOctal(header.devminor, sizeof(header.devminor), + minor(statbuf->st_rdev)); + } else if (S_ISFIFO(statbuf->st_mode)) { + header.typeflag = FIFOTYPE; + } else if (S_ISREG(statbuf->st_mode)) { + header.typeflag = REGTYPE; + putOctal(header.size, sizeof(header.size), statbuf->st_size); + } else { + bb_error_msg("%s: Unknown file type", real_name); + return (FALSE); + } + + /* Calculate and store the checksum (i.e., the sum of all of the bytes of + * the header). The checksum field must be filled with blanks for the + * calculation. The checksum field is formatted differently from the + * other fields: it has [6] digits, a null, then a space -- rather than + * digits, followed by a null like the other fields... */ + memset(header.chksum, ' ', sizeof(header.chksum)); + cp = (const unsigned char *) &header; + while (size-- > 0) + chksum += *cp++; + putOctal(header.chksum, 7, chksum); + + /* Now write the header out to disk */ + if ((size = + bb_full_write(tbInfo->tarFd, (char *) &header, + sizeof(struct TarHeader))) < 0) { + bb_error_msg(bb_msg_io_error, real_name); + return (FALSE); + } + /* Pad the header up to the tar block size */ + for (; size < TAR_BLOCK_SIZE; size++) { + write(tbInfo->tarFd, "\0", 1); + } + /* Now do the verbose thing (or not) */ + + if (tbInfo->verboseFlag) { + FILE *vbFd = stdout; + + if (tbInfo->tarFd == STDOUT_FILENO) /* If the archive goes to stdout, verbose to stderr */ + vbFd = stderr; + + fprintf(vbFd, "%s\n", header.name); + } + + return (TRUE); +} + +# ifdef CONFIG_FEATURE_TAR_FROM +static inline int exclude_file(const llist_t *excluded_files, const char *file) +{ + while (excluded_files) { + if (excluded_files->data[0] == '/') { + if (fnmatch(excluded_files->data, file, + FNM_PATHNAME | FNM_LEADING_DIR) == 0) + return 1; + } else { + const char *p; + + for (p = file; p[0] != '\0'; p++) { + if ((p == file || p[-1] == '/') && p[0] != '/' && + fnmatch(excluded_files->data, p, + FNM_PATHNAME | FNM_LEADING_DIR) == 0) + return 1; + } + } + excluded_files = excluded_files->link; + } + + return 0; +} +# endif + +static int writeFileToTarball(const char *fileName, struct stat *statbuf, + void *userData) +{ + struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; + const char *header_name; + + /* + ** Check to see if we are dealing with a hard link. + ** If so - + ** Treat the first occurance of a given dev/inode as a file while + ** treating any additional occurances as hard links. This is done + ** by adding the file information to the HardLinkInfo linked list. + */ + tbInfo->hlInfo = NULL; + if (statbuf->st_nlink > 1) { + tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); + if (tbInfo->hlInfo == NULL) + addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName); + } + + /* It is against the rules to archive a socket */ + if (S_ISSOCK(statbuf->st_mode)) { + bb_error_msg("%s: socket ignored", fileName); + return (TRUE); + } + + /* It is a bad idea to store the archive we are in the process of creating, + * so check the device and inode to be sure that this particular file isn't + * the new tarball */ + if (tbInfo->statBuf.st_dev == statbuf->st_dev && + tbInfo->statBuf.st_ino == statbuf->st_ino) { + bb_error_msg("%s: file is the archive; skipping", fileName); + return (TRUE); + } + + header_name = fileName; + while (header_name[0] == '/') { + static int alreadyWarned = FALSE; + + if (alreadyWarned == FALSE) { + bb_error_msg("Removing leading '/' from member names"); + alreadyWarned = TRUE; + } + header_name++; + } + + if (strlen(fileName) >= NAME_SIZE) { + bb_error_msg(bb_msg_name_longer_than_foo, NAME_SIZE); + return (TRUE); + } + + if (header_name[0] == '\0') + return TRUE; + +# ifdef CONFIG_FEATURE_TAR_FROM + if (exclude_file(tbInfo->excludeList, header_name)) { + return SKIP; + } +# endif /* CONFIG_FEATURE_TAR_FROM */ + + if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) { + return (FALSE); + } + + /* Now, if the file is a regular file, copy it out to the tarball */ + if ((tbInfo->hlInfo == NULL) + && (S_ISREG(statbuf->st_mode))) { + int inputFileFd; + ssize_t readSize = 0; + + /* open the file we want to archive, and make sure all is well */ + if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { + bb_perror_msg("%s: Cannot open", fileName); + return (FALSE); + } + + /* write the file to the archive */ + readSize = bb_copyfd_eof(inputFileFd, tbInfo->tarFd); + + /* Pad the file up to the tar block size */ + for (; (readSize % TAR_BLOCK_SIZE) != 0; readSize++) { + write(tbInfo->tarFd, "\0", 1); + } + close(inputFileFd); + } + + return (TRUE); +} + +static inline int writeTarFile(const int tar_fd, const int verboseFlag, + const unsigned long dereferenceFlag, const llist_t *include, + const llist_t *exclude, const int gzip) +{ +#ifdef CONFIG_FEATURE_TAR_GZIP + int gzipDataPipe[2] = { -1, -1 }; + int gzipStatusPipe[2] = { -1, -1 }; + pid_t gzipPid = 0; + volatile int vfork_exec_errno = 0; +#endif + + int errorFlag = FALSE; + ssize_t size; + struct TarBallInfo tbInfo; + + tbInfo.hlInfoHead = NULL; + + fchmod(tar_fd, 0644); + tbInfo.tarFd = tar_fd; + tbInfo.verboseFlag = verboseFlag; + + /* Store the stat info for the tarball's file, so + * can avoid including the tarball into itself.... */ + if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) + bb_perror_msg_and_die("Couldnt stat tar file"); + +#ifdef CONFIG_FEATURE_TAR_GZIP + if (gzip) { + if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0) { + bb_perror_msg_and_die("Failed to create gzip pipe"); + } + + signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ + +# if __GNUC__ + /* Avoid vfork clobbering */ + (void) &include; + (void) &errorFlag; +# endif + + gzipPid = vfork(); + + if (gzipPid == 0) { + dup2(gzipDataPipe[0], 0); + close(gzipDataPipe[1]); + + if (tbInfo.tarFd != 1) + dup2(tbInfo.tarFd, 1); + + close(gzipStatusPipe[0]); + fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC); /* close on exec shows sucess */ + + execl("/bin/gzip", "gzip", "-f", 0); + vfork_exec_errno = errno; + + close(gzipStatusPipe[1]); + exit(-1); + } else if (gzipPid > 0) { + close(gzipDataPipe[0]); + close(gzipStatusPipe[1]); + + while (1) { + char buf; + + int n = bb_full_read(gzipStatusPipe[0], &buf, 1); + + if (n == 0 && vfork_exec_errno != 0) { + errno = vfork_exec_errno; + bb_perror_msg_and_die("Could not exec gzip process"); + } else if ((n < 0) && (errno == EAGAIN || errno == EINTR)) + continue; /* try it again */ + break; + } + close(gzipStatusPipe[0]); + + tbInfo.tarFd = gzipDataPipe[1]; + } else { + bb_perror_msg_and_die("Failed to vfork gzip process"); + } + } +#endif + + tbInfo.excludeList = exclude; + + /* Read the directory/files and iterate over them one at a time */ + while (include) { + if (!recursive_action(include->data, TRUE, dereferenceFlag, FALSE, + writeFileToTarball, writeFileToTarball, + (void *) &tbInfo)) { + errorFlag = TRUE; + } + include = include->link; + } + /* Write two empty blocks to the end of the archive */ + for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++) { + write(tbInfo.tarFd, "\0", 1); + } + + /* To be pedantically correct, we would check if the tarball + * is smaller than 20 tar blocks, and pad it if it was smaller, + * but that isn't necessary for GNU tar interoperability, and + * so is considered a waste of space */ + + /* Hang up the tools, close up shop, head home */ + close(tbInfo.tarFd); + if (errorFlag) + bb_error_msg("Error exit delayed from previous errors"); + + freeHardLinkInfo(&tbInfo.hlInfoHead); + +#ifdef CONFIG_FEATURE_TAR_GZIP + if (gzip && gzipPid) { + if (waitpid(gzipPid, NULL, 0) == -1) + printf("Couldnt wait ?"); + } +#endif + + return !errorFlag; +} +#endif /* tar_create */ + +#ifdef CONFIG_FEATURE_TAR_FROM +static llist_t *append_file_list_to_list(llist_t *list) +{ + FILE *src_stream; + llist_t *cur = list; + llist_t *tmp; + char *line; + llist_t *newlist = NULL; + + while(cur) { + src_stream = bb_xfopen(cur->data, "r"); + tmp = cur; + cur = cur->link; + free(tmp); + while((line = bb_get_chomped_line_from_file(src_stream)) != NULL) { + newlist = llist_add_to(newlist, line); + } + fclose(src_stream); + } + return newlist; +} +#endif + +#ifdef CONFIG_FEATURE_TAR_COMPRESS +static char get_header_tar_Z(archive_handle_t *archive_handle) +{ + /* Cant lseek over pipe's */ + archive_handle->seek = seek_by_char; + + /* do the decompression, and cleanup */ + if ((bb_xread_char(archive_handle->src_fd) != 0x1f) || (bb_xread_char(archive_handle->src_fd) != 0x9d)) { + bb_error_msg_and_die("Invalid magic"); + } + + archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS); + + /* Can only do one file at a time */ + return(EXIT_FAILURE); +} +#endif + +#define CTX_TEST (1 << 0) +#define CTX_EXTRACT (1 << 1) +#define TAR_OPT_BASEDIR (1 << 2) +#define TAR_OPT_TARNAME (1 << 3) +#define TAR_OPT_2STDOUT (1 << 4) +#define TAR_OPT_P (1 << 5) +#define TAR_OPT_VERBOSE (1 << 6) +#define TAR_OPT_KEEP_OLD (1 << 7) + +#ifdef CONFIG_FEATURE_TAR_CREATE +# define CTX_CREATE (1 << 8) +# define TAR_OPT_DEREFERNCE (1 << 9) +# define TAR_OPT_STR_CREATE "ch" +# define TAR_OPT_FLAG_CREATE 2 +#else +//# define CTX_CREATE 0 +# define TAR_OPT_STR_CREATE "" +# define TAR_OPT_FLAG_CREATE 0 +#endif + +#ifdef CONFIG_FEATURE_TAR_BZIP2 +# define TAR_OPT_BZIP2 (1 << (8 + TAR_OPT_FLAG_CREATE)) +# define TAR_OPT_STR_BZIP2 "j" +# define TAR_OPT_FLAG_BZIP2 1 +#else +# define TAR_OPT_STR_BZIP2 "" +# define TAR_OPT_FLAG_BZIP2 0 +#endif + +#ifdef CONFIG_FEATURE_TAR_FROM +# define TAR_OPT_INCLUDE_FROM (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2)) +# define TAR_OPT_EXCLUDE_FROM (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + 1)) +# define TAR_OPT_STR_FROM "T:X:" +# define TAR_OPT_FLAG_FROM 2 +#else +# define TAR_OPT_STR_FROM "" +# define TAR_OPT_FLAG_FROM 0 +#endif + +#ifdef CONFIG_FEATURE_TAR_GZIP +# define TAR_OPT_GZIP (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + TAR_OPT_FLAG_FROM)) +# define TAR_OPT_STR_GZIP "z" +# define TAR_OPT_FLAG_GZIP 1 +#else +# define TAR_OPT_STR_GZIP "" +# define TAR_OPT_FLAG_GZIP 0 +#endif + +#ifdef CONFIG_FEATURE_TAR_COMPRESS +# define TAR_OPT_UNCOMPRESS (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + TAR_OPT_FLAG_FROM + TAR_OPT_FLAG_GZIP)) +# define TAR_OPT_STR_COMPRESS "Z" +#else +# define TAR_OPT_STR_COMPRESS "" +#endif + +static const char tar_options[]="txC:f:Opvk" \ + TAR_OPT_STR_CREATE \ + TAR_OPT_STR_BZIP2 \ + TAR_OPT_STR_FROM \ + TAR_OPT_STR_GZIP \ + TAR_OPT_STR_COMPRESS; + +#ifdef CONFIG_FEATURE_TAR_LONG_OPTIONS +static const struct option tar_long_options[] = { + { "list", 0, NULL, 't' }, + { "extract", 0, NULL, 'x' }, + { "directory", 1, NULL, 'C' }, + { "file", 1, NULL, 'f' }, + { "to-stdout", 0, NULL, 'O' }, + { "same-permissions", 0, NULL, 'p' }, + { "verbose", 0, NULL, 'v' }, + { "keep-old", 0, NULL, 'k' }, +# ifdef CONFIG_FEATURE_TAR_CREATE + { "create", 0, NULL, 'c' }, + { "dereference", 0, NULL, 'h' }, +# endif +# ifdef CONFIG_FEATURE_TAR_BZIP2 + { "bzip2", 0, NULL, 'j' }, +# endif +# ifdef CONFIG_FEATURE_TAR_FROM + { "files-from", 1, NULL, 'T' }, + { "exclude-from", 1, NULL, 'X' }, +# endif +# ifdef CONFIG_FEATURE_TAR_GZIP + { "gzip", 0, NULL, 'z' }, +# endif +# ifdef CONFIG_FEATURE_TAR_COMPRESS + { "compress", 0, NULL, 'Z' }, +# endif + { 0, 0, 0, 0 } +}; +#endif + +int tar_main(int argc, char **argv) +{ + char (*get_header_ptr)(archive_handle_t *) = get_header_tar; + archive_handle_t *tar_handle; + char *base_dir = NULL; + const char *tar_filename = "-"; + unsigned long opt; + unsigned long ctx_flag = 0; + + if (argc < 2) { + bb_show_usage(); + } + + /* Prepend '-' to the first argument if required */ + if (argv[1][0] != '-') { + char *tmp; + + bb_xasprintf(&tmp, "-%s", argv[1]); + argv[1] = tmp; + } + + /* Initialise default values */ + tar_handle = init_handle(); + tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_PRESERVE_DATE | ARCHIVE_EXTRACT_UNCONDITIONAL; + + bb_opt_complementaly = "c~tx:t~cx:x~ct:X*:T*"; +#ifdef CONFIG_FEATURE_TAR_LONG_OPTIONS + bb_applet_long_options = tar_long_options; +#endif + + opt = bb_getopt_ulflags(argc, argv, tar_options, + &base_dir, /* Change to dir */ + &tar_filename /* archive filename */ +#ifdef CONFIG_FEATURE_TAR_FROM + , &(tar_handle->accept), + &(tar_handle->reject) +#endif + ); + + /* Check one and only one context option was given */ + if(opt & 0x80000000UL) { + bb_show_usage(); + } +#ifdef CONFIG_FEATURE_TAR_CREATE + ctx_flag = opt & (CTX_CREATE | CTX_TEST | CTX_EXTRACT); +#else + ctx_flag = opt & (CTX_TEST | CTX_EXTRACT); +#endif + if (ctx_flag == 0) { + bb_show_usage(); + } + if(ctx_flag & CTX_TEST) { + if ((tar_handle->action_header == header_list) || + (tar_handle->action_header == header_verbose_list)) { + tar_handle->action_header = header_verbose_list; + } else { + tar_handle->action_header = header_list; + } + } + if(ctx_flag & CTX_EXTRACT) { + if (tar_handle->action_data != data_extract_to_stdout) + tar_handle->action_data = data_extract_all; + } + if(opt & TAR_OPT_2STDOUT) { + /* To stdout */ + tar_handle->action_data = data_extract_to_stdout; + } + if(opt & TAR_OPT_VERBOSE) { + if ((tar_handle->action_header == header_list) || + (tar_handle->action_header == header_verbose_list)) + { + tar_handle->action_header = header_verbose_list; + } else { + tar_handle->action_header = header_list; + } + } + if (opt & TAR_OPT_KEEP_OLD) { + tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL; + } + +#ifdef CONFIG_FEATURE_TAR_GZIP + if(opt & TAR_OPT_GZIP) { + get_header_ptr = get_header_tar_gz; + } +#endif +#ifdef CONFIG_FEATURE_TAR_BZIP2 + if(opt & TAR_OPT_BZIP2) { + get_header_ptr = get_header_tar_bz2; + } +#endif +#ifdef CONFIG_FEATURE_TAR_COMPRESS + if(opt & TAR_OPT_UNCOMPRESS) { + get_header_ptr = get_header_tar_Z; + } +#endif +#ifdef CONFIG_FEATURE_TAR_FROM + if(opt & TAR_OPT_EXCLUDE_FROM) { + tar_handle->reject = append_file_list_to_list(tar_handle->reject); + } + if(opt & TAR_OPT_INCLUDE_FROM) { + tar_handle->accept = append_file_list_to_list(tar_handle->accept); + } +#endif + + /* Check if we are reading from stdin */ + if ((argv[optind]) && (*argv[optind] == '-')) { + /* Default is to read from stdin, so just skip to next arg */ + optind++; + } + + /* Setup an array of filenames to work with */ + /* TODO: This is the same as in ar, separate function ? */ + while (optind < argc) { + char *filename_ptr = last_char_is(argv[optind], '/'); + if (filename_ptr) { + *filename_ptr = '\0'; + } + tar_handle->accept = llist_add_to(tar_handle->accept, argv[optind]); + optind++; + } + + if ((tar_handle->accept) || (tar_handle->reject)) { + tar_handle->filter = filter_accept_reject_list; + } + + /* Open the tar file */ + { + FILE *tar_stream; + int flags; + +#ifdef CONFIG_FEATURE_TAR_CREATE + if (opt & CTX_CREATE) { + /* Make sure there is at least one file to tar up. */ + if (tar_handle->accept == NULL) { + bb_error_msg_and_die("Cowardly refusing to create an empty archive"); + } + tar_stream = stdout; + flags = O_WRONLY | O_CREAT | O_EXCL; + unlink(tar_filename); + } else +#endif + { + tar_stream = stdin; + flags = O_RDONLY; + } + + if ((tar_filename[0] == '-') && (tar_filename[1] == '\0')) { + tar_handle->src_fd = fileno(tar_stream); + tar_handle->seek = seek_by_char; + } else { + tar_handle->src_fd = bb_xopen(tar_filename, flags); + } + } + + if ((base_dir) && (chdir(base_dir))) { + bb_perror_msg_and_die("Couldnt chdir to %s", base_dir); + } + +#ifdef CONFIG_FEATURE_TAR_CREATE + /* create an archive */ + if (opt & CTX_CREATE) { + int verboseFlag = FALSE; + int gzipFlag = FALSE; + +# ifdef CONFIG_FEATURE_TAR_GZIP + if (get_header_ptr == get_header_tar_gz) { + gzipFlag = TRUE; + } +# endif /* CONFIG_FEATURE_TAR_GZIP */ +# ifdef CONFIG_FEATURE_TAR_BZIP2 + if (get_header_ptr == get_header_tar_bz2) { + bb_error_msg_and_die("Creating bzip2 compressed archives is not currently supported."); + } +# endif /* CONFIG_FEATURE_TAR_BZIP2 */ + + if ((tar_handle->action_header == header_list) || + (tar_handle->action_header == header_verbose_list)) { + verboseFlag = TRUE; + } + writeTarFile(tar_handle->src_fd, verboseFlag, opt & TAR_OPT_DEREFERNCE, tar_handle->accept, + tar_handle->reject, gzipFlag); + } else +#endif /* CONFIG_FEATURE_TAR_CREATE */ + { + while (get_header_ptr(tar_handle) == EXIT_SUCCESS); + + /* Ckeck that every file that should have been extracted was */ + while (tar_handle->accept) { + if (find_list_entry(tar_handle->reject, tar_handle->accept->data) == NULL) { + if (find_list_entry(tar_handle->passed, tar_handle->accept->data) == NULL) { + bb_error_msg_and_die("%s: Not found in archive\n", tar_handle->accept->data); + } + } + tar_handle->accept = tar_handle->accept->link; + } + } + +#ifdef CONFIG_FEATURE_CLEAN_UP + if (tar_handle->src_fd != STDIN_FILENO) { + close(tar_handle->src_fd); + } +#endif /* CONFIG_FEATURE_CLEAN_UP */ + + return(EXIT_SUCCESS); +} diff --git a/busybox/archival/uncompress.c b/busybox/archival/uncompress.c new file mode 100644 index 000000000..48b4e2cad --- /dev/null +++ b/busybox/archival/uncompress.c @@ -0,0 +1,115 @@ +/* vi: set sw=4 ts=4: */ +/* + * Uncompress applet for busybox (c) 2002 Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "libbb.h" +#include "unarchive.h" + +#define GUNZIP_TO_STDOUT 1 +#define GUNZIP_FORCE 2 + +extern int uncompress_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + unsigned long flags; + + flags = bb_getopt_ulflags(argc, argv, "cf"); + + while (optind < argc) { + const char *compressed_file = argv[optind++]; + const char *delete_path = NULL; + char *uncompressed_file = NULL; + int src_fd; + int dst_fd; + + if (strcmp(compressed_file, "-") == 0) { + src_fd = STDIN_FILENO; + flags |= GUNZIP_TO_STDOUT; + } else { + src_fd = bb_xopen(compressed_file, O_RDONLY); + } + + /* Check that the input is sane. */ + if (isatty(src_fd) && ((flags & GUNZIP_FORCE) == 0)) { + bb_error_msg_and_die + ("compressed data not read from terminal. Use -f to force it."); + } + + /* Set output filename and number */ + if (flags & GUNZIP_TO_STDOUT) { + dst_fd = STDOUT_FILENO; + } else { + struct stat stat_buf; + char *extension; + + uncompressed_file = bb_xstrdup(compressed_file); + + extension = strrchr(uncompressed_file, '.'); + if (!extension || (strcmp(extension, ".Z") != 0)) { + bb_error_msg_and_die("Invalid extension"); + } + *extension = '\0'; + + /* Open output file */ + dst_fd = bb_xopen(uncompressed_file, O_WRONLY | O_CREAT); + + /* Set permissions on the file */ + stat(compressed_file, &stat_buf); + chmod(uncompressed_file, stat_buf.st_mode); + + /* If unzip succeeds remove the old file */ + delete_path = compressed_file; + } + + /* do the decompression, and cleanup */ + if ((bb_xread_char(src_fd) != 0x1f) || (bb_xread_char(src_fd) != 0x9d)) { + bb_error_msg_and_die("Invalid magic"); + } + + status = uncompress(src_fd, dst_fd); + + if ((status != EXIT_SUCCESS) && (uncompressed_file)) { + /* Unzip failed, remove the uncomressed file instead of compressed file */ + delete_path = uncompressed_file; + } + + if (dst_fd != STDOUT_FILENO) { + close(dst_fd); + } + if (src_fd != STDIN_FILENO) { + close(src_fd); + } + + /* delete_path will be NULL if in test mode or from stdin */ + if (delete_path && (unlink(delete_path) == -1)) { + bb_error_msg_and_die("Couldn't remove %s", delete_path); + } + + free(uncompressed_file); + } + + return status; +} diff --git a/busybox/archival/unzip.c b/busybox/archival/unzip.c new file mode 100644 index 000000000..eea2f5438 --- /dev/null +++ b/busybox/archival/unzip.c @@ -0,0 +1,247 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini unzip implementation for busybox + * + * Copyright (C) 2001 by Laurence Anderson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* For reference see + * http://www.pkware.com/products/enterprise/white_papers/appnote.txt + * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip + */ + +/* TODO Endian issues, exclude, should we accept input from stdin ? */ + +#include +#include +#include +#include +#include +#include "unarchive.h" +#include "busybox.h" + +#define ZIP_FILEHEADER_MAGIC 0x04034b50 +#define ZIP_CDS_MAGIC 0x02014b50 +#define ZIP_CDS_END_MAGIC 0x06054b50 +#define ZIP_DD_MAGIC 0x08074b50 + +extern unsigned int gunzip_crc; +extern unsigned int gunzip_bytes_out; + +static void header_list_unzip(const file_header_t *file_header) +{ + printf(" inflating: %s\n", file_header->name); +} + +static void header_verbose_list_unzip(const file_header_t *file_header) +{ + unsigned int dostime = (unsigned int) file_header->mtime; + + /* can printf arguments cut of the decade component ? */ + unsigned short year = 1980 + ((dostime & 0xfe000000) >> 25); + while (year >= 100) { + year -= 100; + } + + printf("%9u %02u-%02u-%02u %02u:%02u %s\n", + (unsigned int) file_header->size, + (dostime & 0x01e00000) >> 21, + (dostime & 0x001f0000) >> 16, + year, + (dostime & 0x0000f800) >> 11, + (dostime & 0x000007e0) >> 5, + file_header->name); +} + +extern int unzip_main(int argc, char **argv) +{ + union { + unsigned char raw[26]; + struct { + unsigned short version; /* 0-1 */ + unsigned short flags; /* 2-3 */ + unsigned short method; /* 4-5 */ + unsigned short modtime; /* 6-7 */ + unsigned short moddate; /* 8-9 */ + unsigned int crc32 __attribute__ ((packed)); /* 10-13 */ + unsigned int cmpsize __attribute__ ((packed));; /* 14-17 */ + unsigned int ucmpsize __attribute__ ((packed));; /* 18-21 */ + unsigned short filename_len; /* 22-23 */ + unsigned short extra_len; /* 24-25 */ + } formated __attribute__ ((packed)); + } zip_header; + + archive_handle_t *archive_handle; + unsigned int total_size = 0; + unsigned int total_entries = 0; + char *base_dir = NULL; + int opt = 0; + + /* Initialise */ + archive_handle = init_handle(); + archive_handle->action_data = NULL; + archive_handle->action_header = header_list_unzip; + + while ((opt = getopt(argc, argv, "lnopqd:")) != -1) { + switch (opt) { + case 'l': /* list */ + archive_handle->action_header = header_verbose_list_unzip; + archive_handle->action_data = data_skip; + break; + case 'n': /* never overwright existing files */ + break; + case 'o': + archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL; + break; + case 'p': /* extract files to stdout */ + archive_handle->action_data = data_extract_to_stdout; + break; + case 'q': /* Extract files quietly */ + archive_handle->action_header = header_skip; + break; + case 'd': /* Extract files to specified base directory*/ + base_dir = optarg; + break; +#if 0 + case 'x': /* Exclude the specified files */ + archive_handle->filter = filter_accept_reject_list; + break; +#endif + default: + bb_show_usage(); + } + } + + if (argc == optind) { + bb_show_usage(); + } + + printf("Archive: %s\n", argv[optind]); + if (archive_handle->action_header == header_verbose_list_unzip) { + printf(" Length Date Time Name\n"); + printf(" -------- ---- ---- ----\n"); + } + + if (*argv[optind] == '-') { + archive_handle->src_fd = STDIN_FILENO; + archive_handle->seek = seek_by_char; + } else { + archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY); + } + + if ((base_dir) && (chdir(base_dir))) { + bb_perror_msg_and_die("Couldnt chdir"); + } + + while (optind < argc) { + archive_handle->filter = filter_accept_list; + archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind]); + optind++; + } + + while (1) { + unsigned int magic; + int dst_fd; + + /* TODO Endian issues */ + archive_xread_all(archive_handle, &magic, 4); + archive_handle->offset += 4; + + if (magic == ZIP_CDS_MAGIC) { + break; + } + else if (magic != ZIP_FILEHEADER_MAGIC) { + bb_error_msg_and_die("Invlaide zip magic"); + } + + /* Read the file header */ + archive_xread_all(archive_handle, zip_header.raw, 26); + archive_handle->offset += 26; + archive_handle->file_header->mode = S_IFREG | 0777; + + if (zip_header.formated.method != 8) { + bb_error_msg_and_die("Unsupported compression method %d\n", zip_header.formated.method); + } + + /* Read filename */ + archive_handle->file_header->name = xmalloc(zip_header.formated.filename_len + 1); + archive_xread_all(archive_handle, archive_handle->file_header->name, zip_header.formated.filename_len); + archive_handle->offset += zip_header.formated.filename_len; + archive_handle->file_header->name[zip_header.formated.filename_len] = '\0'; + + /* Skip extra header bits */ + archive_handle->file_header->size = zip_header.formated.extra_len; + data_skip(archive_handle); + archive_handle->offset += zip_header.formated.extra_len; + + /* Handle directories */ + archive_handle->file_header->mode = S_IFREG | 0777; + if (last_char_is(archive_handle->file_header->name, '/')) { + archive_handle->file_header->mode ^= S_IFREG; + archive_handle->file_header->mode |= S_IFDIR; + } + + /* Data section */ + archive_handle->file_header->size = zip_header.formated.cmpsize; + if (archive_handle->action_data) { + archive_handle->action_data(archive_handle); + } else { + dst_fd = bb_xopen(archive_handle->file_header->name, O_WRONLY | O_CREAT); + inflate_init(zip_header.formated.cmpsize); + inflate_unzip(archive_handle->src_fd, dst_fd); + close(dst_fd); + chmod(archive_handle->file_header->name, archive_handle->file_header->mode); + + /* Validate decompression - crc */ + if (zip_header.formated.crc32 != (gunzip_crc ^ 0xffffffffL)) { + bb_error_msg("Invalid compressed data--crc error"); + } + + /* Validate decompression - size */ + if (gunzip_bytes_out != zip_header.formated.ucmpsize) { + bb_error_msg("Invalid compressed data--length error"); + } + } + + /* local file descriptor section */ + archive_handle->offset += zip_header.formated.cmpsize; + /* This ISNT unix time */ + archive_handle->file_header->mtime = zip_header.formated.modtime | (zip_header.formated.moddate << 16); + archive_handle->file_header->size = zip_header.formated.ucmpsize; + total_size += archive_handle->file_header->size; + total_entries++; + + archive_handle->action_header(archive_handle->file_header); + + /* Data descriptor section */ + if (zip_header.formated.flags & 4) { + /* skip over duplicate crc, compressed size and uncompressed size */ + unsigned char data_description[12]; + archive_xread_all(archive_handle, data_description, 12); + archive_handle->offset += 12; + } + } + /* Central directory section */ + + if (archive_handle->action_header == header_verbose_list_unzip) { + printf(" -------- -------\n"); + printf("%9d %d files\n", total_size, total_entries); + } + + return(EXIT_SUCCESS); +} diff --git a/busybox/console-tools/Config.in b/busybox/console-tools/Config.in new file mode 100644 index 000000000..e261794ab --- /dev/null +++ b/busybox/console-tools/Config.in @@ -0,0 +1,68 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Console Utilities" + +config CONFIG_CHVT + bool "chvt" + default n + help + This program is used to change to another terminal. + Example: chvt 4 (change to terminal /dev/tty4) + +config CONFIG_CLEAR + bool "clear" + default n + help + This program clears the terminal screen. + +config CONFIG_DEALLOCVT + bool "deallocvt" + default n + help + This program deallocates unused virtual consoles. + +config CONFIG_DUMPKMAP + bool "dumpkmap" + default n + help + This program dumps the kernel's keyboard translation table to + stdout, in binary format. You can then use loadkmap to load it. + +config CONFIG_LOADFONT + bool "loadfont" + default n + help + This program loads a console font from standard input. + +config CONFIG_LOADKMAP + bool "loadkmap" + default n + help + This program loads a keyboard translation table from + standard input. + +config CONFIG_OPENVT + bool "openvt" + default n + help + This program is used to start a command on an unused + virtual terminal. + +config CONFIG_RESET + bool "reset" + default n + help + This program is used to reset the terminal screen, if it + gets messed up. + +config CONFIG_SETKEYCODES + bool "setkeycodes" + default n + help + This program loads entries into the kernel's scancode-to-keycode + map, allowing unusual keyboards to generate usable keycodes. + +endmenu diff --git a/busybox/console-tools/Makefile b/busybox/console-tools/Makefile new file mode 100644 index 000000000..42cf2c8c3 --- /dev/null +++ b/busybox/console-tools/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/console/tools +CONSOLETOOLS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/console-tools/Makefile.in b/busybox/console-tools/Makefile.in new file mode 100644 index 000000000..b19ce5cb2 --- /dev/null +++ b/busybox/console-tools/Makefile.in @@ -0,0 +1,44 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +CONSOLETOOLS_AR:=console-tools.a +ifndef $(CONSOLETOOLS_DIR) +CONSOLETOOLS_DIR:=$(top_builddir)/console-tools/ +endif +srcdir=$(top_srcdir)/console-tools + +CONSOLETOOLS_DIR-y:= +CONSOLETOOLS_DIR-$(CONFIG_CHVT) += chvt.o +CONSOLETOOLS_DIR-$(CONFIG_CLEAR) += clear.o +CONSOLETOOLS_DIR-$(CONFIG_DEALLOCVT) += deallocvt.o +CONSOLETOOLS_DIR-$(CONFIG_DUMPKMAP) += dumpkmap.o +CONSOLETOOLS_DIR-$(CONFIG_LOADFONT) += loadfont.o +CONSOLETOOLS_DIR-$(CONFIG_LOADKMAP) += loadkmap.o +CONSOLETOOLS_DIR-$(CONFIG_OPENVT) += openvt.o +CONSOLETOOLS_DIR-$(CONFIG_RESET) += reset.o +CONSOLETOOLS_DIR-$(CONFIG_SETKEYCODES) += setkeycodes.o + +libraries-y+=$(CONSOLETOOLS_DIR)$(CONSOLETOOLS_AR) + +$(CONSOLETOOLS_DIR)$(CONSOLETOOLS_AR): $(patsubst %,$(CONSOLETOOLS_DIR)%, $(CONSOLETOOLS_DIR-y)) + $(AR) -ro $@ $(patsubst %,$(CONSOLETOOLS_DIR)%, $(CONSOLETOOLS_DIR-y)) + +$(CONSOLETOOLS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/console-tools/chvt.c b/busybox/console-tools/chvt.c new file mode 100644 index 000000000..3398892f5 --- /dev/null +++ b/busybox/console-tools/chvt.c @@ -0,0 +1,61 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chvt implementation for busybox + * + * Copyright (C) 1999-2004 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 + */ + +/* no options, no getopt */ + +#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) { + bb_show_usage(); + } + + fd = get_console_fd(); + num = bb_xgetlarg(argv[1], 10, 0, INT_MAX); + if (ioctl(fd, VT_ACTIVATE, num)) { + bb_perror_msg_and_die("VT_ACTIVATE"); + } + if (ioctl(fd, VT_WAITACTIVE, num)) { + bb_perror_msg_and_die("VT_WAITACTIVE"); + } + return EXIT_SUCCESS; +} + + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/console-tools/clear.c b/busybox/console-tools/clear.c new file mode 100644 index 000000000..e43ed0e02 --- /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-2004 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 + * + */ + +/* no options, no getopt */ + +#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..08a9d2122 --- /dev/null +++ b/busybox/console-tools/deallocvt.c @@ -0,0 +1,56 @@ +/* vi: set sw=4 ts=4: */ +/* + * Disallocate virtual terminal(s) + * + * Copyright (C) 2003 by Tito Ragusa + * Copyright (C) 1999-2004 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 + */ + +/* no options, no getopt */ + +#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[]) +{ + /* num = 0 deallocate all unused consoles */ + int num = 0; + + switch(argc) + { + case 2: + if((num = bb_xgetlarg(argv[1], 10, 0, INT_MAX)) == 0) + bb_error_msg_and_die("0: illegal VT number"); + /* Falltrough */ + case 1: + break; + default: + bb_show_usage(); + } + + if (ioctl( get_console_fd(), VT_DISALLOCATE, num )) { + bb_perror_msg_and_die("VT_DISALLOCATE"); + } + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/dumpkmap.c b/busybox/console-tools/dumpkmap.c new file mode 100644 index 000000000..6085a446b --- /dev/null +++ b/busybox/console-tools/dumpkmap.c @@ -0,0 +1,91 @@ +/* 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]=='-') { + bb_show_usage(); + } + + fd=bb_xopen(CURRENT_VC, O_RDWR); + + write(1, magic, 7); + + for (i=0; i < MAX_NR_KEYMAPS; i++) flags[i]=0; + flags[0]=1; + flags[1]=1; + flags[2]=1; + flags[4]=1; + flags[5]=1; + flags[6]=1; + flags[8]=1; + flags[9]=1; + flags[10]=1; + flags[12]=1; + + /* dump flags */ + for (i=0; i < MAX_NR_KEYMAPS; i++) write(1,&flags[i],1); + + for (i = 0; i < MAX_NR_KEYMAPS; i++) { + if (flags[i] == 1) { + for (j = 0; j < NR_KEYS; j++) { + ke.kb_index = j; + ke.kb_table = i; + if (ioctl(fd, KDGKBENT, &ke) < 0) { + + bb_error_msg("ioctl returned: %m, %s, %s, %xqq", (char *)&ke.kb_index,(char *)&ke.kb_table,(int)&ke.kb_value); + } + else { + write(1,(void*)&ke.kb_value,2); + } + + } + } + } + close(fd); + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/loadfont.c b/busybox/console-tools/loadfont.c new file mode 100644 index 000000000..4580dc4e0 --- /dev/null +++ b/busybox/console-tools/loadfont.c @@ -0,0 +1,207 @@ +/* vi: set sw=4 ts=4: */ +/* + * loadfont.c - Eugene Crosser & Andries Brouwer + * + * Version 0.96bb + * + * Loads the console font, and possibly the corresponding screen map(s). + * (Adapted for busybox by Matej Vela.) + */ +#include +#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) + bb_show_usage(); + + fd = bb_xopen(CURRENT_VC, O_RDWR); + loadnewfont(fd); + + return EXIT_SUCCESS; +} + +static void do_loadfont(int fd, char *inbuf, int unit, int fontsize) +{ + char buf[16384]; + int i; + + memset(buf, 0, sizeof(buf)); + + if (unit < 1 || unit > 32) + bb_error_msg_and_die("Bad character size %d", unit); + + for (i = 0; i < fontsize; i++) + memcpy(buf + (32 * i), inbuf + (unit * i), unit); + +#if defined( PIO_FONTX ) && !defined( __sparc__ ) + { + struct consolefontdesc cfd; + + cfd.charcount = fontsize; + cfd.charheight = unit; + cfd.chardata = buf; + + if (ioctl(fd, PIO_FONTX, &cfd) == 0) + return; /* success */ + bb_perror_msg("PIO_FONTX ioctl error (trying PIO_FONT)"); + } +#endif + if (ioctl(fd, PIO_FONT, buf)) + bb_perror_msg_and_die("PIO_FONT ioctl error"); +} + +static void +do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize) +{ + struct unimapinit advice; + struct unimapdesc ud; + struct unipair *up; + int ct = 0, maxct; + int glyph; + u_short unicode; + + maxct = tailsz; /* more than enough */ + up = (struct unipair *) xmalloc(maxct * sizeof(struct unipair)); + + for (glyph = 0; glyph < fontsize; glyph++) { + while (tailsz >= 2) { + unicode = (((u_short) inbuf[1]) << 8) + inbuf[0]; + tailsz -= 2; + inbuf += 2; + if (unicode == PSF_SEPARATOR) + break; + up[ct].unicode = unicode; + up[ct].fontpos = glyph; + ct++; + } + } + + /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP + this printf did not work on many kernels */ + + advice.advised_hashsize = 0; + advice.advised_hashstep = 0; + advice.advised_hashlevel = 0; + if (ioctl(fd, PIO_UNIMAPCLR, &advice)) { +#ifdef ENOIOCTLCMD + if (errno == ENOIOCTLCMD) { + bb_error_msg("It seems this kernel is older than 1.1.92"); + bb_error_msg_and_die("No Unicode mapping table loaded."); + } else +#endif + bb_perror_msg_and_die("PIO_UNIMAPCLR"); + } + ud.entry_ct = ct; + ud.entries = up; + if (ioctl(fd, PIO_UNIMAP, &ud)) { +#if 0 + if (errno == ENOMEM) { + /* change advice parameters */ + } +#endif + bb_perror_msg_and_die("PIO_UNIMAP"); + } +} + +static void loadnewfont(int fd) +{ + int unit; + char inbuf[32768]; /* primitive */ + unsigned int inputlth, offset; + + /* + * We used to look at the length of the input file + * with stat(); now that we accept compressed files, + * just read the entire file. + */ + inputlth = fread(inbuf, 1, sizeof(inbuf), stdin); + if (ferror(stdin)) + bb_perror_msg_and_die("Error reading input font"); + /* use malloc/realloc in case of giant files; + maybe these do not occur: 16kB for the font, + and 16kB for the map leaves 32 unicode values + for each font position */ + if (!feof(stdin)) + bb_perror_msg_and_die("Font too large"); + + /* test for psf first */ + { + struct psf_header psfhdr; + int fontsize; + int hastable; + unsigned int head0, head; + + if (inputlth < sizeof(struct psf_header)) + goto no_psf; + + psfhdr = *(struct psf_header *) &inbuf[0]; + + if (!PSF_MAGIC_OK(psfhdr)) + goto no_psf; + + if (psfhdr.mode > PSF_MAXMODE) + bb_error_msg_and_die("Unsupported psf file mode"); + fontsize = ((psfhdr.mode & PSF_MODE512) ? 512 : 256); +#if !defined( PIO_FONTX ) || defined( __sparc__ ) + if (fontsize != 256) + bb_error_msg_and_die("Only fontsize 256 supported"); +#endif + hastable = (psfhdr.mode & PSF_MODEHASTAB); + unit = psfhdr.charsize; + head0 = sizeof(struct psf_header); + + head = head0 + fontsize * unit; + if (head > inputlth || (!hastable && head != inputlth)) + bb_error_msg_and_die("Input file: bad length"); + do_loadfont(fd, inbuf + head0, unit, fontsize); + if (hastable) + do_loadtable(fd, inbuf + head, inputlth - head, fontsize); + return; + } + no_psf: + + /* file with three code pages? */ + if (inputlth == 9780) { + offset = 40; + unit = 16; + } else { + /* bare font */ + if (inputlth & 0377) + bb_error_msg_and_die("Bad input file size"); + offset = 0; + unit = inputlth / 256; + } + do_loadfont(fd, inbuf + offset, unit, 256); +} diff --git a/busybox/console-tools/loadkmap.c b/busybox/console-tools/loadkmap.c new file mode 100644 index 000000000..849d747a6 --- /dev/null +++ b/busybox/console-tools/loadkmap.c @@ -0,0 +1,82 @@ +/* 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; +}; +/* sets one entry in translation table */ +#define KDSKBENT 0x4B47 + +/* From */ +#define NR_KEYS 128 +#define MAX_NR_KEYMAPS 256 + +int loadkmap_main(int argc, char **argv) +{ + struct kbentry ke; + int i, j, fd; + u_short ibuff[NR_KEYS]; + char flags[MAX_NR_KEYMAPS]; + char buff[7]; + + if (argc != 1) + bb_show_usage(); + + fd = bb_xopen(CURRENT_VC, O_RDWR); + + if ((bb_full_read(0, buff, 7) != 7) || (strncmp(buff, BINARY_KEYMAP_MAGIC, 7) != 0)) + bb_error_msg_and_die("This is not a valid binary keymap."); + + if (bb_full_read(0, flags, MAX_NR_KEYMAPS) != MAX_NR_KEYMAPS) + bb_perror_msg_and_die("Error reading keymap flags"); + + for (i = 0; i < MAX_NR_KEYMAPS; i++) { + if (flags[i] == 1) { + bb_full_read(0, ibuff, NR_KEYS * sizeof(u_short)); + for (j = 0; j < NR_KEYS; j++) { + ke.kb_index = j; + ke.kb_table = i; + ke.kb_value = ibuff[j]; + ioctl(fd, KDSKBENT, &ke); + } + } + } + + /* Don't bother to close files. Exit does that + * automagically, so we can save a few bytes */ + /* close(fd); */ + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/openvt.c b/busybox/console-tools/openvt.c new file mode 100644 index 000000000..5f244579c --- /dev/null +++ b/busybox/console-tools/openvt.c @@ -0,0 +1,84 @@ +/* vi: set sw=4 ts=4: */ +/* + * openvt.c - open a vt to run a command. + * + * busyboxed by Quy Tonthat + * hacked by Tito + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include + +#include "busybox.h" + +int openvt_main(int argc, char **argv) +{ + int fd; + char vtname[sizeof VC_FORMAT + 2]; + + + if (argc < 3) + bb_show_usage(); + + /* check for Illegal vt number: < 1 or > 12 */ + sprintf(vtname, VC_FORMAT,(int)bb_xgetlarg(argv[1], 10, 1, 12)); + + argv+=2; + argc-=2; + + if(fork() == 0) { + /* leave current vt */ + +#ifdef ESIX_5_3_2_D + if (setpgrp() < 0) { +#else + if (setsid() < 0) { +#endif + + bb_perror_msg_and_die("Unable to set new session"); + } + close(0); /* so that new vt becomes stdin */ + + /* and grab new one */ + fd = bb_xopen(vtname, O_RDWR); + + /* Reassign stdout and sterr */ + close(1); + close(2); + dup(fd); + dup(fd); + + execvp(argv[0], argv); + _exit(1); + } + return EXIT_SUCCESS; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/console-tools/reset.c b/busybox/console-tools/reset.c new file mode 100644 index 000000000..9d38e7a28 --- /dev/null +++ b/busybox/console-tools/reset.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini reset implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * 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 + * + */ + +/* no options, no getopt */ + +#include +#include +#include +#include "busybox.h" + +extern int reset_main(int argc, char **argv) +{ + if (isatty(0) || isatty(0) || isatty(0)) { + /* See 'man 4 console_codes' for details: + * "ESC c" -- Reset + * "ESC ( K" -- Select user mapping + * "ESC [ J" -- Erase display + * "ESC [ 0 m" -- Reset all Graphics Rendition display attributes + * "ESC [ ? 25 h" -- Make cursor visible. + */ + printf("\033c\033(K\033[J\033[0m\033[?25h"); + } + return EXIT_SUCCESS; +} + diff --git a/busybox/console-tools/setkeycodes.c b/busybox/console-tools/setkeycodes.c new file mode 100644 index 000000000..169d0bb0a --- /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) { + bb_show_usage(); + } + + fd = get_console_fd(); + + while (argc > 2) { + a.keycode = atoi(argv[2]); + a.scancode = sc = strtol(argv[1], &ep, 16); + if (*ep) { + bb_error_msg_and_die("error reading SCANCODE: '%s'", argv[1]); + } + if (a.scancode > 127) { + a.scancode -= 0xe000; + a.scancode += 128; + } + if (a.scancode > 255 || a.keycode > 127) { + bb_error_msg_and_die("SCANCODE or KEYCODE outside bounds"); + } + if (ioctl(fd,KDSETKEYCODE,&a)) { + perror("KDSETKEYCODE"); + bb_error_msg_and_die("failed to set SCANCODE %x to KEYCODE %d", sc, a.keycode); + } + argc -= 2; + argv += 2; + } + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/Config.in b/busybox/coreutils/Config.in new file mode 100644 index 000000000..e1f0516fd --- /dev/null +++ b/busybox/coreutils/Config.in @@ -0,0 +1,613 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Coreutils" + +config CONFIG_BASENAME + bool "basename" + default n + help + basename is used to strip the directory and suffix from filenames, + leaving just the filename itself. Enable this option if you wish + to enable the 'basename' utility. + +config CONFIG_CAL + bool "cal" + default n + help + cal is used to display a monthly calender. + +config CONFIG_CAT + bool "cat" + default n + help + cat is used to concatenate files and print them to the standard + output. Enable this option if you wish to enable the 'cat' utility. + +config CONFIG_CHGRP + bool "chgrp" + default n + help + chgrp is used to change the group ownership of files. + +config CONFIG_CHMOD + bool "chmod" + default n + help + chmod is used to change the access permission of files. + +config CONFIG_CHOWN + bool "chown" + default n + help + chown is used to change the user and/or group ownership + of files. + +config CONFIG_CHROOT + bool "chroot" + default n + help + chroot is used to change the root directory and run a command. + The default command is `/bin/sh'. + +config CONFIG_CMP + bool "cmp" + default n + help + cmp is used to compare two files and returns the result + to standard output. + +config CONFIG_CP + bool "cp" + default n + help + cp is used to copy files and directories. + +config CONFIG_CUT + bool "cut" + default n + help + cut is used to print selected parts of lines from + each file to stdout. + +if CONFIG_WATCH + config CONFIG_DATE + default y + comment "date (forced enabled for use with watch)" +endif + +if !CONFIG_WATCH + config CONFIG_DATE + bool "date" + default n + help + date is used to set the system date or display the + current time in the given format. +endif + +config CONFIG_FEATURE_DATE_ISOFMT + bool " Enable ISO date format output (-I)" + default y + depends on CONFIG_DATE + help + Enable option (-I) to output an ISO-8601 compliant + date/time string. + +config CONFIG_DD + bool "dd" + default n + help + dd copies a file (from standard input to standard output, + by default) using specific input and output blocksizes, + while optionally performing conversions on it. + +config CONFIG_DF + bool "df" + default n + help + df reports the amount of disk space used and available + on filesystems. + +config CONFIG_DIRNAME + bool "dirname" + default n + help + dirname is used to strip a non-directory suffix from + a file name. + +config CONFIG_DOS2UNIX + bool "dos2unix/unix2dos" + default n + help + dos2unix is used to convert a text file from DOS format to + UNIX format, and vice versa. + +config CONFIG_UNIX2DOS + bool + default y + depends on CONFIG_DOS2UNIX + +config CONFIG_DU + bool "du (default blocksize of 512 bytes)" + default n + help + du is used to report the amount of disk space used + for specified files. + +config CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K + bool " Use a default blocksize of 1024 bytes (1K)" + default y + depends on CONFIG_DU + help + Use a blocksize of (1K) instead of the default 512b. + +config CONFIG_ECHO + bool "echo (basic SUSv3 version taking no options)" + default n + help + echo is used to print a specified string to stdout. + +config CONFIG_FEATURE_FANCY_ECHO + bool " Enable echo options (-n and -e)" + default y + depends on CONFIG_ECHO + help + This adds options (-n and -e) to echo. + +config CONFIG_ENV + bool "env" + default n + help + env is used to set an environment variable and run + a command; without options it displays the current + environment. + +config CONFIG_EXPR + bool "expr" + default n + help + expr is used to calculate numbers and print the result + to standard output. + +if CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH + config CONFIG_FALSE + default y + comment "false (forced enabled for use with shell)" +endif + +if !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH + config CONFIG_FALSE + bool "false" + default n + help + false returns an exit code of FALSE (1). +endif + +config CONFIG_FOLD + bool "fold" + default n + help + Wrap text to fit a specific width. + +config CONFIG_HEAD + bool "head" + default n + help + head is used to print the first specified number of lines + from files. + +config CONFIG_FEATURE_FANCY_HEAD + bool " Enable head options (-c, -q, and -v)" + default n + depends on CONFIG_HEAD + help + This enables the head options (-c, -q, and -v). + +config CONFIG_HOSTID + bool "hostid" + default n + help + hostid prints the numeric identifier (in hexadecimal) for + the current host. + +config CONFIG_ID + bool "id" + default n + help + id displays the current user and group ID names. + +config CONFIG_INSTALL + bool "install" + default n + help + Copy files and set attributes. + +config CONFIG_LENGTH + bool "length" + default n + help + length is used to print out the length of a specified string. + +config CONFIG_LN + bool "ln" + default n + help + ln is used to create hard or soft links between files. + +config CONFIG_LOGNAME + bool "logname" + default n + help + logname is used to print the current user's login name. + +config CONFIG_LS + bool "ls" + default n + help + ls is used to list the contents of directories. + +config CONFIG_FEATURE_LS_FILETYPES + bool " Enable filetyping options (-p and -F)" + default y + depends on CONFIG_LS + help + Enable the ls options (-p and -F). + +config CONFIG_FEATURE_LS_FOLLOWLINKS + bool " Enable symlinks dereferencing (-L)" + default y + depends on CONFIG_LS + help + Enable the ls option (-L). + +config CONFIG_FEATURE_LS_RECURSIVE + bool " Enable recursion (-R)" + default y + depends on CONFIG_LS + help + Enable the ls option (-R). + +config CONFIG_FEATURE_LS_SORTFILES + bool " Sort the file names" + default y + depends on CONFIG_LS + help + Allow ls to sort file names alphabetically. + +config CONFIG_FEATURE_LS_TIMESTAMPS + bool " Show file timestamps" + default y + depends on CONFIG_LS + help + Allow ls to display timestamps for files. + +config CONFIG_FEATURE_LS_USERNAME + bool " Show username/groupnames" + default y + depends on CONFIG_LS + help + Allow ls to display username/groupname for files. + +config CONFIG_FEATURE_LS_COLOR + bool " Use color to identify file types" + default y + depends on CONFIG_LS + help + Allow ls to use color when displaying files. + +config CONFIG_MD5SUM + bool "md5sum" + default n + help + md5sum is used to print or check MD5 checksums. + +config CONFIG_MKDIR + bool "mkdir" + default n + help + mkdir is used to create directories with the specified names. + +config CONFIG_MKFIFO + bool "mkfifo" + default n + help + mkfifo is used to create FIFOs (named pipes). + The `mknod' program can also create FIFOs. + +config CONFIG_MKNOD + bool "mknod" + default n + help + mknod is used to create FIFOs or block/character special + files with the specified names. + +config CONFIG_MV + bool "mv" + default n + help + mv is used to move or rename files or directories. + +config CONFIG_OD + bool "od" + default n + help + od is used to dump binary files in octal and other formats. + +config CONFIG_PRINTF + bool "printf" + default n + help + printf is used to format and print specified strings. + It's similar to `echo' except it has more options. + +config CONFIG_PWD + bool "pwd" + default n + help + pwd is used to print the current directory. + +config CONFIG_REALPATH + bool "realpath" + default n + help + Return the canonicalized absolute pathname. + This isn't provided by GNU shellutils, but where else does it belong. + +config CONFIG_RM + bool "rm" + default n + help + rm is used to remove files or directories. + +config CONFIG_RMDIR + bool "rmdir" + default n + help + rmdir is used to remove empty directories. + +config CONFIG_SEQ + bool "seq" + default n + help + print a sequence of numbers + +config CONFIG_SHA1SUM + bool "sha1sum" + default n + help + Compute and check SHA1 message digest + +config CONFIG_SLEEP + bool "sleep (single integer arg with no suffix)" + default n + help + sleep is used to pause for a specified number of seconds, + +config CONFIG_FEATURE_FANCY_SLEEP + bool " Enable multiple integer args and optional time suffixes" + default n + depends on CONFIG_SLEEP + help + Allow sleep to pause for specified minutes, hours, and days. + +config CONFIG_SORT + bool "sort" + default n + help + sort is used to sort lines of text in specified files. + +config CONFIG_STTY + bool "stty" + default n + help + stty is used to change and print terminal line settings. + +config CONFIG_SYNC + bool "sync" + default n + help + sync is used to flush filesystem buffers. + +config CONFIG_TAIL + bool "tail" + default n + help + tail is used to print the last specified number of lines + from files. + +config CONFIG_FEATURE_FANCY_TAIL + bool " Enable extra tail options (-q, -s, and -v)" + default y + depends on CONFIG_TAIL + help + The options (-q, -s, and -v) are provided by GNU tail, but + are not specific in the SUSv3 standard. + +config CONFIG_TEE + bool "tee" + default n + help + tee is used to read from standard input and write + to standard output and files. + +config CONFIG_FEATURE_TEE_USE_BLOCK_IO + bool " Enable block i/o (larger/faster) instead of byte i/o." + default n + depends on CONFIG_TEE + help + Enable this option for a faster tee, at expense of size. + +if CONFIG_ASH || CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH + config CONFIG_TEST + default y + comment "test (forced enabled for use with shell)" +endif + +if !CONFIG_ASH && !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH + config CONFIG_TEST + bool "test" + default n + help + test is used to check file types and compare values, + returning an appropriate exit code. The shells (ash + and bash) have test builtin. +endif + +config CONFIG_FEATURE_TEST_64 + bool " Extend test to 64 bit" + default n + depends on CONFIG_TEST + help + Enable 64-bit support in test. + +config CONFIG_TOUCH + bool "touch" + default n + help + touch is used to create or change the access and/or + modification timestamp of specified files. + +config CONFIG_TR + bool "tr" + default n + help + tr is used to squeeze, and/or delete characters from standard + input, writing to standard output. + +if CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH + config CONFIG_TRUE + default y + comment "true (forced enabled for use with shell)" +endif + +if !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH + config CONFIG_TRUE + bool "true" + default n + help + true returns an exit code of TRUE (0). + +endif + +config CONFIG_TTY + bool "tty" + default n + help + tty is used to print the name of the current terminal to + standard output. + +config CONFIG_UNAME + bool "uname" + default n + help + uname is used to print system information. + +config CONFIG_UNIQ + bool "uniq" + default n + help + uniq is used to remove duplicate lines from a sorted file. + +config CONFIG_USLEEP + bool "usleep" + default n + help + usleep is used to pause for a specified number of microseconds. + +config CONFIG_UUDECODE + bool "uudecode" + default n + help + uudecode is used to decode a uuencoded file. + +config CONFIG_UUENCODE + bool "uuencode" + default n + help + uuencode is used to uuencode a file. + +config CONFIG_WATCH + bool "watch" + default n + help + watch is used to execute a program periodically, showing + output to the screen. + +config CONFIG_WC + bool "wc" + default n + help + wc is used to print the number of bytes, words, and lines, + in specified files. + +config CONFIG_WHO + bool "who" + default n + select CONFIG_FEATURE_U_W_TMP + help + who is used to show who is logged on. + +config CONFIG_WHOAMI + bool "whoami" + default n + help + whoami is used to print the username of the current + user id (same as id -un). + +config CONFIG_YES + bool "yes" + default n + help + yes is used to repeatedly output a specific string, or + the default string `y'. + +comment "Common options for cp and mv" + depends on CONFIG_CP || CONFIG_MV + +config CONFIG_FEATURE_PRESERVE_HARDLINKS + bool " Preserve hard links" + default n + depends on CONFIG_CP || CONFIG_MV + help + Allow cp and mv to preserve hard links. + +comment "Common options for ls and more" + depends on CONFIG_LS || CONFIG_MORE + +config CONFIG_FEATURE_AUTOWIDTH + bool " Calculate terminal & column widths" + default y + depends on CONFIG_LS || CONFIG_MORE + help + This option allows utilities such as 'ls' and 'more' to determine the + width of the screen, which can allow them to display additional text + or avoid wrapping text onto the next line. If you leave this + disabled, your utilities will be especially primitive and will be + unable to determine the current screen width. + +comment "Common options for df, du, ls" + depends on CONFIG_DF || CONFIG_DU || CONFIG_LS + +config CONFIG_FEATURE_HUMAN_READABLE + bool " Support for human readable output (example 13k, 23M, 235G)" + default n + depends on CONFIG_DF || CONFIG_DU || CONFIG_LS + help + Allow df, du, and ls to have human readable output. + +comment "Common options for md5sum, sha1sum" + depends on CONFIG_MD5SUM || CONFIG_SHA1SUM + +config CONFIG_FEATURE_MD5_SHA1_SUM_CHECK + bool " Enable -c, -s and -w options" + default n + depends on CONFIG_MD5SUM || CONFIG_SHA1SUM + help + Enabling the -c options allows files to be checked + against pre-calculated hash values. + + -s and -w are useful options when verifying checksums. + +endmenu diff --git a/busybox/coreutils/Makefile b/busybox/coreutils/Makefile new file mode 100644 index 000000000..50fdac236 --- /dev/null +++ b/busybox/coreutils/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/coreutils +SHELLUTILS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/coreutils/Makefile.in b/busybox/coreutils/Makefile.in new file mode 100644 index 000000000..aacb813b3 --- /dev/null +++ b/busybox/coreutils/Makefile.in @@ -0,0 +1,98 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +COREUTILS_AR:=coreutils.a +ifndef $(COREUTILS_DIR) +COREUTILS_DIR:=$(top_builddir)/coreutils/ +endif +srcdir=$(top_srcdir)/coreutils + +COREUTILS-y:= +COREUTILS-$(CONFIG_BASENAME) += basename.o +COREUTILS-$(CONFIG_CAL) += cal.o +COREUTILS-$(CONFIG_CAT) += cat.o +COREUTILS-$(CONFIG_CHGRP) += chgrp.o +COREUTILS-$(CONFIG_CHMOD) += chmod.o +COREUTILS-$(CONFIG_CHOWN) += chown.o +COREUTILS-$(CONFIG_CHROOT) += chroot.o +COREUTILS-$(CONFIG_CMP) += cmp.o +COREUTILS-$(CONFIG_CP) += cp.o +COREUTILS-$(CONFIG_CUT) += cut.o +COREUTILS-$(CONFIG_DATE) += date.o +COREUTILS-$(CONFIG_DD) += dd.o +COREUTILS-$(CONFIG_DF) += df.o +COREUTILS-$(CONFIG_DIRNAME) += dirname.o +COREUTILS-$(CONFIG_DOS2UNIX) += dos2unix.o +COREUTILS-$(CONFIG_DU) += du.o +COREUTILS-$(CONFIG_ECHO) += echo.o +COREUTILS-$(CONFIG_ENV) += env.o +COREUTILS-$(CONFIG_EXPR) += expr.o +COREUTILS-$(CONFIG_FALSE) += false.o +COREUTILS-$(CONFIG_FOLD) += fold.o +COREUTILS-$(CONFIG_HEAD) += head.o +COREUTILS-$(CONFIG_HOSTID) += hostid.o +COREUTILS-$(CONFIG_ID) += id.o +COREUTILS-$(CONFIG_INSTALL) += install.o +COREUTILS-$(CONFIG_LENGTH) += length.o +COREUTILS-$(CONFIG_LN) += ln.o +COREUTILS-$(CONFIG_LOGNAME) += logname.o +COREUTILS-$(CONFIG_LS) += ls.o +COREUTILS-$(CONFIG_MD5SUM) += md5_sha1_sum.o +COREUTILS-$(CONFIG_MKDIR) += mkdir.o +COREUTILS-$(CONFIG_MKFIFO) += mkfifo.o +COREUTILS-$(CONFIG_MKNOD) += mknod.o +COREUTILS-$(CONFIG_MV) += mv.o +COREUTILS-$(CONFIG_OD) += od.o +COREUTILS-$(CONFIG_PRINTF) += printf.o +COREUTILS-$(CONFIG_PWD) += pwd.o +COREUTILS-$(CONFIG_REALPATH) += realpath.o +COREUTILS-$(CONFIG_RM) += rm.o +COREUTILS-$(CONFIG_RMDIR) += rmdir.o +COREUTILS-$(CONFIG_SEQ) += seq.o +COREUTILS-$(CONFIG_SHA1SUM) += md5_sha1_sum.o +COREUTILS-$(CONFIG_SLEEP) += sleep.o +COREUTILS-$(CONFIG_SORT) += sort.o +COREUTILS-$(CONFIG_STTY) += stty.o +COREUTILS-$(CONFIG_SYNC) += sync.o +COREUTILS-$(CONFIG_TAIL) += tail.o +COREUTILS-$(CONFIG_TEE) += tee.o +COREUTILS-$(CONFIG_TEST) += test.o +COREUTILS-$(CONFIG_TOUCH) += touch.o +COREUTILS-$(CONFIG_TR) += tr.o +COREUTILS-$(CONFIG_TRUE) += true.o +COREUTILS-$(CONFIG_TTY) += tty.o +COREUTILS-$(CONFIG_UNAME) += uname.o +COREUTILS-$(CONFIG_UNIQ) += uniq.o +COREUTILS-$(CONFIG_USLEEP) += usleep.o +COREUTILS-$(CONFIG_UUDECODE) += uudecode.o +COREUTILS-$(CONFIG_UUENCODE) += uuencode.o +COREUTILS-$(CONFIG_WATCH) += watch.o +COREUTILS-$(CONFIG_WC) += wc.o +COREUTILS-$(CONFIG_WHO) += who.o +COREUTILS-$(CONFIG_WHOAMI) += whoami.o +COREUTILS-$(CONFIG_YES) += yes.o + +libraries-y+=$(COREUTILS_DIR)$(COREUTILS_AR) + +$(COREUTILS_DIR)$(COREUTILS_AR): $(patsubst %,$(COREUTILS_DIR)%, $(COREUTILS-y)) + $(AR) -ro $@ $(patsubst %,$(COREUTILS_DIR)%, $(COREUTILS-y)) + +$(COREUTILS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/coreutils/basename.c b/busybox/coreutils/basename.c new file mode 100644 index 000000000..7b8b7b6f0 --- /dev/null +++ b/busybox/coreutils/basename.c @@ -0,0 +1,62 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini basename implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */ + + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Changes: + * 1) Now checks for too many args. Need at least one and at most two. + * 2) Don't check for options, as per SUSv3. + * 3) Save some space by using strcmp(). Calling strncmp() here was silly. + */ + +#include +#include +#include +#include "busybox.h" + +extern int basename_main(int argc, char **argv) +{ + size_t m, n; + char *s; + + if (((unsigned int)(argc-2)) >= 2) { + bb_show_usage(); + } + + s = bb_get_last_path_component(*++argv); + + if (*++argv) { + n = strlen(*argv); + m = strlen(s); + if ((m > n) && ((strcmp)(s+m-n, *argv) == 0)) { + s[m-n] = '\0'; + } + } + + puts(s); + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/cal.c b/busybox/coreutils/cal.c new file mode 100644 index 000000000..93c5692d0 --- /dev/null +++ b/busybox/coreutils/cal.c @@ -0,0 +1,379 @@ +/* + * Calendar implementation for busybox + * + * See original copyright at the end of this file + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ + +/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */ +/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream + * BB_AUDIT BUG: version in util-linux seems to be broken as well. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Major size reduction... over 50% (>1.5k) on i386. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +#ifdef CONFIG_LOCALE_SUPPORT +#include +#endif + +#define THURSDAY 4 /* for reformation */ +#define SATURDAY 6 /* 1 Jan 1 was a Saturday */ + +#define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */ +#define NUMBER_MISSING_DAYS 11 /* 11 day correction */ + +#define MAXDAYS 42 /* max slots in a month array */ +#define SPACE -1 /* used in day array */ + +static const char days_in_month[] = { + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static const char sep1752[] = { + 1, 2, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30 +}; + +static int julian; + +/* leap year -- account for Gregorian reformation in 1752 */ +#define leap_year(yr) \ + ((yr) <= 1752 ? !((yr) % 4) : \ + (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400)) + +static int is_leap_year(int year) +{ + return leap_year(year); +} +#undef leap_year +#define leap_year(yr) is_leap_year(yr) + +/* number of centuries since 1700, not inclusive */ +#define centuries_since_1700(yr) \ + ((yr) > 1700 ? (yr) / 100 - 17 : 0) + +/* number of centuries since 1700 whose modulo of 400 is 0 */ +#define quad_centuries_since_1700(yr) \ + ((yr) > 1600 ? ((yr) - 1600) / 400 : 0) + +/* number of leap years between year 1 and this year, not inclusive */ +#define leap_years_since_year_1(yr) \ + ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr)) + +static void center __P((char *, int, int)); +static void day_array __P((int, int, int *)); +static void trim_trailing_spaces_and_print __P((char *)); + +static void blank_string(char *buf, size_t buflen); +static char *build_row(char *p, int *dp); + +#define DAY_LEN 3 /* 3 spaces per day */ +#define J_DAY_LEN (DAY_LEN + 1) +#define WEEK_LEN 20 /* 7 * 3 - one space at the end */ +#define J_WEEK_LEN (WEEK_LEN + 7) +#define HEAD_SEP 2 /* spaces between day headings */ + +int cal_main(int argc, char **argv) +{ + struct tm *local_time; + struct tm zero_tm; + time_t now; + int month, year, flags, i; + char *month_names[12]; + char day_headings[28]; /* 28 for julian, 21 for nonjulian */ + char buf[40]; + +#ifdef CONFIG_LOCALE_SUPPORT + setlocale(LC_TIME, ""); +#endif + + flags = bb_getopt_ulflags(argc, argv, "jy"); + + julian = flags & 1; + + argv += optind; + + month = 0; + + if ((argc -= optind) > 2) { + bb_show_usage(); + } + + if (!argc) { + time(&now); + local_time = localtime(&now); + year = local_time->tm_year + 1900; + if (!(flags & 2)) { + month = local_time->tm_mon + 1; + } + } else { + if (argc == 2) { + month = bb_xgetularg10_bnd(*argv++, 1, 12); + } + year = bb_xgetularg10_bnd(*argv, 1, 9999); + } + + blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian); + + i = 0; + do { + zero_tm.tm_mon = i; + strftime(buf, sizeof(buf), "%B", &zero_tm); + month_names[i] = bb_xstrdup(buf); + + if (i < 7) { + zero_tm.tm_wday = i; + strftime(buf, sizeof(buf), "%a", &zero_tm); + strncpy(day_headings + i * (3+julian) + julian, buf, 2); + } + } while (++i < 12); + + if (month) { + int row, len, days[MAXDAYS]; + int *dp = days; + char lineout[30]; + + day_array(month, year, dp); + len = sprintf(lineout, "%s %d", month_names[month - 1], year); + bb_printf("%*s%s\n%s\n", + ((7*julian + WEEK_LEN) - len) / 2, "", + lineout, day_headings); + for (row = 0; row < 6; row++) { + build_row(lineout, dp)[0] = '\0'; + dp += 7; + trim_trailing_spaces_and_print(lineout); + } + } else { + int row, which_cal, week_len, days[12][MAXDAYS]; + int *dp; + char lineout[80]; + + sprintf(lineout, "%d", year); + center(lineout, + (WEEK_LEN * 3 + HEAD_SEP * 2) + + julian * (J_WEEK_LEN * 2 + HEAD_SEP + - (WEEK_LEN * 3 + HEAD_SEP * 2)), + 0); + puts("\n"); /* two \n's */ + for (i = 0; i < 12; i++) { + day_array(i + 1, year, days[i]); + } + blank_string(lineout, sizeof(lineout)); + week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN); + for (month = 0; month < 12; month += 3-julian) { + center(month_names[month], week_len, HEAD_SEP); + if (!julian) { + center(month_names[month + 1], week_len, HEAD_SEP); + } + center(month_names[month + 2 - julian], week_len, 0); + bb_printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings); + if (!julian) { + bb_printf("%*s%s", HEAD_SEP, "", day_headings); + } + putchar('\n'); + for (row = 0; row < (6*7); row += 7) { + for (which_cal = 0; which_cal < 3-julian; which_cal++) { + dp = days[month + which_cal] + row; + build_row(lineout + which_cal * (week_len + 2), dp); + } + /* blank_string took care of nul termination. */ + trim_trailing_spaces_and_print(lineout); + } + } + } + + bb_fflush_stdout_and_exit(0); +} + +/* + * day_array -- + * Fill in an array of 42 integers with a calendar. Assume for a moment + * that you took the (maximum) 6 rows in a calendar and stretched them + * out end to end. You would have 42 numbers or spaces. This routine + * builds that array for any month from Jan. 1 through Dec. 9999. + */ +static void day_array(int month, int year, int *days) +{ + long temp; + int i; + int j_offset; + int day, dw, dm; + + memset(days, SPACE, MAXDAYS * sizeof(int)); + + if ((month == 9) && (year == 1752)) { + j_offset = julian * 244; + i = 0; + do { + days[i+2] = sep1752[i] + j_offset; + } while (++i < sizeof(sep1752)); + + return; + } + + /* day_in_year + * return the 1 based day number within the year + */ + day = 1; + if ((month > 2) && leap_year(year)) { + ++day; + } + + i = month; + while (i) { + day += days_in_month[--i]; + } + + /* day_in_week + * return the 0 based day number for any date from 1 Jan. 1 to + * 31 Dec. 9999. Assumes the Gregorian reformation eliminates + * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all + * missing days. + */ + dw = THURSDAY; + temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + + day; + if (temp < FIRST_MISSING_DAY) { + dw = ((temp - 1 + SATURDAY) % 7); + } else if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) { + dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); + } + + if (!julian) { + day = 1; + } + + dm = days_in_month[month]; + if ((month == 2) && leap_year(year)) { + ++dm; + } + + while (dm) { + days[dw++] = day++; + --dm; + } +} + +static void trim_trailing_spaces_and_print(char *s) +{ + char *p = s; + + while (*p) { + ++p; + } + while (p > s) { + --p; + if (!(isspace)(*p)) { /* We want the function... not the inline. */ + p[1] = '\0'; + break; + } + } + + puts(s); +} + +static void center(char *str, int len, int separate) +{ + int n = strlen(str); + len -= n; + bb_printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, ""); +} + +static void blank_string(char *buf, size_t buflen) +{ + memset(buf, ' ', buflen); + buf[buflen-1] = '\0'; +} + +static char *build_row(char *p, int *dp) +{ + int col, val, day; + + memset(p, ' ', (julian + DAY_LEN) * 7); + + col = 0; + do { + if ((day = *dp++) != SPACE) { + if (julian) { + ++p; + if (day >= 100) { + *p = '0'; + p[-1] = (day / 100) + '0'; + day %= 100; + } + } + if ((val = day / 10) > 0) { + *p = val + '0'; + } + *++p = day % 10 + '0'; + p += 2; + } else { + p += DAY_LEN + julian; + } + } while (++col < 7); + + return p; +} + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kim Letkeman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + diff --git a/busybox/coreutils/cat.c b/busybox/coreutils/cat.c new file mode 100644 index 000000000..62af6c5d5 --- /dev/null +++ b/busybox/coreutils/cat.c @@ -0,0 +1,67 @@ +/* vi: set sw=4 ts=4: */ +/* + * cat implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * This is a new implementation of 'cat' which aims to be SUSv3 compliant. + * + * Changes from the previous implementation include: + * 1) Multiple '-' args are accepted as required by SUSv3. The previous + * implementation would close stdin and segfault on a subsequent '-'. + * 2) The '-u' options is required by SUSv3. Note that the specified + * behavior for '-u' is done by default, so all we need do is accept + * the option. + */ + +#include +#include +#include +#include "busybox.h" + +extern int cat_main(int argc, char **argv) +{ + FILE *f; + int retval = EXIT_SUCCESS; + + bb_getopt_ulflags(argc, argv, "u"); + + argv += optind; + if (!*argv) { + *--argv = "-"; + } + + do { + if ((f = bb_wfopen_input(*argv)) != NULL) { + int r = bb_copyfd_eof(fileno(f), STDOUT_FILENO); + bb_fclose_nonstdin(f); + if (r >= 0) { + continue; + } + } + retval = EXIT_FAILURE; + } while (*++argv); + + return retval; +} diff --git a/busybox/coreutils/chgrp.c b/busybox/coreutils/chgrp.c new file mode 100644 index 000000000..8cfb54241 --- /dev/null +++ b/busybox/coreutils/chgrp.c @@ -0,0 +1,81 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chgrp implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 defects - unsupported options -h, -H, -L, and -P. */ +/* BB_AUDIT GNU defects - unsupported options -h, -c, -f, -v, and long options. */ +/* BB_AUDIT Note: gnu chgrp does not support -H, -L, or -P. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */ + +#include +#include +#include "busybox.h" + +/* Don't use lchown glibc older then 2.1.x */ +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) +#define lchown chown +#endif + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +{ + if (lchown(fileName, statbuf->st_uid, *((long *) junk)) == 0) { + return (TRUE); + } + bb_perror_msg("%s", fileName); /* Avoid multibyte problems. */ + return (FALSE); +} + +int chgrp_main(int argc, char **argv) +{ + long gid; + int recursiveFlag; + int retval = EXIT_SUCCESS; + + recursiveFlag = bb_getopt_ulflags(argc, argv, "R"); + + if (argc - optind < 2) { + bb_show_usage(); + } + + argv += optind; + + /* Find the selected group */ + gid = get_ug_id(*argv, my_getgrnam); + ++argv; + + /* Ok, ready to do the deed now */ + do { + if (! recursive_action (*argv, recursiveFlag, FALSE, FALSE, + fileAction, fileAction, &gid)) { + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/chmod.c b/busybox/coreutils/chmod.c new file mode 100644 index 000000000..0cb888628 --- /dev/null +++ b/busybox/coreutils/chmod.c @@ -0,0 +1,112 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chmod implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Reworked by (C) 2002 Vladimir Oleynik + * to correctly parse '-rwxgoa' + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU defects - unsupported options -c, -f, -v, and long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +{ + if (!bb_parse_mode((char *)junk, &(statbuf->st_mode))) + bb_error_msg_and_die( "invalid mode: %s", (char *)junk); + if (chmod(fileName, statbuf->st_mode) == 0) + return (TRUE); + bb_perror_msg("%s", fileName); /* Avoid multibyte problems. */ + return (FALSE); +} + +int chmod_main(int argc, char **argv) +{ + int retval = EXIT_SUCCESS; + int recursiveFlag = FALSE; + int count; + char *smode; + char **p; + char *p0; + char opt = '-'; + + ++argv; + count = 0; + + for (p = argv ; *p ; p++) { + p0 = p[0]; + if (p0[0] == opt) { + if ((p0[1] == '-') && !p0[2]) { + opt = 0; /* Disable further option processing. */ + continue; + } + if (p0[1] == 'R') { + char *s = p0 + 2; + while (*s == 'R') { + ++s; + } + if (*s) { + bb_show_usage(); + } + recursiveFlag = TRUE; + continue; + } + if (count) { + bb_show_usage(); + } + } + argv[count] = p0; + ++count; + } + + argv[count] = NULL; + + if (count < 2) { + bb_show_usage(); + } + + smode = *argv; + ++argv; + + /* Ok, ready to do the deed now */ + do { + if (! recursive_action (*argv, recursiveFlag, FALSE, FALSE, + fileAction, fileAction, smode)) { + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/chown.c b/busybox/coreutils/chown.c new file mode 100644 index 000000000..638745f17 --- /dev/null +++ b/busybox/coreutils/chown.c @@ -0,0 +1,105 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 defects - unsupported options -h, -H, -L, and -P. */ +/* BB_AUDIT GNU defects - unsupported options -h, -c, -f, -v, and long options. */ +/* BB_AUDIT Note: gnu chown does not support -H, -L, or -P. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */ + +#include +#include +#include +#include "busybox.h" + +/* Don't use lchown for glibc older then 2.1.x */ +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1) +#define lchown chown +#endif + +static long uid; +static long gid; + +static int (*chown_func)(const char *, uid_t, gid_t) = chown; + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +{ + if (chown_func(fileName, uid, (gid == -1) ? statbuf->st_gid : gid) == 0) { + chmod(fileName, statbuf->st_mode); + return (TRUE); + } + bb_perror_msg("%s", fileName); /* Avoid multibyte problems. */ + return (FALSE); +} + +#define FLAG_R 1 +#define FLAG_h 2 + +int chown_main(int argc, char **argv) +{ + int flags; + int retval = EXIT_SUCCESS; + char *groupName; + + flags = bb_getopt_ulflags(argc, argv, "Rh"); + + if (flags & FLAG_h) chown_func = lchown; + + if (argc - optind < 2) { + bb_show_usage(); + } + + argv += optind; + + /* First, check if there is a group name here */ + if ((groupName = strchr(*argv, '.')) == NULL) { + groupName = strchr(*argv, ':'); + } + + gid = -1; + if (groupName) { + *groupName++ = '\0'; + gid = get_ug_id(groupName, my_getgrnam); + } + + /* Now check for the username */ + uid = get_ug_id(*argv, my_getpwnam); + + ++argv; + + /* Ok, ready to do the deed now */ + do { + if (! recursive_action (*argv, (flags & FLAG_R), FALSE, FALSE, + fileAction, fileAction, NULL)) { + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/chroot.c b/busybox/coreutils/chroot.c new file mode 100644 index 000000000..62257021d --- /dev/null +++ b/busybox/coreutils/chroot.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chroot implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include +#include +#include +#include +#include "busybox.h" + +int chroot_main(int argc, char **argv) +{ + if (argc < 2) { + bb_show_usage(); + } + + ++argv; + if (chroot(*argv) || (chdir("/"))) { + bb_perror_msg_and_die("cannot change root directory to %s", *argv); + } + + ++argv; + if (argc == 2) { + argv -= 2; + if (!(*argv = getenv("SHELL"))) { + *argv = (char *) DEFAULT_SHELL; + } + argv[1] = (char *) "-i"; + } + + execvp(*argv, argv); + bb_perror_msg_and_die("cannot execute %s", *argv); +} diff --git a/busybox/coreutils/cmp.c b/busybox/coreutils/cmp.c new file mode 100644 index 000000000..d0fc662a5 --- /dev/null +++ b/busybox/coreutils/cmp.c @@ -0,0 +1,152 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cmp implementation for busybox + * + * Copyright (C) 2000,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 + * + */ + +/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Original version majorly reworked for SUSv3 compliance, bug fixes, and + * size optimizations. Changes include: + * 1) Now correctly distinguishes between errors and actual file differences. + * 2) Proper handling of '-' args. + * 3) Actual error checking of i/o. + * 4) Accept SUSv3 -l option. Note that we use the slightly nicer gnu format + * in the '-l' case. + */ + +#include +#include +#include +#include "busybox.h" + +static FILE *cmp_xfopen_input(const char *filename) +{ + FILE *fp; + + if ((fp = bb_wfopen_input(filename)) != NULL) { + return fp; + } + + exit(bb_default_error_retval); /* We already output an error message. */ +} + +static const char fmt_eof[] = "cmp: EOF on %s\n"; +static const char fmt_differ[] = "%s %s differ: char %d, line %d\n"; +#if 0 +static const char fmt_l_opt[] = "%.0s%.0s%d %o %o\n"; /* SUSv3 format */ +#else +static const char fmt_l_opt[] = "%.0s%.0s%d %3o %3o\n"; /* nicer gnu format */ +#endif + +static const char opt_chars[] = "sl"; + +enum { + OPT_s = 1, + OPT_l = 2 +}; + +int cmp_main(int argc, char **argv) +{ + FILE *fp1, *fp2, *outfile = stdout; + const char *filename1, *filename2; + const char *fmt; + int c1, c2, char_pos, line_pos; + int opt_flags; + int exit_val = 0; + + bb_default_error_retval = 2; /* 1 is returned if files are different. */ + + opt_flags = bb_getopt_ulflags(argc, argv, opt_chars); + + if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > 1)) { + bb_show_usage(); + } + + fp1 = cmp_xfopen_input(filename1 = *(argv += optind)); + + filename2 = "-"; + if (*++argv) { + filename2 = *argv; + } + fp2 = cmp_xfopen_input(filename2); + + if (fp1 == fp2) { /* Paranioa check... stdin == stdin? */ + /* Note that we don't bother reading stdin. Neither does gnu wc. + * But perhaps we should, so that other apps down the chain don't + * get the input. Consider 'echo hello | (cmp - - && cat -)'. + */ + return 0; + } + + fmt = fmt_differ; + if (opt_flags == OPT_l) { + fmt = fmt_l_opt; + } + + char_pos = 0; + line_pos = 1; + do { + c1 = getc(fp1); + c2 = getc(fp2); + ++char_pos; + if (c1 != c2) { /* Remember -- a read error may have occurred. */ + exit_val = 1; /* But assume the files are different for now. */ + if (c2 == EOF) { + /* We know that fp1 isn't at EOF or in an error state. But to + * save space below, things are setup to expect an EOF in fp1 + * if an EOF occurred. So, swap things around. + */ + fp1 = fp2; + filename1 = filename2; + c1 = c2; + } + if (c1 == EOF) { + bb_xferror(fp1, filename1); + fmt = fmt_eof; /* Well, no error, so it must really be EOF. */ + outfile = stderr; + /* There may have been output to stdout (option -l), so + * make sure we fflush before writing to stderr. */ + bb_xfflush_stdout(); + } + if (opt_flags != OPT_s) { + if (opt_flags == OPT_l) { + line_pos = c1; /* line_pos is unused in the -l case. */ + } + bb_fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2); + if (opt_flags) { /* This must be -l since not -s. */ + /* If we encountered and EOF, the while check will catch it. */ + continue; + } + } + break; + } + if (c1 == '\n') { + ++line_pos; + } + } while (c1 != EOF); + + bb_xferror(fp1, filename1); + bb_xferror(fp2, filename2); + + bb_fflush_stdout_and_exit(exit_val); +} diff --git a/busybox/coreutils/cp.c b/busybox/coreutils/cp.c new file mode 100644 index 000000000..6a82f6bff --- /dev/null +++ b/busybox/coreutils/cp.c @@ -0,0 +1,119 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 defects - unsupported options -H, -L, and -P. */ +/* BB_AUDIT GNU defects - only extension options supported are -a and -d. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +/* WARNING!! ORDER IS IMPORTANT!! */ +static const char cp_opts[] = "pdRfiar"; + +extern int cp_main(int argc, char **argv) +{ + struct stat source_stat; + struct stat dest_stat; + const char *last; + const char *dest; + int s_flags; + int d_flags; + int flags; + int status = 0; + + /* Since these are enums, #if tests will not work. So use assert()s. */ + assert(FILEUTILS_PRESERVE_STATUS == 1); + assert(FILEUTILS_DEREFERENCE == 2); + assert(FILEUTILS_RECUR == 4); + assert(FILEUTILS_FORCE == 8); + assert(FILEUTILS_INTERACTIVE == 16); + + flags = bb_getopt_ulflags(argc, argv, cp_opts); + + if (flags & 32) { + flags |= (FILEUTILS_PRESERVE_STATUS | FILEUTILS_RECUR | FILEUTILS_DEREFERENCE); + } + if (flags & 64) { + /* Make -r a synonym for -R, + * -r was marked as obsolete in SUSv3, but is included for compatability + */ + flags |= FILEUTILS_RECUR; + } + + flags ^= FILEUTILS_DEREFERENCE; /* The sense of this flag was reversed. */ + + if (optind + 2 > argc) { + bb_show_usage(); + } + + last = argv[argc - 1]; + argv += optind; + + /* If there are only two arguments and... */ + if (optind + 2 == argc) { + s_flags = cp_mv_stat2(*argv, &source_stat, + (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); + if ((s_flags < 0) || ((d_flags = cp_mv_stat(last, &dest_stat)) < 0)) { + exit(EXIT_FAILURE); + } + /* ...if neither is a directory or... */ + if ( !((s_flags | d_flags) & 2) || + /* ...recursing, the 1st is a directory, and the 2nd doesn't exist... */ + /* ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) */ + /* Simplify the above since FILEUTILS_RECUR >> 1 == 2. */ + ((((flags & FILEUTILS_RECUR) >> 1) & s_flags) && !d_flags) + ) { + /* ...do a simple copy. */ + dest = last; + goto DO_COPY; /* Note: optind+2==argc implies argv[1]==last below. */ + } + } + + do { + dest = concat_path_file(last, bb_get_last_path_component(*argv)); + DO_COPY: + if (copy_file(*argv, dest, flags) < 0) { + status = 1; + } + if (*++argv == last) { + break; + } + free((void *) dest); + } while (1); + + exit(status); +} diff --git a/busybox/coreutils/cut.c b/busybox/coreutils/cut.c new file mode 100644 index 000000000..d26e80eee --- /dev/null +++ b/busybox/coreutils/cut.c @@ -0,0 +1,344 @@ +/* vi: set sw=8 ts=8: */ +/* + * cut.c - minimalist version of cut + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + + +/* option vars */ +static const char optstring[] = "b:c:f:d:sn"; +#define OPT_BYTE_FLGS 1 +#define OPT_CHAR_FLGS 2 +#define OPT_FIELDS_FLGS 4 +#define OPT_DELIM_FLGS 8 +#define OPT_SUPRESS_FLGS 16 +static char part; /* (b)yte, (c)har, (f)ields */ +static unsigned int supress_non_delimited_lines; +static char delim = '\t'; /* delimiter, default is tab */ + +struct cut_list { + int startpos; + int endpos; +}; + +static const int BOL = 0; +static const int EOL = INT_MAX; +static const int NON_RANGE = -1; + +static struct cut_list *cut_lists = NULL; /* growable array holding a series of lists */ +static unsigned int nlists = 0; /* number of elements in above list */ + + +static int cmpfunc(const void *a, const void *b) +{ + struct cut_list *la = (struct cut_list *)a; + struct cut_list *lb = (struct cut_list *)b; + + if (la->startpos > lb->startpos) + return 1; + if (la->startpos < lb->startpos) + return -1; + return 0; +} + + +/* + * parse_lists() - parses a list and puts values into startpos and endpos. + * valid list formats: N, N-, N-M, -M + * more than one list can be separated by commas + */ +static void parse_lists(char *lists) +{ + char *ltok = NULL; + char *ntok = NULL; + char *junk; + int s = 0, e = 0; + + /* take apart the lists, one by one (they are separated with commas */ + while ((ltok = strsep(&lists, ",")) != NULL) { + + /* it's actually legal to pass an empty list */ + if (strlen(ltok) == 0) + continue; + + /* get the start pos */ + ntok = strsep(<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) + bb_error_msg_and_die("invalid byte or field list"); + + /* account for the fact that arrays are zero based, while the user + * expects the first char on the line to be char # 1 */ + if (s != 0) + s--; + } + + /* get the end pos */ + ntok = strsep(<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) + bb_error_msg_and_die("invalid byte or field list"); + /* if the user specified and end position of 0, that means "til the + * end of the line */ + if (e == 0) + e = INT_MAX; + e--; /* again, arrays are zero based, lines are 1 based */ + if (e == s) + e = NON_RANGE; + } + + /* if there's something left to tokenize, the user past an invalid list */ + if (ltok) + bb_error_msg_and_die("invalid byte or field list"); + + /* add the new list */ + cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists)); + cut_lists[nlists-1].startpos = s; + cut_lists[nlists-1].endpos = e; + } + + /* make sure we got some cut positions out of all that */ + if (nlists == 0) + bb_error_msg_and_die("missing list of positions"); + + /* now that the lists are parsed, we need to sort them to make life easier + * on us when it comes time to print the chars / fields / lines */ + qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc); + +} + + +static void cut_line_by_chars(const char *line) +{ + int c, l; + /* set up a list so we can keep track of what's been printed */ + char *printed = xcalloc(strlen(line), sizeof(char)); + + /* print the chars specified in each cut list */ + for (c = 0; c < nlists; c++) { + l = cut_lists[c].startpos; + while (l < strlen(line)) { + if (!printed[l]) { + putchar(line[l]); + printed[l] = 'X'; + } + l++; + if (cut_lists[c].endpos == NON_RANGE || l > cut_lists[c].endpos) + break; + } + } + putchar('\n'); /* cuz we were handed a chomped line */ + free(printed); +} + + +static void cut_line_by_fields(char *line) +{ + int c, f; + int ndelim = -1; /* zero-based / one-based problem */ + int nfields_printed = 0; + char *field = NULL; + char d[2] = { delim, 0 }; + char *printed; + + /* test the easy case first: does this line contain any delimiters? */ + if (strchr(line, delim) == NULL) { + if (!supress_non_delimited_lines) + puts(line); + return; + } + + /* set up a list so we can keep track of what's been printed */ + printed = xcalloc(strlen(line), sizeof(char)); + + /* process each list on this line, for as long as we've got a line to process */ + for (c = 0; c < nlists && line; c++) { + f = cut_lists[c].startpos; + do { + + /* find the field we're looking for */ + while (line && ndelim < f) { + field = strsep(&line, d); + ndelim++; + } + + /* we found it, and it hasn't been printed yet */ + if (field && ndelim == f && !printed[ndelim]) { + /* if this isn't our first time through, we need to print the + * delimiter after the last field that was printed */ + if (nfields_printed > 0) + putchar(delim); + fputs(field, stdout); + printed[ndelim] = 'X'; + nfields_printed++; + } + + f++; + + /* keep going as long as we have a line to work with, this is a + * list, and we're not at the end of that list */ + } while (line && cut_lists[c].endpos != NON_RANGE && f <= cut_lists[c].endpos); + } + + /* if we printed anything at all, we need to finish it with a newline cuz + * we were handed a chomped line */ + putchar('\n'); + + free(printed); +} + + +static void cut_file_by_lines(const char *line, unsigned int linenum) +{ + static int c = 0; + static int l = -1; + + /* I can't initialize this above cuz the "initializer isn't + * constant" *sigh* */ + if (l == -1) + l = cut_lists[c].startpos; + + /* get out if we have no more lists to process or if the lines are lower + * than what we're interested in */ + if (c >= nlists || linenum < l) + return; + + /* if the line we're looking for is lower than the one we were passed, it + * means we displayed it already, so move on */ + while (l < linenum) { + l++; + /* move on to the next list if we're at the end of this one */ + if (cut_lists[c].endpos == NON_RANGE || l > cut_lists[c].endpos) { + c++; + /* get out if there's no more lists to process */ + if (c >= nlists) + return; + l = cut_lists[c].startpos; + /* get out if the current line is lower than the one we just became + * interested in */ + if (linenum < l) + return; + } + } + + /* If we made it here, it means we've found the line we're looking for, so print it */ + puts(line); +} + + +/* + * snippy-snip + */ +static void cut_file(FILE *file) +{ + char *line = NULL; + unsigned int linenum = 0; /* keep these zero-based to be consistent */ + + /* go through every line in the file */ + while ((line = bb_get_chomped_line_from_file(file)) != NULL) { + + /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ + if ((part & (OPT_CHAR_FLGS | OPT_BYTE_FLGS))) + cut_line_by_chars(line); + + /* cut based on fields */ + else { + if (delim == '\n') + cut_file_by_lines(line, linenum); + else + cut_line_by_fields(line); + } + + linenum++; + free(line); + } +} + + +extern int cut_main(int argc, char **argv) +{ + unsigned long opt; + char *sopt, *sdopt; + + bb_opt_complementaly = "b~bcf:c~bcf:f~bcf"; + opt = bb_getopt_ulflags(argc, argv, optstring, &sopt, &sopt, &sopt, &sdopt); + part = opt & (OPT_BYTE_FLGS|OPT_CHAR_FLGS|OPT_FIELDS_FLGS); + if(part == 0) + bb_error_msg_and_die("you must specify a list of bytes, characters, or fields"); + if(opt & 0x80000000UL) + bb_error_msg_and_die("only one type of list may be specified"); + parse_lists(sopt); + if((opt & (OPT_DELIM_FLGS))) { + if (strlen(sdopt) > 1) { + bb_error_msg_and_die("the delimiter must be a single character"); + } + delim = sdopt[0]; + } + supress_non_delimited_lines = opt & OPT_SUPRESS_FLGS; + + /* non-field (char or byte) cutting has some special handling */ + if (part != OPT_FIELDS_FLGS) { + if (supress_non_delimited_lines) { + bb_error_msg_and_die("suppressing non-delimited lines makes sense" + " only when operating on fields"); + } + if (delim != '\t') { + bb_error_msg_and_die("a delimiter may be specified only when operating on fields"); + } + } + + /* argv[(optind)..(argc-1)] should be names of file to process. If no + * files were specified or '-' was specified, take input from stdin. + * Otherwise, we process all the files specified. */ + if (argv[optind] == NULL || (strcmp(argv[optind], "-") == 0)) { + cut_file(stdin); + } + else { + int i; + FILE *file; + for (i = optind; i < argc; i++) { + file = bb_wfopen(argv[i], "r"); + if(file) { + cut_file(file); + fclose(file); + } + } + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/date.c b/busybox/coreutils/date.c new file mode 100644 index 000000000..3608df69f --- /dev/null +++ b/busybox/coreutils/date.c @@ -0,0 +1,292 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini date implementation for busybox + * + * by Matthew Grant + * + * iso-format handling added by Robert Griebl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 surprising some people */ + +static struct tm *date_conv_time(struct tm *tm_time, const char *t_string) +{ + int nr; + char *cp; + + nr = sscanf(t_string, "%2d%2d%2d%2d%d", &(tm_time->tm_mon), + &(tm_time->tm_mday), &(tm_time->tm_hour), &(tm_time->tm_min), + &(tm_time->tm_year)); + + if (nr < 4 || nr > 5) { + bb_error_msg_and_die(bb_msg_invalid_date, t_string); + } + + cp = strchr(t_string, '.'); + if (cp) { + nr = sscanf(cp + 1, "%2d", &(tm_time->tm_sec)); + if (nr != 1) { + bb_error_msg_and_die(bb_msg_invalid_date, t_string); + } + } + + /* correct for century - minor Y2K problem here? */ + if (tm_time->tm_year >= 1900) { + tm_time->tm_year -= 1900; + } + /* adjust date */ + tm_time->tm_mon -= 1; + + return (tm_time); + +} + + +/* The new stuff for LRP */ + +static struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string) +{ + struct tm t; + + /* Parse input and assign appropriately to tm_time */ + + if (t = + *tm_time, sscanf(t_string, "%d:%d:%d", &t.tm_hour, &t.tm_min, + &t.tm_sec) == 3) { + /* no adjustments needed */ + } else if (t = + *tm_time, sscanf(t_string, "%d:%d", &t.tm_hour, + &t.tm_min) == 2) { + /* no adjustments needed */ + } else if (t = + *tm_time, sscanf(t_string, "%d.%d-%d:%d:%d", &t.tm_mon, + &t.tm_mday, &t.tm_hour, &t.tm_min, + &t.tm_sec) == 5) { + /* Adjust dates from 1-12 to 0-11 */ + t.tm_mon -= 1; + } else if (t = + *tm_time, sscanf(t_string, "%d.%d-%d:%d", &t.tm_mon, + &t.tm_mday, &t.tm_hour, &t.tm_min) == 4) { + /* Adjust dates from 1-12 to 0-11 */ + t.tm_mon -= 1; + } else if (t = + *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d:%d", &t.tm_year, + &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, + &t.tm_sec) == 6) { + t.tm_year -= 1900; /* Adjust years */ + t.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + } else if (t = + *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d", &t.tm_year, + &t.tm_mon, &t.tm_mday, &t.tm_hour, + &t.tm_min) == 5) { + t.tm_year -= 1900; /* Adjust years */ + t.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + } else { + bb_error_msg_and_die(bb_msg_invalid_date, t_string); + } + *tm_time = t; + return (tm_time); +} + +#define DATE_OPT_RFC2822 0x01 +#define DATE_OPT_SET 0x02 +#define DATE_OPT_UTC 0x04 +#define DATE_OPT_DATE 0x08 +#define DATE_OPT_REFERENCE 0x10 +#ifdef CONFIG_FEATURE_DATE_ISOFMT +# define DATE_OPT_TIMESPEC 0x20 +#endif + +int date_main(int argc, char **argv) +{ + char *date_str = NULL; + char *date_fmt = NULL; + char *t_buff; + int set_time; + int utc; + int use_arg = 0; + time_t tm; + unsigned long opt; + struct tm tm_time; + char *filename = NULL; + +#ifdef CONFIG_FEATURE_DATE_ISOFMT + int ifmt = 0; + char *isofmt_arg; + +# define GETOPT_ISOFMT "I::" +#else +# define GETOPT_ISOFMT +#endif + bb_opt_complementaly = "d~ds:s~ds"; + opt = bb_getopt_ulflags(argc, argv, "Rs:ud:r:" GETOPT_ISOFMT, + &date_str, &date_str, &filename +#ifdef CONFIG_FEATURE_DATE_ISOFMT + , &isofmt_arg +#endif + ); + set_time = opt & DATE_OPT_SET; + utc = opt & DATE_OPT_UTC; + if ((utc) && (putenv("TZ=UTC0") != 0)) { + bb_error_msg_and_die(bb_msg_memory_exhausted); + } + use_arg = opt & DATE_OPT_DATE; + if(opt & 0x80000000UL) + bb_show_usage(); +#ifdef CONFIG_FEATURE_DATE_ISOFMT + if(opt & DATE_OPT_TIMESPEC) { + if (!isofmt_arg) { + ifmt = 1; + } else { + int ifmt_len = bb_strlen(isofmt_arg); + + if ((ifmt_len <= 4) + && (strncmp(isofmt_arg, "date", ifmt_len) == 0)) { + ifmt = 1; + } else if ((ifmt_len <= 5) + && (strncmp(isofmt_arg, "hours", ifmt_len) == 0)) { + ifmt = 2; + } else if ((ifmt_len <= 7) + && (strncmp(isofmt_arg, "minutes", ifmt_len) == 0)) { + ifmt = 3; + } else if ((ifmt_len <= 7) + && (strncmp(isofmt_arg, "seconds", ifmt_len) == 0)) { + ifmt = 4; + } + } + if (!ifmt) { + bb_show_usage(); + } + } +#endif + + if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+')) { + date_fmt = &argv[optind][1]; /* Skip over the '+' */ + } else if (date_str == NULL) { + set_time = 1; + date_str = argv[optind]; + } + + /* Now we have parsed all the information except the date format + which depends on whether the clock is being set or read */ + + if(filename) { + struct stat statbuf; + if(stat(filename,&statbuf)) + bb_perror_msg_and_die("File '%s' not found.\n",filename); + tm=statbuf.st_mtime; + } else time(&tm); + memcpy(&tm_time, localtime(&tm), sizeof(tm_time)); + /* Zero out fields - take her back to midnight! */ + if (date_str != NULL) { + tm_time.tm_sec = 0; + tm_time.tm_min = 0; + tm_time.tm_hour = 0; + + /* Process any date input to UNIX time since 1 Jan 1970 */ + if (strchr(date_str, ':') != NULL) { + date_conv_ftime(&tm_time, date_str); + } else { + date_conv_time(&tm_time, date_str); + } + + /* Correct any day of week and day of year etc. fields */ + tm_time.tm_isdst = -1; /* Be sure to recheck dst. */ + tm = mktime(&tm_time); + if (tm < 0) { + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } + if (utc && (putenv("TZ=UTC0") != 0)) { + bb_error_msg_and_die(bb_msg_memory_exhausted); + } + + /* if setting time, set it */ + if (set_time && (stime(&tm) < 0)) { + bb_perror_msg("cannot set date"); + } + } + + /* Display output */ + + /* Deal with format string */ + if (date_fmt == NULL) { +#ifdef CONFIG_FEATURE_DATE_ISOFMT + switch (ifmt) { + case 4: + date_fmt = utc ? "%Y-%m-%dT%H:%M:%SZ" : "%Y-%m-%dT%H:%M:%S%z"; + break; + case 3: + date_fmt = utc ? "%Y-%m-%dT%H:%MZ" : "%Y-%m-%dT%H:%M%z"; + break; + case 2: + date_fmt = utc ? "%Y-%m-%dT%HZ" : "%Y-%m-%dT%H%z"; + break; + case 1: + date_fmt = "%Y-%m-%d"; + break; + case 0: + default: +#endif + date_fmt = (opt & DATE_OPT_RFC2822 ? + (utc ? "%a, %d %b %Y %H:%M:%S GMT" : + "%a, %d %b %Y %H:%M:%S %z") : + "%a %b %e %H:%M:%S %Z %Y"); + +#ifdef CONFIG_FEATURE_DATE_ISOFMT + break; + } +#endif + } else if (*date_fmt == '\0') { + /* Imitate what GNU 'date' does with NO format string! */ + printf("\n"); + return EXIT_SUCCESS; + } + + /* Handle special conversions */ + + if (strncmp(date_fmt, "%f", 2) == 0) { + date_fmt = "%Y.%m.%d-%H:%M:%S"; + } + + /* Print OUTPUT (after ALL that!) */ + t_buff = xmalloc(201); + strftime(t_buff, 200, date_fmt, &tm_time); + puts(t_buff); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/dd.c b/busybox/coreutils/dd.c new file mode 100644 index 000000000..9a149e24a --- /dev/null +++ b/busybox/coreutils/dd.c @@ -0,0 +1,203 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dd implementation for busybox + * + * + * Copyright (C) 2000,2001 Matt Kraai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#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) +{ + size_t out_full = 0; + size_t out_part = 0; + size_t in_full = 0; + size_t in_part = 0; + size_t count = -1; + size_t bs = 512; + ssize_t n; + off_t seek = 0; + off_t skip = 0; + int sync_flag = FALSE; + int noerror = FALSE; + int trunc_flag = TRUE; + int oflag; + int ifd; + int ofd; + int i; + const char *infile = NULL; + const char *outfile = NULL; + char *buf; + + for (i = 1; i < argc; i++) { + if (strncmp("bs=", argv[i], 3) == 0) + bs = bb_xparse_number(argv[i]+3, dd_suffixes); + else if (strncmp("count=", argv[i], 6) == 0) + count = bb_xparse_number(argv[i]+6, dd_suffixes); + else if (strncmp("seek=", argv[i], 5) == 0) + seek = bb_xparse_number(argv[i]+5, dd_suffixes); + else if (strncmp("skip=", argv[i], 5) == 0) + skip = bb_xparse_number(argv[i]+5, dd_suffixes); + else if (strncmp("if=", argv[i], 3) == 0) + infile = argv[i]+3; + else if (strncmp("of=", argv[i], 3) == 0) + outfile = argv[i]+3; + else if (strncmp("conv=", argv[i], 5) == 0) { + buf = argv[i]+5; + while (1) { + if (strncmp("notrunc", buf, 7) == 0) { + trunc_flag = FALSE; + buf += 7; + } else if (strncmp("sync", buf, 4) == 0) { + sync_flag = TRUE; + buf += 4; + } else if (strncmp("noerror", buf, 7) == 0) { + noerror = TRUE; + buf += 7; + } else { + bb_error_msg_and_die("invalid conversion `%s'", argv[i]+5); + } + if (buf[0] == '\0') + break; + if (buf[0] == ',') + buf++; + } + } else + bb_show_usage(); + } + + buf = xmalloc(bs); + + if (infile != NULL) { + ifd = bb_xopen(infile, O_RDONLY); + } else { + ifd = STDIN_FILENO; + infile = bb_msg_standard_input; + } + + if (outfile != NULL) { + oflag = O_WRONLY | O_CREAT; + + if (!seek && trunc_flag) { + oflag |= O_TRUNC; + } + + if ((ofd = open(outfile, oflag, 0666)) < 0) { + bb_perror_msg_and_die("%s", outfile); + } + + if (seek && trunc_flag) { + if (ftruncate(ofd, seek * bs) < 0) { + struct stat st; + + if (fstat (ofd, &st) < 0 || S_ISREG (st.st_mode) || + S_ISDIR (st.st_mode)) { + bb_perror_msg_and_die("%s", outfile); + } + } + } + } else { + ofd = STDOUT_FILENO; + outfile = bb_msg_standard_output; + } + + if (skip) { + if (lseek(ifd, skip * bs, SEEK_CUR) < 0) { + bb_perror_msg_and_die("%s", infile); + } + } + + if (seek) { + if (lseek(ofd, seek * bs, SEEK_CUR) < 0) { + bb_perror_msg_and_die("%s", outfile); + } + } + + while (in_full + in_part != count) { + if (noerror) { + /* Pre-zero the buffer when doing the noerror thing */ + memset(buf, '\0', bs); + } + n = safe_read(ifd, buf, bs); + if (n < 0) { + if (noerror) { + n = bs; + bb_perror_msg("%s", infile); + } else { + bb_perror_msg_and_die("%s", infile); + } + } + if (n == 0) { + break; + } + if (n == bs) { + in_full++; + } else { + in_part++; + } + if (sync_flag) { + memset(buf + n, '\0', bs - n); + n = bs; + } + n = bb_full_write(ofd, buf, n); + if (n < 0) { + bb_perror_msg_and_die("%s", outfile); + } + if (n == bs) { + out_full++; + } else { + out_part++; + } + } + + if (close (ifd) < 0) { + bb_perror_msg_and_die("%s", infile); + } + + if (close (ofd) < 0) { + bb_perror_msg_and_die("%s", outfile); + } + + fprintf(stderr, "%ld+%ld records in\n%ld+%ld records out\n", + (long)in_full, (long)in_part, + (long)out_full, (long)out_part); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/df.c b/busybox/coreutils/df.c new file mode 100644 index 000000000..ba2e7ccc9 --- /dev/null +++ b/busybox/coreutils/df.c @@ -0,0 +1,170 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini df implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- options -P and -t missing. Also blocksize. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. Removed floating point dependency. Added error checking + * on output. Output stats on 0-sized filesystems if specifically listed on + * the command line. Properly round *-blocks, Used, and Available quantities. + */ + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#ifndef CONFIG_FEATURE_HUMAN_READABLE +static long kscale(long b, long bs) +{ + return ( b * (long long) bs + KILOBYTE/2 ) / KILOBYTE; +} +#endif + +extern int df_main(int argc, char **argv) +{ + long blocks_used; + long blocks_percent_used; +#ifdef CONFIG_FEATURE_HUMAN_READABLE + unsigned long df_disp_hr = KILOBYTE; +#endif + int status = EXIT_SUCCESS; + unsigned long opt; + FILE *mount_table; + struct mntent *mount_entry; + struct statfs s; + static const char hdr_1k[] = "1k-blocks"; /* default display is kilobytes */ + const char *disp_units_hdr = hdr_1k; + +#ifdef CONFIG_FEATURE_HUMAN_READABLE + bb_opt_complementaly = "h-km:k-hm:m-hk"; + opt = bb_getopt_ulflags(argc, argv, "hmk"); + if(opt & 1) { + df_disp_hr = 0; + disp_units_hdr = " Size"; + } + if(opt & 2) { + df_disp_hr = MEGABYTE; + disp_units_hdr = "1M-blocks"; + } +#else + opt = bb_getopt_ulflags(argc, argv, "k"); +#endif + + bb_printf("Filesystem%11s%-15sUsed Available Use%% Mounted on\n", + "", disp_units_hdr); + + mount_table = NULL; + argv += optind; + if (optind >= argc) { + if (!(mount_table = setmntent(bb_path_mtab_file, "r"))) { + bb_perror_msg_and_die(bb_path_mtab_file); + } + } + + do { + const char *device; + const char *mount_point; + + if (mount_table) { + if (!(mount_entry = getmntent(mount_table))) { + endmntent(mount_table); + break; + } + } else { + if (!(mount_point = *argv++)) { + break; + } + if (!(mount_entry = find_mount_point(mount_point, bb_path_mtab_file))) { + bb_error_msg("%s: can't find mount point.", mount_point); + SET_ERROR: + status = EXIT_FAILURE; + continue; + } + } + + device = mount_entry->mnt_fsname; + mount_point = mount_entry->mnt_dir; + + if (statfs(mount_point, &s) != 0) { + bb_perror_msg("%s", mount_point); + goto SET_ERROR; + } + + if ((s.f_blocks > 0) || !mount_table){ + blocks_used = s.f_blocks - s.f_bfree; + blocks_percent_used = 0; + if (blocks_used + s.f_bavail) { + blocks_percent_used = (((long long) blocks_used) * 100 + + (blocks_used + s.f_bavail)/2 + ) / (blocks_used + s.f_bavail); + } + + if (strcmp(device, "rootfs") == 0) { + continue; + } else if (strcmp(device, "/dev/root") == 0) { + /* Adjusts device to be the real root device, + * or leaves device alone if it can't find it */ + if ((device = find_real_root_device_name()) == NULL) { + goto SET_ERROR; + } + } + +#ifdef CONFIG_FEATURE_HUMAN_READABLE + bb_printf("%-21s%9s ", device, + make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr)); + + bb_printf("%9s ", + make_human_readable_str( (s.f_blocks - s.f_bfree), + s.f_bsize, df_disp_hr)); + + bb_printf("%9s %3ld%% %s\n", + make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), + blocks_percent_used, mount_point); +#else + bb_printf("%-21s%9ld %9ld %9ld %3ld%% %s\n", + device, + kscale(s.f_blocks, s.f_bsize), + kscale(s.f_blocks-s.f_bfree, s.f_bsize), + kscale(s.f_bavail, s.f_bsize), + blocks_percent_used, mount_point); +#endif + } + + } while (1); + + bb_fflush_stdout_and_exit(status); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/coreutils/dirname.c b/busybox/coreutils/dirname.c new file mode 100644 index 000000000..5136e4909 --- /dev/null +++ b/busybox/coreutils/dirname.c @@ -0,0 +1,39 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dirname implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */ + +#include +#include +#include "busybox.h" + +extern int dirname_main(int argc, char **argv) +{ + if (argc != 2) { + bb_show_usage(); + } + + puts(dirname(argv[1])); + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/dos2unix.c b/busybox/coreutils/dos2unix.c new file mode 100644 index 000000000..df0b4f977 --- /dev/null +++ b/busybox/coreutils/dos2unix.c @@ -0,0 +1,198 @@ +/* + * dos2unix for BusyBox + * + * dos2unix '\n' convertor 0.5.0 + * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997) + * Copyright 1997,.. by Peter Hanecak . + * 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 +#include "busybox.h" + +#define CT_AUTO 0 +#define CT_UNIX2DOS 1 +#define CT_DOS2UNIX 2 + +/* We are making a lame pseudo-random string generator here. in + * convert(), each pass through the while loop will add more and more + * stuff into value, which is _supposed_ to wrap. We don't care about + * it being accurate. We care about it being messy, since we then mod + * it by the sizeof(letters) and then use that as an index into letters + * to pick a random letter to add to out temporary file. */ +typedef unsigned long int bb_uint64_t; + +static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +// if fn is NULL then input is stdin and output is stdout +static int convert(char *fn, int ConvType) +{ + int c, fd; + struct timeval tv; + char tempFn[BUFSIZ]; + static bb_uint64_t value=0; + FILE *in = stdin, *out = stdout; + + if (fn != NULL) { + in = bb_xfopen(fn, "rw"); + safe_strncpy(tempFn, fn, sizeof(tempFn)); + c = strlen(tempFn); + tempFn[c] = '.'; + while(1) { + /* tempFn is BUFSIZ so the last addressable spot it BUFSIZ-1. + * The loop increments by 2. So this must check for BUFSIZ-3. */ + if (c >=BUFSIZ-3) + bb_error_msg_and_die("unique name not found"); + /* Get some semi random stuff to try and make a + * random filename based (and in the same dir as) + * the input file... */ + gettimeofday (&tv, NULL); + value += ((bb_uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid (); + tempFn[++c] = letters[value % 62]; + tempFn[c+1] = '\0'; + value /= 62; + + if ((fd = open(tempFn, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0 ) { + continue; + } + out = fdopen(fd, "w+"); + if (!out) { + close(fd); + remove(tempFn); + continue; + } + break; + } + } + + while ((c = fgetc(in)) != EOF) { + if (c == '\r') { + if ((ConvType == CT_UNIX2DOS) && (fn != NULL)) { + // file is alredy in DOS format so it is not necessery to touch it + remove(tempFn); + if (fclose(in) < 0 || fclose(out) < 0) { + bb_perror_nomsg(); + return -2; + } + return 0; + } + if (!ConvType) + ConvType = CT_DOS2UNIX; + break; + } + if (c == '\n') { + if ((ConvType == CT_DOS2UNIX) && (fn != NULL)) { + // file is alredy in UNIX format so it is not necessery to touch it + remove(tempFn); + if ((fclose(in) < 0) || (fclose(out) < 0)) { + bb_perror_nomsg(); + return -2; + } + return 0; + } + if (!ConvType) { + ConvType = CT_UNIX2DOS; + } + if (ConvType == CT_UNIX2DOS) { + fputc('\r', out); + } + fputc('\n', out); + break; + } + fputc(c, out); + } + if (c != EOF) + while ((c = fgetc(in)) != EOF) { + if (c == '\r') + continue; + if (c == '\n') { + if (ConvType == CT_UNIX2DOS) + fputc('\r', out); + fputc('\n', out); + continue; + } + fputc(c, out); + } + + if (fn != NULL) { + if (fclose(in) < 0 || fclose(out) < 0) { + bb_perror_nomsg(); + remove(tempFn); + return -2; + } + + /* Assume they are both on the same filesystem (which + * should be true since we put them into the same directory + * so we _should_ be ok, but you never know... */ + if (rename(tempFn, fn) < 0) { + bb_perror_msg("unable to rename '%s' as '%s'", tempFn, fn); + return -1; + } + } + + return 0; +} + +int dos2unix_main(int argc, char *argv[]) +{ + int ConvType = CT_AUTO; + int o; + + //See if we are supposed to be doing dos2unix or unix2dos + if (argv[0][0]=='d') { + ConvType = CT_DOS2UNIX; + } + if (argv[0][0]=='u') { + ConvType = CT_UNIX2DOS; + } + + // process parameters + while ((o = getopt(argc, argv, "du")) != EOF) { + switch (o) { + case 'd': + ConvType = CT_UNIX2DOS; + break; + case 'u': + ConvType = CT_DOS2UNIX; + break; + default: + bb_show_usage(); + } + } + + if (optind < argc) { + while(optind < argc) + if ((o = convert(argv[optind++], ConvType)) < 0) + break; + } + else + o = convert(NULL, ConvType); + + return o; +} + diff --git a/busybox/coreutils/du.c b/busybox/coreutils/du.c new file mode 100644 index 000000000..bfa44034a --- /dev/null +++ b/busybox/coreutils/du.c @@ -0,0 +1,269 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini du implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * Copyright (C) 2002 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 + * + */ + +/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Mostly rewritten for SUSv3 compliance and to fix bugs/defects. + * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options. + * The -d option allows setting of max depth (similar to gnu --max-depth). + * 2) Fixed incorrect size calculations for links and directories, especially + * when errors occurred. Calculates sizes should now match gnu du output. + * 3) Added error checking of output. + * 4) Fixed busybox bug #1284 involving long overflow with human_readable. + */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +#ifdef CONFIG_FEATURE_HUMAN_READABLE +# ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K +static unsigned long disp_hr = KILOBYTE; +# else +static unsigned long disp_hr = 512; +# endif +#elif defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K +static unsigned int disp_k = 1; +#else +static unsigned int disp_k; /* bss inits to 0 */ +#endif + +static int max_print_depth = INT_MAX; +static int count_hardlinks = 1; + +static int status +#if EXIT_SUCCESS == 0 + = EXIT_SUCCESS +#endif + ; +static int print_files; +static int slink_depth; +static int du_depth; +static int one_file_system; +static dev_t dir_dev; + + +static void print(long size, char *filename) +{ + /* TODO - May not want to defer error checking here. */ +#ifdef CONFIG_FEATURE_HUMAN_READABLE + bb_printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr), + filename); +#else + if (disp_k) { + size++; + size >>= 1; + } + bb_printf("%ld\t%s\n", size, filename); +#endif +} + +/* tiny recursive du */ +static long du(char *filename) +{ + struct stat statbuf; + long sum; + + if ((lstat(filename, &statbuf)) != 0) { + bb_perror_msg("%s", filename); + status = EXIT_FAILURE; + return 0; + } + + if (one_file_system) { + if (du_depth == 0) { + dir_dev = statbuf.st_dev; + } else if (dir_dev != statbuf.st_dev) { + return 0; + } + } + + sum = statbuf.st_blocks; + + if (S_ISLNK(statbuf.st_mode)) { + if (slink_depth > du_depth) { /* -H or -L */ + if ((stat(filename, &statbuf)) != 0) { + bb_perror_msg("%s", filename); + status = EXIT_FAILURE; + return 0; + } + sum = statbuf.st_blocks; + if (slink_depth == 1) { + slink_depth = INT_MAX; /* Convert -H to -L. */ + } + } + } + + if (statbuf.st_nlink > count_hardlinks) { + /* Add files/directories with links only once */ + if (is_in_ino_dev_hashtable(&statbuf, NULL)) { + return 0; + } + add_to_ino_dev_hashtable(&statbuf, NULL); + } + + if (S_ISDIR(statbuf.st_mode)) { + DIR *dir; + struct dirent *entry; + char *newfile; + + dir = opendir(filename); + if (!dir) { + bb_perror_msg("%s", filename); + status = EXIT_FAILURE; + return sum; + } + + newfile = last_char_is(filename, '/'); + if (newfile) + *newfile = '\0'; + + while ((entry = readdir(dir))) { + char *name = entry->d_name; + + newfile = concat_subpath_file(filename, name); + if(newfile == NULL) + continue; + ++du_depth; + sum += du(newfile); + --du_depth; + free(newfile); + } + closedir(dir); + } else if (du_depth > print_files) { + return sum; + } + if (du_depth <= max_print_depth) { + print(sum, filename); + } + return sum; +} + +int du_main(int argc, char **argv) +{ + long total; + int slink_depth_save; + int print_final_total; + char *smax_print_depth; + unsigned long opt; + +#ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K + if (getenv("POSIXLY_CORRECT")) { /* TODO - a new libbb function? */ +#ifdef CONFIG_FEATURE_HUMAN_READABLE + disp_hr = 512; +#else + disp_k = 0; +#endif + } +#endif + + /* Note: SUSv3 specifies that -a and -s options can not be used together + * in strictly conforming applications. However, it also says that some + * du implementations may produce output when -a and -s are used together. + * gnu du exits with an error code in this case. We choose to simply + * ignore -a. This is consistent with -s being equivalent to -d 0. + */ +#ifdef CONFIG_FEATURE_HUMAN_READABLE + bb_opt_complementaly = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s"; + opt = bb_getopt_ulflags(argc, argv, "aHkLsx" "d:" "lc" "hm", &smax_print_depth); + if((opt & (1 << 9))) { + /* -h opt */ + disp_hr = 0; + } + if((opt & (1 << 10))) { + /* -m opt */ + disp_hr = MEGABYTE; + } + if((opt & (1 << 2))) { + /* -k opt */ + disp_hr = KILOBYTE; + } +#else + bb_opt_complementaly = "H-L:L-H:s-d:d-s"; + opt = bb_getopt_ulflags(argc, argv, "aHkLsx" "d:" "lc", &smax_print_depth); +#if !defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K + if((opt & (1 << 2))) { + /* -k opt */ + disp_k = 1; + } +#endif +#endif + if((opt & (1 << 0))) { + /* -a opt */ + print_files = INT_MAX; + } + if((opt & (1 << 1))) { + /* -H opt */ + slink_depth = 1; + } + if((opt & (1 << 3))) { + /* -L opt */ + slink_depth = INT_MAX; + } + if((opt & (1 << 4))) { + /* -s opt */ + max_print_depth = 0; + } + one_file_system = opt & (1 << 5); /* -x opt */ + if((opt & (1 << 6))) { + /* -d opt */ + max_print_depth = bb_xgetularg10_bnd(smax_print_depth, 0, INT_MAX); + } + if((opt & (1 << 7))) { + /* -l opt */ + count_hardlinks = INT_MAX; + } + print_final_total = opt & (1 << 8); /* -c opt */ + + /* go through remaining args (if any) */ + argv += optind; + if (optind >= argc) { + *--argv = "."; + if (slink_depth == 1) { + slink_depth = 0; + } + } + + slink_depth_save = slink_depth; + total = 0; + do { + total += du(*argv); + slink_depth = slink_depth_save; + } while (*++argv); +#ifdef CONFIG_FEATURE_CLEAN_UP + reset_ino_dev_hashtable(); +#endif + + if (print_final_total) { + print(total, "total"); + } + + bb_fflush_stdout_and_exit(status); +} diff --git a/busybox/coreutils/echo.c b/busybox/coreutils/echo.c new file mode 100644 index 000000000..539640fb0 --- /dev/null +++ b/busybox/coreutils/echo.c @@ -0,0 +1,165 @@ +/* vi: set sw=4 ts=4: */ +/* + * echo implementation for busybox + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Original copyright notice is retained at the end of this file. + */ + +/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Because of behavioral differences, implemented configurable SUSv3 + * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs. + * 1) In handling '\c' escape, the previous version only suppressed the + * trailing newline. SUSv3 specifies _no_ output after '\c'. + * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}. + * The previous version version did not allow 4-digit octals. + */ + +#include +#include +#include +#include "busybox.h" + +extern int echo_main(int argc, char** argv) +{ +#ifndef CONFIG_FEATURE_FANCY_ECHO +#define eflag '\\' + ++argv; +#else + const char *p; + int nflag = 1; + int eflag = 0; + + while (*++argv && (**argv == '-')) { + /* If it appears that we are handling options, then make sure + * that all of the options specified are actually valid. + * Otherwise, the string should just be echoed. + */ + + if (!*(p = *argv + 1)) { /* A single '-', so echo it. */ + goto just_echo; + } + + do { + if (strrchr("neE", *p) == 0) { + goto just_echo; + } + } while (*++p); + + /* All of the options in this arg are valid, so handle them. */ + p = *argv + 1; + do { + if (*p == 'n') { + nflag = 0; + } else if (*p == 'e') { + eflag = '\\'; + } else { + eflag = 0; + } + } while (*++p); + } + +just_echo: +#endif + while (*argv) { + register int c; + + while ((c = *(*argv)++)) { + if (c == eflag) { /* Check for escape seq. */ + if (**argv == 'c') { + /* '\c' means cancel newline and + * ignore all subsequent chars. */ + goto DONE; + } +#ifndef CONFIG_FEATURE_FANCY_ECHO + /* SUSv3 specifies that octal escapes must begin with '0'. */ + if (((unsigned int)(**argv - '1')) >= 7) +#endif + { + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if ((**argv == '0') && (((unsigned int)(argv[0][1] - '0')) < 8)) { + (*argv)++; + } + /* bb_process_escape_sequence can handle nul correctly */ + c = bb_process_escape_sequence((const char **) argv); + } + } + putchar(c); + } + + if (*++argv) { + putchar(' '); + } + } + +#ifdef CONFIG_FEATURE_FANCY_ECHO + if (nflag) { + putchar('\n'); + } +#else + putchar('\n'); +#endif + +DONE: + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * 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..87ab30cdd --- /dev/null +++ b/busybox/coreutils/env.c @@ -0,0 +1,144 @@ +/* vi: set sw=4 ts=4: */ +/* + * env implementation for busybox + * + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Original copyright notice is retained at the end of this file. + * + * Modified for BusyBox by Erik Andersen + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Fixed bug involving exit return codes if execvp fails. Also added + * output error checking. + */ + +/* + * Modified by Vladimir Oleynik (C) 2003 + * - correct "-" option usage + * - multiple "-u unsetenv" support + * - GNU long option support + * - save errno after exec failed before bb_perror_msg() + */ + + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +static const struct option env_long_options[] = { + { "ignore-environment", 0, NULL, 'i' }, + { "unset", 1, NULL, 'u' }, + { 0, 0, 0, 0 } +}; + +extern int env_main(int argc, char** argv) +{ + char **ep, *p; + char *cleanenv[1] = { NULL }; + unsigned long opt; + llist_t *unset_env = NULL; + extern char **environ; + + bb_opt_complementaly = "u*"; + bb_applet_long_options = env_long_options; + + opt = bb_getopt_ulflags(argc, argv, "+iu:", &unset_env); + + argv += optind; + if (*argv && (argv[0][0] == '-') && !argv[0][1]) { + opt |= 1; + ++argv; + } + + if(opt & 1) + environ = cleanenv; + else if(opt & 2) { + while(unset_env) { + unsetenv(unset_env->data); + unset_env = unset_env->link; + } + } + + while (*argv && ((p = strchr(*argv, '=')) != NULL)) { + if (putenv(*argv) < 0) { + bb_perror_msg_and_die("putenv"); + } + ++argv; + } + + if (*argv) { + int er; + + execvp(*argv, argv); + er = errno; + bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */ + return (er == ENOENT) ? 127 : 126; /* SUSv3-mandated exit codes. */ + } + + for (ep = environ; *ep; ep++) { + puts(*ep); + } + + bb_fflush_stdout_and_exit(0); +} + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * 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..cbbd4cd03 --- /dev/null +++ b/busybox/coreutils/expr.c @@ -0,0 +1,528 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini expr implementation for busybox + * + * based on GNU expr Mike Parker. + * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc. + * + * Busybox modifications + * Copyright (c) 2000 Edward Betts . + * Aug 2003 Vladimir Oleynik - reduced 464 bytes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* This program evaluates expressions. Each token (operator, operand, + * parenthesis) of the expression must be a separate argument. The + * parser used is a reasonably general one, though any incarnation of + * it is language-specific. It is especially nice for expressions. + * + * No parse tree is needed; a new node is evaluated immediately. + * One function can handle multiple operators all of equal precedence, + * provided they all associate ((x op x) op x). */ + +/* no getopt needed */ + +#include +#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) { + bb_error_msg_and_die("too few arguments"); + } + + args = argv + 1; + + v = eval (); + if (*args) + bb_error_msg_and_die ("syntax error"); + + if (v->type == integer) + printf ("%d\n", v->u.i); + else + puts (v->u.s); + + exit (null (v)); +} + +/* Return a VALUE for I. */ + +static VALUE *int_value (int i) +{ + VALUE *v; + + v = xmalloc (sizeof(VALUE)); + v->type = integer; + v->u.i = i; + return v; +} + +/* Return a VALUE for S. */ + +static VALUE *str_value (char *s) +{ + VALUE *v; + + v = xmalloc (sizeof(VALUE)); + v->type = string; + v->u.s = bb_xstrdup (s); + return v; +} + +/* Free VALUE V, including structure components. */ + +static void freev (VALUE *v) +{ + if (v->type == string) + free (v->u.s); + free (v); +} + +/* Return nonzero if V is a null-string or zero-number. */ + +static int null (VALUE *v) +{ + switch (v->type) { + case integer: + return v->u.i == 0; + default: /* string: */ + return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0; + } +} + +/* Coerce V to a string value (can't fail). */ + +static void tostring (VALUE *v) +{ + if (v->type == integer) { + bb_xasprintf (&(v->u.s), "%d", v->u.i); + v->type = string; + } +} + +/* Coerce V to an integer value. Return 1 on success, 0 on failure. */ + +static int toarith (VALUE *v) +{ + if(v->type == string) { + int i; + char *e; + + /* Don't interpret the empty string as an integer. */ + /* Currently does not worry about overflow or int/long differences. */ + i = (int) strtol(v->u.s, &e, 10); + if ((v->u.s == e) || *e) + return 0; + free (v->u.s); + v->u.i = i; + v->type = integer; + } + return 1; +} + +/* Return nonzero if the next token matches STR exactly. + STR must not be NULL. */ + +static int +nextarg (char *str) +{ + if (*args == NULL) + return 0; + return strcmp (*args, str) == 0; +} + +/* The comparison operator handling functions. */ + +static int cmp_common (VALUE *l, VALUE *r, int op) +{ + int cmpval; + + if (l->type == string || r->type == string) { + tostring (l); + tostring (r); + cmpval = strcmp (l->u.s, r->u.s); + } + else + cmpval = l->u.i - r->u.i; + switch(op) { + case '<': + return cmpval < 0; + case ('L'+'E'): + return cmpval <= 0; + case '=': + return cmpval == 0; + case '!': + return cmpval != 0; + case '>': + return cmpval > 0; + default: /* >= */ + return cmpval >= 0; + } +} + +/* The arithmetic operator handling functions. */ + +static int arithmetic_common (VALUE *l, VALUE *r, int op) +{ + int li, ri; + + if (!toarith (l) || !toarith (r)) + bb_error_msg_and_die ("non-numeric argument"); + li = l->u.i; + ri = r->u.i; + if((op == '/' || op == '%') && ri == 0) + bb_error_msg_and_die ( "division by zero"); + switch(op) { + case '+': + return li + ri; + case '-': + return li - ri; + case '*': + return li * ri; + case '/': + return li / ri; + default: + return li % ri; + } +} + +/* Do the : operator. + SV is the VALUE for the lhs (the string), + PV is the VALUE for the rhs (the pattern). */ + +static VALUE *docolon (VALUE *sv, VALUE *pv) +{ + VALUE *v; + const char *errmsg; + struct re_pattern_buffer re_buffer; + struct re_registers re_regs; + int len; + + tostring (sv); + tostring (pv); + + if (pv->u.s[0] == '^') { + fprintf (stderr, "\ +warning: unportable BRE: `%s': using `^' as the first character\n\ +of a basic regular expression is not portable; it is being ignored", + pv->u.s); + } + + len = strlen (pv->u.s); + memset (&re_buffer, 0, sizeof (re_buffer)); + memset (&re_regs, 0, sizeof (re_regs)); + re_buffer.allocated = 2 * len; + re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated); + re_buffer.translate = 0; + re_syntax_options = RE_SYNTAX_POSIX_BASIC; + errmsg = re_compile_pattern (pv->u.s, len, &re_buffer); + if (errmsg) { + bb_error_msg_and_die("%s", errmsg); + } + + len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs); + if (len >= 0) { + /* Were \(...\) used? */ + if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */ + sv->u.s[re_regs.end[1]] = '\0'; + v = str_value (sv->u.s + re_regs.start[1]); + } + else + v = int_value (len); + } + else { + /* Match failed -- return the right kind of null. */ + if (re_buffer.re_nsub > 0) + v = str_value (""); + else + v = int_value (0); + } + free (re_buffer.buffer); + return v; +} + +/* Handle bare operands and ( expr ) syntax. */ + +static VALUE *eval7 (void) +{ + VALUE *v; + + if (!*args) + bb_error_msg_and_die ( "syntax error"); + + if (nextarg ("(")) { + args++; + v = eval (); + if (!nextarg (")")) + bb_error_msg_and_die ( "syntax error"); + args++; + return v; + } + + if (nextarg (")")) + bb_error_msg_and_die ( "syntax error"); + + return str_value (*args++); +} + +/* Handle match, substr, index, length, and quote keywords. */ + +static VALUE *eval6 (void) +{ + VALUE *l, *r, *v, *i1, *i2; + + if (nextarg ("quote")) { + args++; + if (!*args) + bb_error_msg_and_die ( "syntax error"); + return str_value (*args++); + } + else if (nextarg ("length")) { + args++; + r = eval6 (); + tostring (r); + v = int_value (strlen (r->u.s)); + freev (r); + return v; + } + else if (nextarg ("match")) { + args++; + l = eval6 (); + r = eval6 (); + v = docolon (l, r); + freev (l); + freev (r); + return v; + } + else if (nextarg ("index")) { + args++; + l = eval6 (); + r = eval6 (); + tostring (l); + tostring (r); + v = int_value (strcspn (l->u.s, r->u.s) + 1); + if (v->u.i == (int) strlen (l->u.s) + 1) + v->u.i = 0; + freev (l); + freev (r); + return v; + } + else if (nextarg ("substr")) { + args++; + l = eval6 (); + i1 = eval6 (); + i2 = eval6 (); + tostring (l); + if (!toarith (i1) || !toarith (i2) + || i1->u.i > (int) strlen (l->u.s) + || i1->u.i <= 0 || i2->u.i <= 0) + v = str_value (""); + else { + v = xmalloc (sizeof(VALUE)); + v->type = string; + v->u.s = bb_xstrndup(l->u.s + i1->u.i - 1, i2->u.i); + } + freev (l); + freev (i1); + freev (i2); + return v; + } + else + return eval7 (); +} + +/* Handle : operator (pattern matching). + Calls docolon to do the real work. */ + +static VALUE *eval5 (void) +{ + VALUE *l, *r, *v; + + l = eval6 (); + while (nextarg (":")) { + args++; + r = eval6 (); + v = docolon (l, r); + freev (l); + freev (r); + l = v; + } + return l; +} + +/* Handle *, /, % operators. */ + +static VALUE *eval4 (void) +{ + VALUE *l, *r; + int op, val; + + l = eval5 (); + while (1) { + if (nextarg ("*")) + op = '*'; + else if (nextarg ("/")) + op = '/'; + else if (nextarg ("%")) + op = '%'; + else + return l; + args++; + r = eval5 (); + val = arithmetic_common (l, r, op); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle +, - operators. */ + +static VALUE *eval3 (void) +{ + VALUE *l, *r; + int op, val; + + l = eval4 (); + while (1) { + if (nextarg ("+")) + op = '+'; + else if (nextarg ("-")) + op = '-'; + else + return l; + args++; + r = eval4 (); + val = arithmetic_common (l, r, op); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle comparisons. */ + +static VALUE *eval2 (void) +{ + VALUE *l, *r; + int op, val; + + l = eval3 (); + while (1) { + if (nextarg ("<")) + op = '<'; + else if (nextarg ("<=")) + op = 'L'+'E'; + else if (nextarg ("=") || nextarg ("==")) + op = '='; + else if (nextarg ("!=")) + op = '!'; + else if (nextarg (">=")) + op = 'G'+'E'; + else if (nextarg (">")) + op = '>'; + else + return l; + args++; + r = eval3 (); + toarith (l); + toarith (r); + val = cmp_common (l, r, op); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle &. */ + +static VALUE *eval1 (void) +{ + VALUE *l, *r; + + l = eval2 (); + while (nextarg ("&")) { + args++; + r = eval2 (); + if (null (l) || null (r)) { + freev (l); + freev (r); + l = int_value (0); + } + else + freev (r); + } + return l; +} + +/* Handle |. */ + +static VALUE *eval (void) +{ + VALUE *l, *r; + + l = eval1 (); + while (nextarg ("|")) { + args++; + r = eval1 (); + if (null (l)) { + freev (l); + l = r; + } + else + freev (r); + } + return l; +} diff --git a/busybox/coreutils/false.c b/busybox/coreutils/false.c new file mode 100644 index 000000000..5cf238409 --- /dev/null +++ b/busybox/coreutils/false.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini false implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/false.html */ + +#include +#include "busybox.h" + +extern int false_main(int argc, char **argv) +{ + return EXIT_FAILURE; +} diff --git a/busybox/coreutils/fold.c b/busybox/coreutils/fold.c new file mode 100644 index 000000000..68f24e61a --- /dev/null +++ b/busybox/coreutils/fold.c @@ -0,0 +1,194 @@ +/* fold -- wrap each input line to fit in specified width. + + Written by David MacKenzie, djm@gnu.ai.mit.edu. + Copyright (C) 91, 1995-2002 Free Software Foundation, Inc. + + Modified for busybox based on coreutils v 5.0 + Copyright (C) 2003 Glenn McGrath + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +/* If nonzero, count bytes, not column positions. */ +static int count_bytes; + +/* Assuming the current column is COLUMN, return the column that + printing C will move the cursor to. + The first column is 0. */ + +static int adjust_column(int column, char c) +{ + if (!count_bytes) { + if (c == '\b') { + if (column > 0) + column--; + } else if (c == '\r') + column = 0; + else if (c == '\t') + column = column + 8 - column % 8; + else /* if (isprint (c)) */ + column++; + } else + column++; + return column; +} + +extern int fold_main(int argc, char **argv) +{ + /* If nonzero, try to break on whitespace. */ + int break_spaces; + + /* If nonzero, at least one of the files we read was standard input. */ + int have_read_stdin; + + int width = 80; + int i; + int optc; + int errs = 0; + + break_spaces = count_bytes = have_read_stdin = 0; + + /* Turn any numeric options into -w options. */ + for (i = 1; i < argc; i++) { + char const *a = argv[i]; + + if (a[0] == '-') { + if (a[1] == '-' && !a[2]) + break; + if (isdigit(a[1])) { + char *s = xmalloc(strlen(a) + 2); + + s[0] = '-'; + s[1] = 'w'; + strcpy(s + 2, a + 1); + argv[i] = s; + } + } + } + + while ((optc = getopt(argc, argv, "bsw:")) > 0) { + switch (optc) { + case 'b': /* Count bytes rather than columns. */ + count_bytes = 1; + break; + case 's': /* Break at word boundaries. */ + break_spaces = 1; + break; + case 'w': { /* Line width. */ + width = bb_xgetlarg(optarg, 10, 1, 10000); + break; + } + default: + bb_show_usage(); + } + } + + argv += optind; + if (!*argv) { + *--argv = "-"; + } + + do { + FILE *istream = bb_wfopen_input(*argv); + if (istream != NULL) { + int c; + int column = 0; /* Screen column where next char will go. */ + int offset_out = 0; /* Index in `line_out' for next char. */ + static char *line_out = NULL; + static int allocated_out = 0; + + while ((c = getc(istream)) != EOF) { + if (offset_out + 1 >= allocated_out) { + allocated_out += 1024; + line_out = xrealloc(line_out, allocated_out); + } + + if (c == '\n') { + line_out[offset_out++] = c; + fwrite(line_out, sizeof(char), (size_t) offset_out, stdout); + column = offset_out = 0; + continue; + } + +rescan: + column = adjust_column(column, c); + + if (column > width) { + /* This character would make the line too long. + Print the line plus a newline, and make this character + start the next line. */ + if (break_spaces) { + /* Look for the last blank. */ + int logical_end; + + for (logical_end = offset_out - 1; logical_end >= 0; logical_end--) { + if (isblank(line_out[logical_end])) { + break; + } + } + if (logical_end >= 0) { + /* Found a blank. Don't output the part after it. */ + logical_end++; + fwrite(line_out, sizeof(char), (size_t) logical_end, stdout); + putchar('\n'); + /* Move the remainder to the beginning of the next line. + The areas being copied here might overlap. */ + memmove(line_out, line_out + logical_end, offset_out - logical_end); + offset_out -= logical_end; + for (column = i = 0; i < offset_out; i++) { + column = adjust_column(column, line_out[i]); + } + goto rescan; + } + } else { + if (offset_out == 0) { + line_out[offset_out++] = c; + continue; + } + } + line_out[offset_out++] = '\n'; + fwrite(line_out, sizeof(char), (size_t) offset_out, stdout); + column = offset_out = 0; + goto rescan; + } + + line_out[offset_out++] = c; + } + + if (offset_out) { + fwrite(line_out, sizeof(char), (size_t) offset_out, stdout); + } + + if (ferror(istream) || bb_fclose_nonstdin(istream)) { + bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */ + errs |= EXIT_FAILURE; + } + } else { + errs |= EXIT_FAILURE; + } + } while (*++argv); + + bb_fflush_stdout_and_exit(errs); +} diff --git a/busybox/coreutils/head.c b/busybox/coreutils/head.c new file mode 100644 index 000000000..dab4de11b --- /dev/null +++ b/busybox/coreutils/head.c @@ -0,0 +1,138 @@ +/* vi: set sw=4 ts=4: */ +/* + * head implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +static const char head_opts[] = + "n:" +#ifdef CONFIG_FEATURE_FANCY_HEAD + "c:qv" +#endif + ; + +static const char header_fmt_str[] = "\n==> %s <==\n"; + +int head_main(int argc, char **argv) +{ + unsigned long count = 10; + unsigned long i; +#ifdef CONFIG_FEATURE_FANCY_HEAD + int count_bytes = 0; + int header_threshhold = 1; +#endif + + FILE *fp; + const char *fmt; + char *p; + int opt; + int c; + int retval = EXIT_SUCCESS; + + /* Allow legacy syntax of an initial numeric option without -n. */ + if ((argc > 1) && (argv[1][0] == '-') + /* && (isdigit)(argv[1][1]) */ + && (((unsigned int)(argv[1][1] - '0')) <= 9) + ) { + --argc; + ++argv; + p = (*argv) + 1; + goto GET_COUNT; + } + + while ((opt = getopt(argc, argv, head_opts)) > 0) { + switch(opt) { +#ifdef CONFIG_FEATURE_FANCY_HEAD + case 'q': + header_threshhold = INT_MAX; + break; + case 'v': + header_threshhold = -1; + break; + case 'c': + count_bytes = 1; + /* fall through */ +#endif + case 'n': + p = optarg; + GET_COUNT: + count = bb_xgetularg10(p); + break; + default: + bb_show_usage(); + } + } + + argv += optind; + if (!*argv) { + *--argv = "-"; + } + + fmt = header_fmt_str + 1; +#ifdef CONFIG_FEATURE_FANCY_HEAD + if (argc - optind <= header_threshhold) { + header_threshhold = 0; + } +#else + if (argc <= optind + 1) { + fmt += 11; + } + /* Now define some things here to avoid #ifdefs in the code below. + * These should optimize out of the if conditions below. */ +#define header_threshhold 1 +#define count_bytes 0 +#endif + + do { + if ((fp = bb_wfopen_input(*argv)) != NULL) { + if (fp == stdin) { + *argv = (char *) bb_msg_standard_input; + } + if (header_threshhold) { + bb_printf(fmt, *argv); + } + i = count; + while (i && ((c = getc(fp)) != EOF)) { + if (count_bytes || (c == '\n')) { + --i; + } + putchar(c); + } + if (bb_fclose_nonstdin(fp)) { + bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */ + retval = EXIT_FAILURE; + } + bb_xferror_stdout(); + } + fmt = header_fmt_str; + } while (*++argv); + + bb_fflush_stdout_and_exit(retval); +} diff --git a/busybox/coreutils/hostid.c b/busybox/coreutils/hostid.c new file mode 100644 index 000000000..917dc223e --- /dev/null +++ b/busybox/coreutils/hostid.c @@ -0,0 +1,38 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include +#include +#include "busybox.h" + +extern int hostid_main(int argc, char **argv) +{ + if (argc > 1) { + bb_show_usage(); + } + + bb_printf("%lx\n", gethostid()); + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/id.c b/busybox/coreutils/id.c new file mode 100644 index 000000000..d5182b953 --- /dev/null +++ b/busybox/coreutils/id.c @@ -0,0 +1,135 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- option -G is not currently supported. */ +/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever length and to + * be more similar to GNU id. + */ + +#include "busybox.h" +#include "pwd_.h" +#include +#include +#include + +#ifdef CONFIG_SELINUX +#include +#include +#endif + +#define PRINT_REAL 1 +#define NAME_NOT_NUMBER 2 +#define JUST_USER 4 +#define JUST_GROUP 8 + +static short printf_full(unsigned int id, const char *arg, const char prefix) +{ + const char *fmt = "%cid=%u"; + short status=EXIT_FAILURE; + + if(arg) { + fmt = "%cid=%u(%s)"; + status=EXIT_SUCCESS; + } + bb_printf(fmt, prefix, id, arg); + return status; +} + +extern int id_main(int argc, char **argv) +{ + struct passwd *p; + uid_t uid; + gid_t gid; + unsigned long flags; + short status; +#ifdef CONFIG_SELINUX + int is_flask_enabled_flag = is_flask_enabled(); +#endif + + bb_opt_complementaly = "u~g:g~u"; + flags = bb_getopt_ulflags(argc, argv, "rnug"); + + if ((flags & 0x80000000UL) + /* Don't allow -n -r -nr */ + || (flags <= 3 && flags > 0) + /* Don't allow more than one username */ + || (argc > optind + 1)) + bb_show_usage(); + + /* This values could be overwritten later */ + uid = geteuid(); + gid = getegid(); + if (flags & PRINT_REAL) { + uid = getuid(); + gid = getgid(); + } + + if(argv[optind]) { + p=getpwnam(argv[optind]); + /* my_getpwnam is needed because it exits on failure */ + uid = my_getpwnam(argv[optind]); + gid = p->pw_gid; + /* in this case PRINT_REAL is the same */ + } + + if(flags & (JUST_GROUP | JUST_USER)) { + /* JUST_GROUP and JUST_USER are mutually exclusive */ + if(flags & NAME_NOT_NUMBER) { + /* my_getpwuid and my_getgrgid exit on failure so puts cannot segfault */ + puts((flags & JUST_USER) ? my_getpwuid(NULL, uid, -1 ) : my_getgrgid(NULL, gid, -1 )); + } else { + bb_printf("%u\n",(flags & JUST_USER) ? uid : gid); + } + /* exit */ + bb_fflush_stdout_and_exit(EXIT_SUCCESS); + } + + /* Print full info like GNU id */ + /* my_getpwuid doesn't exit on failure here */ + status=printf_full(uid, my_getpwuid(NULL, uid, 0), 'u'); + putchar(' '); + /* my_getgrgid doesn't exit on failure here */ + status|=printf_full(gid, my_getgrgid(NULL, gid, 0), 'g'); +#ifdef CONFIG_SELINUX + if(is_flask_enabled_flag) { + security_id_t mysid = getsecsid(); + char context[80]; + int len = sizeof(context); + context[0] = '\0'; + if(security_sid_to_context(mysid, context, &len)) + strcpy(context, "unknown"); + bb_printf(" context=%s", context); + } +#endif + putchar('\n'); + bb_fflush_stdout_and_exit(status); +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + diff --git a/busybox/coreutils/install.c b/busybox/coreutils/install.c new file mode 100644 index 000000000..36dc1d618 --- /dev/null +++ b/busybox/coreutils/install.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2003 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * TODO: -d option, need a way of recursively making directories and changing + * owner/group, will probably modify bb_make_directory(...) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +#define INSTALL_OPT_CMD 1 +#define INSTALL_OPT_DIRECTORY 2 +#define INSTALL_OPT_PRESERVE_TIME 4 +#define INSTALL_OPT_STRIP 8 +#define INSTALL_OPT_GROUP 16 +#define INSTALL_OPT_MODE 32 +#define INSTALL_OPT_OWNER 64 + +static const struct option install_long_options[] = { + { "directory", 0, NULL, 'd' }, + { "preserve-timestamps", 0, NULL, 'p' }, + { "strip", 0, NULL, 's' }, + { "group", 0, NULL, 'g' }, + { "mode", 0, NULL, 'm' }, + { "owner", 0, NULL, 'o' }, + { 0, 0, 0, 0 } +}; + +extern int install_main(int argc, char **argv) +{ + struct stat statbuf; + mode_t mode; + uid_t uid; + gid_t gid; + char *gid_str = "-1"; + char *uid_str = "-1"; + char *mode_str = "0755"; + int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE; + int ret = EXIT_SUCCESS; + int flags; + int i; + + bb_applet_long_options = install_long_options; + bb_opt_complementaly = "s~d:d~s"; + /* -c exists for backwards compatability, its needed */ + flags = bb_getopt_ulflags(argc, argv, "cdpsg:m:o:", &gid_str, &mode_str, &uid_str); /* 'a' must be 2nd */ + + /* Check valid options were given */ + if(flags & 0x80000000UL) { + bb_show_usage(); + } + + /* preserve access and modification time, this is GNU behaviour, BSD only preserves modification time */ + if (flags & INSTALL_OPT_PRESERVE_TIME) { + copy_flags |= FILEUTILS_PRESERVE_STATUS; + } + bb_parse_mode(mode_str, &mode); + gid = get_ug_id(gid_str, my_getgrnam); + uid = get_ug_id(uid_str, my_getpwnam); + umask(0); + + /* Create directories + * dont use bb_make_directory() as it cant change uid or gid + * perhaps bb_make_directory() should be improved. + */ + if (flags & INSTALL_OPT_DIRECTORY) { + for (argv += optind; *argv; argv++) { + char *old_argv_ptr = *argv + 1; + char *argv_ptr; + do { + argv_ptr = strchr(old_argv_ptr, '/'); + old_argv_ptr = argv_ptr; + if (argv_ptr) { + *argv_ptr = '\0'; + old_argv_ptr++; + } + if (mkdir(*argv, mode) == -1) { + if (errno != EEXIST) { + bb_perror_msg("coulnt create %s", *argv); + ret = EXIT_FAILURE; + break; + } + } + else if (lchown(*argv, uid, gid) == -1) { + bb_perror_msg("cannot change ownership of %s", *argv); + ret = EXIT_FAILURE; + break; + } + if (argv_ptr) { + *argv_ptr = '/'; + } + } while (old_argv_ptr); + } + return(ret); + } + + cp_mv_stat2(argv[argc - 1], &statbuf, lstat); + for (i = optind; i < argc - 1; i++) { + unsigned char *dest; + + if (S_ISDIR(statbuf.st_mode)) { + dest = concat_path_file(argv[argc - 1], basename(argv[i])); + } else { + dest = argv[argc - 1]; + } + ret |= copy_file(argv[i], dest, copy_flags); + + /* Set the file mode */ + if (chmod(dest, mode) == -1) { + bb_perror_msg("cannot change permissions of %s", dest); + ret = EXIT_FAILURE; + } + + /* Set the user and group id */ + if (lchown(dest, uid, gid) == -1) { + bb_perror_msg("cannot change ownership of %s", dest); + ret = EXIT_FAILURE; + } + if (flags & INSTALL_OPT_STRIP) { + if (execlp("strip", "strip", dest, NULL) == -1) { + bb_error_msg("strip failed"); + ret = EXIT_FAILURE; + } + } + } + + return(ret); +} diff --git a/busybox/coreutils/length.c b/busybox/coreutils/length.c new file mode 100644 index 000000000..bce43ab3f --- /dev/null +++ b/busybox/coreutils/length.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */ + +#include +#include +#include +#include "busybox.h" + +extern int length_main(int argc, char **argv) +{ + if ((argc != 2) || (**(++argv) == '-')) { + bb_show_usage(); + } + + bb_printf("%lu\n", (unsigned long)strlen(*argv)); + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/libcoreutils/Makefile b/busybox/coreutils/libcoreutils/Makefile new file mode 100644 index 000000000..0a1c80a41 --- /dev/null +++ b/busybox/coreutils/libcoreutils/Makefile @@ -0,0 +1,33 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=../.. +top_builddir=../.. +srcdir=$(top_srcdir)/coreutils/libcoreutils +LIBCOREUTILS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in + +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/coreutils/libcoreutils/Makefile.in b/busybox/coreutils/libcoreutils/Makefile.in new file mode 100644 index 000000000..cf83d7107 --- /dev/null +++ b/busybox/coreutils/libcoreutils/Makefile.in @@ -0,0 +1,37 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +LIBCOREUTILS_AR:=libcoreutils.a +ifndef $(LIBCOREUTILS_DIR) +LIBCOREUTILS_DIR:=$(top_builddir)/coreutils/libcoreutils/ +endif +srcdir=$(top_srcdir)/coreutils/libcoreutils + +LIBCOREUTILS_SRC:= cp_mv_stat.c getopt_mk_fifo_nod.c xgetoptfile_sort_uniq.c + +LIBCOREUTILS_OBJS=$(patsubst %.c,$(LIBCOREUTILS_DIR)%.o, $(LIBCOREUTILS_SRC)) + +libraries-y+=$(LIBCOREUTILS_DIR)$(LIBCOREUTILS_AR) + +$(LIBCOREUTILS_DIR)$(LIBCOREUTILS_AR): $(LIBCOREUTILS_OBJS) + $(AR) -ro $@ $(LIBCOREUTILS_OBJS) + +$(LIBCOREUTILS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/coreutils/libcoreutils/coreutils.h b/busybox/coreutils/libcoreutils/coreutils.h new file mode 100644 index 000000000..eabca8204 --- /dev/null +++ b/busybox/coreutils/libcoreutils/coreutils.h @@ -0,0 +1,12 @@ +#ifndef COREUTILS_H +#define COREUTILS_H 1 + +typedef int (*stat_func)(const char *fn, struct stat *ps); + +extern int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf); +extern int cp_mv_stat(const char *fn, struct stat *fn_stat); + +extern mode_t getopt_mk_fifo_nod(int argc, char **argv); +extern FILE *xgetoptfile_sort_uniq(char **argv, const char *mode); + +#endif diff --git a/busybox/coreutils/libcoreutils/cp_mv_stat.c b/busybox/coreutils/libcoreutils/cp_mv_stat.c new file mode 100644 index 000000000..5a70b0221 --- /dev/null +++ b/busybox/coreutils/libcoreutils/cp_mv_stat.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" +#include "coreutils.h" + +extern int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) +{ + if (sf(fn, fn_stat) < 0) { + if (errno != ENOENT) { + bb_perror_msg("unable to stat `%s'", fn); + return -1; + } + return 0; + } else if (S_ISDIR(fn_stat->st_mode)) { + return 3; + } + return 1; +} + +extern int cp_mv_stat(const char *fn, struct stat *fn_stat) +{ + return cp_mv_stat2(fn, fn_stat, stat); +} diff --git a/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c b/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c new file mode 100644 index 000000000..0872bdcf0 --- /dev/null +++ b/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" +#include "coreutils.h" + +extern mode_t getopt_mk_fifo_nod(int argc, char **argv) +{ + mode_t mode = 0666; + int opt; + + while ((opt = getopt(argc, argv, "m:")) > 0) { + if (opt == 'm') { + mode = 0666; + if (bb_parse_mode(optarg, &mode)) { + umask(0); + continue; + } + } + bb_show_usage(); + } + return mode; +} diff --git a/busybox/coreutils/libcoreutils/xgetoptfile_sort_uniq.c b/busybox/coreutils/libcoreutils/xgetoptfile_sort_uniq.c new file mode 100644 index 000000000..a63daf97b --- /dev/null +++ b/busybox/coreutils/libcoreutils/xgetoptfile_sort_uniq.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" +#include "coreutils.h" + +extern FILE *xgetoptfile_sort_uniq(char **argv, const char *mode) +{ + const char *n; + + if ((n = *argv) != NULL) { + if ((*n != '-') || n[1]) { + return bb_xfopen(n, mode); + } + } + return (*mode == 'r') ? stdin : stdout; +} diff --git a/busybox/coreutils/ln.c b/busybox/coreutils/ln.c new file mode 100644 index 000000000..885ba61db --- /dev/null +++ b/busybox/coreutils/ln.c @@ -0,0 +1,102 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ln implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU options missing: -b, -d, -F, -i, -S, and -v. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Fixed bug involving -n option. Essentially, -n was always in effect. + */ + +#include +#include +#include "busybox.h" + +#define LN_SYMLINK 1 +#define LN_FORCE 2 +#define LN_NODEREFERENCE 4 + +extern int ln_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int flag; + char *last; + char *src_name; + char *src; + struct stat statbuf; + int (*link_func)(const char *, const char *); + + flag = bb_getopt_ulflags(argc, argv, "sfn"); + + if (argc == optind) { + bb_show_usage(); + } + + last = argv[argc - 1]; + argv += optind; + + if (argc == optind + 1) { + *--argv = last; + last = bb_get_last_path_component(bb_xstrdup(last)); + } + + do { + src_name = NULL; + src = last; + + if (is_directory(src, + (flag & LN_NODEREFERENCE) ^ LN_NODEREFERENCE, + NULL)) { + src_name = bb_xstrdup(*argv); + src = concat_path_file(src, bb_get_last_path_component(src_name)); + free(src_name); + src_name = src; + } + if (!(flag & LN_SYMLINK) && stat(*argv, &statbuf)) { + bb_perror_msg(*argv); + status = EXIT_FAILURE; + free(src_name); + continue; + } + + if (flag & LN_FORCE) { + unlink(src); + } + + link_func = link; + if (flag & LN_SYMLINK) { + link_func = symlink; + } + + if (link_func(*argv, src) != 0) { + bb_perror_msg(src); + status = EXIT_FAILURE; + } + + free(src_name); + + } while ((++argv)[1]); + + return status; +} diff --git a/busybox/coreutils/logname.c b/busybox/coreutils/logname.c new file mode 100644 index 000000000..ca5eb41cf --- /dev/null +++ b/busybox/coreutils/logname.c @@ -0,0 +1,55 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * SUSv3 specifies the string used is that returned from getlogin(). + * The previous implementation used getpwuid() for geteuid(), which + * is _not_ the same. Erik apparently made this change almost 3 years + * ago to avoid failing when no utmp was available. However, the + * correct course of action wrt SUSv3 for a failing getlogin() is + * a diagnostic message and an error return. + */ + +#include +#include +#include +#include "busybox.h" + +extern int logname_main(int argc, char **argv) +{ + const char *p; + + if (argc > 1) { + bb_show_usage(); + } + + if ((p = getlogin()) != NULL) { + puts(p); + bb_fflush_stdout_and_exit(EXIT_SUCCESS); + } + + bb_perror_msg_and_die("getlogin"); +} diff --git a/busybox/coreutils/ls.c b/busybox/coreutils/ls.c new file mode 100644 index 000000000..4e21454ce --- /dev/null +++ b/busybox/coreutils/ls.c @@ -0,0 +1,1134 @@ +/* vi: set sw=4 ts=4: */ +/* + * tiny-ls.c version 0.1.0: A minimalist 'ls' + * Copyright (C) 1996 Brian Candler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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_GAP = 2, /* includes the file type char */ +}; + +/************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* major() and minor() */ +#include "busybox.h" +#ifdef CONFIG_SELINUX +#include +#include +#include +#endif + +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS +#include +#endif + +/* what is the overall style of the listing */ +#define STYLE_AUTO (0) +#define STYLE_COLUMNS (1U<<21) /* fill columns */ +#define STYLE_LONG (2U<<21) /* one record per line, extended info */ +#define STYLE_SINGLE (3U<<21) /* one record per line */ + +#define STYLE_MASK STYLE_SINGLE +#define STYLE_ONE_RECORD_FLAG STYLE_LONG + +/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ +/* what file information will be listed */ +#define LIST_INO (1U<<0) +#define LIST_BLOCKS (1U<<1) +#define LIST_MODEBITS (1U<<2) +#define LIST_NLINKS (1U<<3) +#define LIST_ID_NAME (1U<<4) +#define LIST_ID_NUMERIC (1U<<5) +#define LIST_CONTEXT (1U<<6) +#define LIST_SIZE (1U<<7) +#define LIST_DEV (1U<<8) +#define LIST_DATE_TIME (1U<<9) +#define LIST_FULLTIME (1U<<10) +#define LIST_FILENAME (1U<<11) +#define LIST_SYMLINK (1U<<12) +#define LIST_FILETYPE (1U<<13) +#define LIST_EXEC (1U<<14) + +#define LIST_MASK ((LIST_EXEC << 1) - 1) + +/* what files will be displayed */ +/* TODO -- We may be able to make DISP_NORMAL 0 to save a bit slot. */ +#define DISP_NORMAL (1U<<14) /* show normal filenames */ +#define DISP_DIRNAME (1U<<15) /* 2 or more items? label directories */ +#define DISP_HIDDEN (1U<<16) /* show filenames starting with . */ +#define DISP_DOT (1U<<17) /* show . and .. */ +#define DISP_NOLIST (1U<<18) /* show directory as itself, not contents */ +#define DISP_RECURSIVE (1U<<19) /* show directory and everything below it */ +#define DISP_ROWS (1U<<20) /* print across rows */ + +#define DISP_MASK (((DISP_ROWS << 1) - 1) & ~(DISP_NORMAL - 1)) + +#ifdef CONFIG_FEATURE_LS_SORTFILES +/* how will the files be sorted */ +#define SORT_ORDER_FORWARD 0 /* sort in reverse order */ +#define SORT_ORDER_REVERSE (1U<<27) /* sort in reverse order */ + +#define SORT_NAME 0 /* sort by file name */ +#define SORT_SIZE (1U<<28) /* sort by file size */ +#define SORT_ATIME (2U<<28) /* sort by last access time */ +#define SORT_CTIME (3U<<28) /* sort by last change time */ +#define SORT_MTIME (4U<<28) /* sort by last modification time */ +#define SORT_VERSION (5U<<28) /* sort by version */ +#define SORT_EXT (6U<<28) /* sort by file name extension */ +#define SORT_DIR (7U<<28) /* sort by file or directory */ + +#define SORT_MASK (7U<<28) +#endif + +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS +/* which of the three times will be used */ +#define TIME_MOD 0 +#define TIME_CHANGE (1U<<23) +#define TIME_ACCESS (1U<<24) + +#define TIME_MASK (3U<<23) +#endif + +#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS +#define FOLLOW_LINKS (1U<<25) +#endif +#ifdef CONFIG_FEATURE_HUMAN_READABLE +#define LS_DISP_HR (1U<<26) +#endif + +#define LIST_SHORT (LIST_FILENAME) +#define LIST_ISHORT (LIST_INO | LIST_FILENAME) +#define LIST_LONG (LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \ + LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK) +#define LIST_ILONG (LIST_INO | LIST_LONG) + +#define SPLIT_DIR 1 +#define SPLIT_FILE 0 +#define SPLIT_SUBDIR 2 + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) + +#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR) +# define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) +#endif + +/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ +#ifdef CONFIG_FEATURE_LS_COLOR +static int show_color = 0; + +#define COLOR(mode) ("\000\043\043\043\042\000\043\043"\ + "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)]) +#define ATTR(mode) ("\00\00\01\00\01\00\01\00"\ + "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)]) +#endif + +/* + * a directory entry and its stat info are stored here + */ +struct dnode { /* the basic node */ + char *name; /* the dir entry name */ + char *fullname; /* the dir entry name */ + struct stat dstat; /* the file stat info */ +#ifdef CONFIG_SELINUX + security_id_t sid; +#endif + struct dnode *next; /* point at the next node */ +}; +typedef struct dnode dnode_t; + +static struct dnode **list_dir(const char *); +static struct dnode **dnalloc(int); +static int list_single(struct dnode *); + +static unsigned int all_fmt; + +#ifdef CONFIG_SELINUX +static int is_flask_enabled_flag; +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static int terminal_width = TERMINAL_WIDTH; +static unsigned short tabstops = COLUMN_GAP; +#else +#define tabstops COLUMN_GAP +#define terminal_width TERMINAL_WIDTH +#endif + +static int status = EXIT_SUCCESS; + +static struct dnode *my_stat(char *fullname, char *name) +{ + struct stat dstat; + struct dnode *cur; +#ifdef CONFIG_SELINUX + security_id_t sid; +#endif + int rc; + +#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS + if (all_fmt & FOLLOW_LINKS) { +#ifdef CONFIG_SELINUX + if(is_flask_enabled_flag) + rc = stat_secure(fullname, &dstat, &sid); + else +#endif + rc = stat(fullname, &dstat); + if(rc) + { + bb_perror_msg("%s", fullname); + status = EXIT_FAILURE; + return 0; + } + } else +#endif + { +#ifdef CONFIG_SELINUX + if(is_flask_enabled_flag) + rc = lstat_secure(fullname, &dstat, &sid); + else +#endif + rc = lstat(fullname, &dstat); + if(rc) + { + bb_perror_msg("%s", fullname); + status = EXIT_FAILURE; + return 0; + } + } + + cur = (struct dnode *) xmalloc(sizeof(struct dnode)); + cur->fullname = fullname; + cur->name = name; + cur->dstat = dstat; +#ifdef CONFIG_SELINUX + cur->sid = sid; +#endif + return cur; +} + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_FEATURE_LS_COLOR +static char fgcolor(mode_t mode) +{ + /* Check wheter the file is existing (if so, color it red!) */ + if (errno == ENOENT) { + return '\037'; + } + if (LIST_EXEC && S_ISREG(mode) + && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return COLOR(0xF000); /* File is executable ... */ + return COLOR(mode); +} + +/*----------------------------------------------------------------------*/ +static char bgcolor(mode_t mode) +{ + if (LIST_EXEC && S_ISREG(mode) + && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return ATTR(0xF000); /* File is executable ... */ + return ATTR(mode); +} +#endif + +/*----------------------------------------------------------------------*/ +#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR) +static char append_char(mode_t mode) +{ + if (!(all_fmt & LIST_FILETYPE)) + return '\0'; + if ((all_fmt & LIST_EXEC) && S_ISREG(mode) + && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return '*'; + return APPCHAR(mode); +} +#endif + +/*----------------------------------------------------------------------*/ + +#define countdirs(A,B) count_dirs((A), (B), 1) +#define countsubdirs(A,B) count_dirs((A), (B), 0) + +static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs) +{ + int i, dirs; + + if (dn == NULL || nfiles < 1) + return (0); + dirs = 0; + for (i = 0; i < nfiles; i++) { + if (S_ISDIR(dn[i]->dstat.st_mode) + && (notsubdirs + || ((dn[i]->name[0] != '.') + || (dn[i]->name[1] + && ((dn[i]->name[1] != '.') + || dn[i]->name[2]))))) + dirs++; + } + return (dirs); +} + +static int countfiles(struct dnode **dnp) +{ + int nfiles; + struct dnode *cur; + + if (dnp == NULL) + return (0); + nfiles = 0; + for (cur = dnp[0]; cur->next != NULL; cur = cur->next) + nfiles++; + nfiles++; + return (nfiles); +} + +/* get memory to hold an array of pointers */ +static struct dnode **dnalloc(int num) +{ + struct dnode **p; + + if (num < 1) + return (NULL); + + p = (struct dnode **) xcalloc((size_t) num, + (size_t) (sizeof(struct dnode *))); + return (p); +} + +#ifdef CONFIG_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp) +{ + struct dnode *cur, *next; + + if (dnp == NULL) + return; + + cur = dnp[0]; + while (cur != NULL) { + free(cur->fullname); /* free the filename */ + next = cur->next; + free(cur); /* free the dnode */ + cur = next; + } + free(dnp); /* free the array holding the dnode pointers */ +} +#endif + +static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) +{ + int dncnt, i, d; + struct dnode **dnp; + + if (dn == NULL || nfiles < 1) + return (NULL); + + /* count how many dirs and regular files there are */ + if (which == SPLIT_SUBDIR) + dncnt = countsubdirs(dn, nfiles); + else { + dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */ + if (which == SPLIT_FILE) + dncnt = nfiles - dncnt; /* looking for files */ + } + + /* allocate a file array and a dir array */ + dnp = dnalloc(dncnt); + + /* copy the entrys into the file or dir array */ + for (d = i = 0; i < nfiles; i++) { + if (S_ISDIR(dn[i]->dstat.st_mode)) { + if (which & (SPLIT_DIR|SPLIT_SUBDIR)) { + if ((which & SPLIT_DIR) + || ((dn[i]->name[0] != '.') + || (dn[i]->name[1] + && ((dn[i]->name[1] != '.') + || dn[i]->name[2])))) { + dnp[d++] = dn[i]; + } + } + } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) { + dnp[d++] = dn[i]; + } + } + return (dnp); +} + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_FEATURE_LS_SORTFILES +static int sortcmp(struct dnode *d1, struct dnode *d2) +{ + unsigned int sort_opts = all_fmt & SORT_MASK; + int dif; + + dif = 0; /* assume SORT_NAME */ + if (sort_opts == SORT_SIZE) { + dif = (int) (d2->dstat.st_size - d1->dstat.st_size); + } else if (sort_opts == SORT_ATIME) { + dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime); + } else if (sort_opts == SORT_CTIME) { + dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime); + } else if (sort_opts == SORT_MTIME) { + dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime); + } else if (sort_opts == SORT_DIR) { + dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode); + /* } else if (sort_opts == SORT_VERSION) { */ + /* } else if (sort_opts == SORT_EXT) { */ + } + + if (dif == 0) { + /* sort by name- may be a tie_breaker for time or size cmp */ +#ifdef CONFIG_LOCALE_SUPPORT + dif = strcoll(d1->name, d2->name); +#else + dif = strcmp(d1->name, d2->name); +#endif + } + + if (all_fmt & SORT_ORDER_REVERSE) { + dif = -dif; + } + return (dif); +} + +/*----------------------------------------------------------------------*/ +static void shellsort(struct dnode **dn, int size) +{ + struct dnode *temp; + int gap, i, j; + + /* shell short the array */ + if (dn == NULL || size < 2) + return; + + for (gap = size / 2; gap > 0; gap /= 2) { + for (i = gap; i < size; i++) { + for (j = i - gap; j >= 0; j -= gap) { + if (sortcmp(dn[j], dn[j + gap]) <= 0) + break; + /* they are out of order, swap them */ + temp = dn[j]; + dn[j] = dn[j + gap]; + dn[j + gap] = temp; + } + } + } +} +#endif + +/*----------------------------------------------------------------------*/ +static void showfiles(struct dnode **dn, int nfiles) +{ + int i, ncols, nrows, row, nc; + int column = 0; + int nexttab = 0; + int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */ + + if (dn == NULL || nfiles < 1) + return; + + if (all_fmt & STYLE_ONE_RECORD_FLAG) { + ncols = 1; + } else { + /* find the longest file name- use that as the column width */ + for (i = 0; i < nfiles; i++) { + int len = strlen(dn[i]->name) + +#ifdef CONFIG_SELINUX + ((all_fmt & LIST_CONTEXT) ? 33 : 0) + +#endif + ((all_fmt & LIST_INO) ? 8 : 0) + + ((all_fmt & LIST_BLOCKS) ? 5 : 0); + if (column_width < len) + column_width = len; + } + column_width += tabstops; + ncols = (int) (terminal_width / column_width); + } + + if (ncols > 1) { + nrows = nfiles / ncols; + if ((nrows * ncols) < nfiles) + nrows++; /* round up fractionals */ + } else { + nrows = nfiles; + ncols = 1; + } + + for (row = 0; row < nrows; row++) { + for (nc = 0; nc < ncols; nc++) { + /* reach into the array based on the column and row */ + i = (nc * nrows) + row; /* assume display by column */ + if (all_fmt & DISP_ROWS) + i = (row * ncols) + nc; /* display across row */ + if (i < nfiles) { + if (column > 0) { + nexttab -= column; + while (nexttab--) { + putchar(' '); + column++; + } + } + nexttab = column + column_width; + column += list_single(dn[i]); + } + } + putchar('\n'); + column = 0; + } +} + +/*----------------------------------------------------------------------*/ +static void showdirs(struct dnode **dn, int ndirs, int first) +{ + int i, nfiles; + struct dnode **subdnp; + +#ifdef CONFIG_FEATURE_LS_RECURSIVE + int dndirs; + struct dnode **dnd; +#endif + + if (dn == NULL || ndirs < 1) + return; + + for (i = 0; i < ndirs; i++) { + if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { + if (!first) + printf("\n"); + first = 0; + printf("%s:\n", dn[i]->fullname); + } + subdnp = list_dir(dn[i]->fullname); + nfiles = countfiles(subdnp); + if (nfiles > 0) { + /* list all files at this level */ +#ifdef CONFIG_FEATURE_LS_SORTFILES + shellsort(subdnp, nfiles); +#endif + showfiles(subdnp, nfiles); +#ifdef CONFIG_FEATURE_LS_RECURSIVE + if (all_fmt & DISP_RECURSIVE) { + /* recursive- list the sub-dirs */ + dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR); + dndirs = countsubdirs(subdnp, nfiles); + if (dndirs > 0) { +#ifdef CONFIG_FEATURE_LS_SORTFILES + shellsort(dnd, dndirs); +#endif + showdirs(dnd, dndirs, 0); + free(dnd); /* free the array of dnode pointers to the dirs */ + } + } + dfree(subdnp); /* free the dnodes and the fullname mem */ +#endif + } + } +} + +/*----------------------------------------------------------------------*/ +static struct dnode **list_dir(const char *path) +{ + struct dnode *dn, *cur, **dnp; + struct dirent *entry; + DIR *dir; + int i, nfiles; + + if (path == NULL) + return (NULL); + + dn = NULL; + nfiles = 0; + dir = opendir(path); + if (dir == NULL) { + bb_perror_msg("%s", path); + status = EXIT_FAILURE; + return (NULL); /* could not open the dir */ + } + while ((entry = readdir(dir)) != NULL) { + char *fullname; + + /* are we going to list the file- it may be . or .. or a hidden file */ + if (entry->d_name[0] == '.') { + if ((entry->d_name[1] == 0 || ( + entry->d_name[1] == '.' + && entry->d_name[2] == 0)) + && !(all_fmt & DISP_DOT)) + continue; + if (!(all_fmt & DISP_HIDDEN)) + continue; + } + fullname = concat_path_file(path, entry->d_name); + cur = my_stat(fullname, strrchr(fullname, '/') + 1); + if (!cur) + continue; + cur->next = dn; + dn = cur; + nfiles++; + } + closedir(dir); + + /* now that we know how many files there are + ** allocate memory for an array to hold dnode pointers + */ + if (dn == NULL) + return (NULL); + dnp = dnalloc(nfiles); + for (i = 0, cur = dn; i < nfiles; i++) { + dnp[i] = cur; /* save pointer to node in array */ + cur = cur->next; + } + + return (dnp); +} + +/*----------------------------------------------------------------------*/ +static int list_single(struct dnode *dn) +{ + int i, column = 0; + +#ifdef CONFIG_FEATURE_LS_USERNAME + char scratch[16]; +#endif +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + char *filetime; + time_t ttime, age; +#endif +#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR) + struct stat info; + char append; +#endif + + if (dn->fullname == NULL) + return (0); + +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + ttime = dn->dstat.st_mtime; /* the default time */ + if (all_fmt & TIME_ACCESS) + ttime = dn->dstat.st_atime; + if (all_fmt & TIME_CHANGE) + ttime = dn->dstat.st_ctime; + filetime = ctime(&ttime); +#endif +#ifdef CONFIG_FEATURE_LS_FILETYPES + append = append_char(dn->dstat.st_mode); +#endif + + for (i = 0; i <= 31; i++) { + switch (all_fmt & (1 << i)) { + case LIST_INO: + column += printf("%7ld ", (long int) dn->dstat.st_ino); + break; + case LIST_BLOCKS: +#if _FILE_OFFSET_BITS == 64 + column += printf("%4lld ", dn->dstat.st_blocks >> 1); +#else + column += printf("%4ld ", dn->dstat.st_blocks >> 1); +#endif + break; + case LIST_MODEBITS: + column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); + break; + case LIST_NLINKS: + column += printf("%4ld ", (long) dn->dstat.st_nlink); + break; + case LIST_ID_NAME: +#ifdef CONFIG_FEATURE_LS_USERNAME + my_getpwuid(scratch, dn->dstat.st_uid, sizeof(scratch)); + printf("%-8.8s ", scratch); + my_getgrgid(scratch, dn->dstat.st_gid, sizeof(scratch)); + printf("%-8.8s", scratch); + column += 17; + break; +#endif + case LIST_ID_NUMERIC: + column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid); + break; + case LIST_SIZE: + case LIST_DEV: + if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { + column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev), + (int) minor(dn->dstat.st_rdev)); + } else { +#ifdef CONFIG_FEATURE_HUMAN_READABLE + if (all_fmt & LS_DISP_HR) { + column += printf("%9s ", + make_human_readable_str(dn->dstat.st_size, 1, 0)); + } else +#endif + { +#if _FILE_OFFSET_BITS == 64 + column += printf("%9lld ", (long long) dn->dstat.st_size); +#else + column += printf("%9ld ", dn->dstat.st_size); +#endif + } + } + break; +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + case LIST_FULLTIME: + printf("%24.24s ", filetime); + column += 25; + break; + case LIST_DATE_TIME: + if ((all_fmt & LIST_FULLTIME) == 0) { + age = time(NULL) - ttime; + printf("%6.6s ", filetime + 4); + if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { + /* hh:mm if less than 6 months old */ + printf("%5.5s ", filetime + 11); + } else { + printf(" %4.4s ", filetime + 20); + } + column += 13; + } + break; +#endif +#ifdef CONFIG_SELINUX + case LIST_CONTEXT: + { + char context[64]; + int len = sizeof(context); + if(security_sid_to_context(dn->sid, context, &len)) + { + strcpy(context, "unknown"); + len = 7; + } + printf("%-32s ", context); + column += MAX(33, len); + } + break; +#endif + case LIST_FILENAME: +#ifdef CONFIG_FEATURE_LS_COLOR + errno = 0; + if (show_color && !lstat(dn->fullname, &info)) { + printf("\033[%d;%dm", bgcolor(info.st_mode), + fgcolor(info.st_mode)); + } +#endif + column += printf("%s", dn->name); +#ifdef CONFIG_FEATURE_LS_COLOR + if (show_color) { + printf("\033[0m"); + } +#endif + break; + case LIST_SYMLINK: + if (S_ISLNK(dn->dstat.st_mode)) { + char *lpath = xreadlink(dn->fullname); + + if (lpath) { + printf(" -> "); +#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR) + if (!stat(dn->fullname, &info)) { + append = append_char(info.st_mode); + } +#endif +#ifdef CONFIG_FEATURE_LS_COLOR + if (show_color) { + errno = 0; + printf("\033[%d;%dm", bgcolor(info.st_mode), + fgcolor(info.st_mode)); + } +#endif + column += printf("%s", lpath) + 4; +#ifdef CONFIG_FEATURE_LS_COLOR + if (show_color) { + printf("\033[0m"); + } +#endif + free(lpath); + } + } + break; +#ifdef CONFIG_FEATURE_LS_FILETYPES + case LIST_FILETYPE: + if (append != '\0') { + printf("%1c", append); + column++; + } + break; +#endif + } + } + + return column; +} + +/*----------------------------------------------------------------------*/ + +/* "[-]Cadil1", POSIX mandated options, busybox always supports */ +/* "[-]gnsx", POSIX non-mandated options, busybox always supports */ +/* "[-]Ak" GNU options, busybox always supports */ +/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */ +/* "[-]p", POSIX non-mandated options, busybox optionally supports */ +/* "[-]SXvThw", GNU options, busybox optionally supports */ +/* "[-]K", SELinux mandated options, busybox optionally supports */ +/* "[-]e", I think we made this one up */ + +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS +# define LS_STR_TIMESTAMPS "cetu" +#else +# define LS_STR_TIMESTAMPS "" +#endif + +#ifdef CONFIG_FEATURE_LS_SORTFILES +# define LS_STR_SORTFILES "SXrv" +#else +# define LS_STR_SORTFILES "" +#endif + +#ifdef CONFIG_FEATURE_LS_FILETYPES +# define LS_STR_FILETYPES "Fp" +#else +# define LS_STR_FILETYPES "" +#endif + +#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS +# define LS_STR_FOLLOW_LINKS "L" +#else +# define LS_STR_FOLLOW_LINKS "" +#endif + +#ifdef CONFIG_FEATURE_LS_RECURSIVE +# define LS_STR_RECURSIVE "R" +#else +# define LS_STR_RECURSIVE "" +#endif + +#ifdef CONFIG_FEATURE_HUMAN_READABLE +# define LS_STR_HUMAN_READABLE "h" +#else +# define LS_STR_HUMAN_READABLE "" +#endif + +#ifdef CONFIG_SELINUX +# define LS_STR_SELINUX "K" +#else +# define LS_STR_SELINUX "" +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +# define LS_STR_AUTOWIDTH "T:w:" +#else +# define LS_STR_AUTOWIDTH "" +#endif + +static const char ls_options[]="Cadil1gnsxAk" \ + LS_STR_TIMESTAMPS \ + LS_STR_SORTFILES \ + LS_STR_FILETYPES \ + LS_STR_FOLLOW_LINKS \ + LS_STR_RECURSIVE \ + LS_STR_HUMAN_READABLE \ + LS_STR_SELINUX \ + LS_STR_AUTOWIDTH; + +#define LIST_MASK_TRIGGER 0 +#define STYLE_MASK_TRIGGER STYLE_MASK +#define SORT_MASK_TRIGGER SORT_MASK +#define DISP_MASK_TRIGGER DISP_ROWS +#define TIME_MASK_TRIGGER TIME_MASK + +static const unsigned opt_flags[] = { + LIST_SHORT | STYLE_COLUMNS, /* C */ + DISP_HIDDEN | DISP_DOT, /* a */ + DISP_NOLIST, /* d */ + LIST_INO, /* i */ + LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */ + LIST_SHORT | STYLE_SINGLE, /* 1 */ + 0, /* g - ingored */ + LIST_ID_NUMERIC, /* n */ + LIST_BLOCKS, /* s */ + DISP_ROWS, /* x */ + DISP_HIDDEN, /* A */ +#ifdef CONFIG_SELINUX + LIST_CONTEXT, /* k */ +#else + 0, /* k - ingored */ +#endif +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS +# ifdef CONFIG_FEATURE_LS_SORTFILES + TIME_CHANGE | SORT_CTIME, /* c */ +# else + TIME_CHANGE, /* c */ +# endif + LIST_FULLTIME, /* e */ +# ifdef CONFIG_FEATURE_LS_SORTFILES + SORT_MTIME, /* t */ +# else + 0, /* t - ignored -- is this correct? */ +# endif +# ifdef CONFIG_FEATURE_LS_SORTFILES + TIME_ACCESS | SORT_ATIME, /* u */ +# else + TIME_ACCESS, /* u */ +# endif +#endif +#ifdef CONFIG_FEATURE_LS_SORTFILES + SORT_SIZE, /* S */ + SORT_EXT, /* X */ + SORT_ORDER_REVERSE, /* r */ + SORT_VERSION, /* v */ +#endif +#ifdef CONFIG_FEATURE_LS_FILETYPES + LIST_FILETYPE | LIST_EXEC, /* F */ + LIST_FILETYPE, /* p */ +#endif +#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS + FOLLOW_LINKS, /* L */ +#endif +#ifdef CONFIG_FEATURE_LS_RECURSIVE + DISP_RECURSIVE, /* R */ +#endif +#ifdef CONFIG_FEATURE_HUMAN_READABLE + LS_DISP_HR, /* h */ +#endif +#ifdef CONFIG_SELINUX + LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */ +#endif + (1U<<31) +}; + + +/*----------------------------------------------------------------------*/ + +extern int ls_main(int argc, char **argv) +{ + struct dnode **dnd; + struct dnode **dnf; + struct dnode **dnp; + struct dnode *dn; + struct dnode *cur; + long opt; + int nfiles = 0; + int dnfiles; + int dndirs; + int oi; + int ac; + int i; + char **av; +#ifdef CONFIG_FEATURE_AUTOWIDTH + char *tabstops_str = NULL; + char *terminal_width_str = NULL; +#endif + +#ifdef CONFIG_SELINUX + is_flask_enabled_flag = is_flask_enabled(); +#endif + + all_fmt = LIST_SHORT | DISP_NORMAL | STYLE_AUTO +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + | TIME_MOD +#endif +#ifdef CONFIG_FEATURE_LS_SORTFILES + | SORT_NAME | SORT_ORDER_FORWARD +#endif + ; + +#ifdef CONFIG_FEATURE_AUTOWIDTH + /* Obtain the terminal width. */ + get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL); + /* Go one less... */ + terminal_width--; +#endif + +#ifdef CONFIG_FEATURE_LS_COLOR + if (isatty(STDOUT_FILENO)) + show_color = 1; +#endif + + /* process options */ +#ifdef CONFIG_FEATURE_AUTOWIDTH + opt = bb_getopt_ulflags(argc, argv, ls_options, &tabstops_str, &terminal_width_str); + if (tabstops_str) { + tabstops = atoi(tabstops_str); + } + if (terminal_width_str) { + terminal_width = atoi(terminal_width_str); + } +#else + opt = bb_getopt_ulflags(argc, argv, ls_options); +#endif + for (i = 0; opt_flags[i] != (1U<<31); i++) { + if (opt & (1 << i)) { + unsigned int flags = opt_flags[i]; + if (flags & LIST_MASK_TRIGGER) { + all_fmt &= ~LIST_MASK; + } + if (flags & STYLE_MASK_TRIGGER) { + all_fmt &= ~STYLE_MASK; + } +#ifdef CONFIG_FEATURE_LS_SORTFILES + if (flags & SORT_MASK_TRIGGER) { + all_fmt &= ~SORT_MASK; + } +#endif + if (flags & DISP_MASK_TRIGGER) { + all_fmt &= ~DISP_MASK; + } +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + if (flags & TIME_MASK_TRIGGER) { + all_fmt &= ~TIME_MASK; + } +#endif + if (flags & LIST_CONTEXT) { + all_fmt |= STYLE_SINGLE; + } +#ifdef CONFIG_FEATURE_HUMAN_READABLE + if (opt == 'l') { + all_fmt &= ~LS_DISP_HR; + } +#endif + all_fmt |= flags; + } + } + + /* sort out which command line options take precedence */ +#ifdef CONFIG_FEATURE_LS_RECURSIVE + if (all_fmt & DISP_NOLIST) + all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ +#endif +#if defined (CONFIG_FEATURE_LS_TIMESTAMPS) && defined (CONFIG_FEATURE_LS_SORTFILES) + if (all_fmt & TIME_CHANGE) + all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; + if (all_fmt & TIME_ACCESS) + all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; +#endif + if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */ + all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC); +#ifdef CONFIG_FEATURE_LS_USERNAME + if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC)) + all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ +#endif + + /* choose a display format */ + if ((all_fmt & STYLE_MASK) == STYLE_AUTO) +#if STYLE_AUTO != 0 + all_fmt = (all_fmt & ~STYLE_MASK) + | (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE); +#else + all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE); +#endif + + /* + * when there are no cmd line args we have to supply a default "." arg. + * we will create a second argv array, "av" that will hold either + * our created "." arg, or the real cmd line args. The av array + * just holds the pointers- we don't move the date the pointers + * point to. + */ + ac = argc - optind; /* how many cmd line args are left */ + if (ac < 1) { + av = (char **) xcalloc((size_t) 1, (size_t) (sizeof(char *))); + av[0] = bb_xstrdup("."); + ac = 1; + } else { + av = (char **) xcalloc((size_t) ac, (size_t) (sizeof(char *))); + for (oi = 0; oi < ac; oi++) { + av[oi] = argv[optind++]; /* copy pointer to real cmd line arg */ + } + } + + /* now, everything is in the av array */ + if (ac > 1) + all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ + + /* stuff the command line file names into an dnode array */ + dn = NULL; + for (oi = 0; oi < ac; oi++) { + char *fullname = bb_xstrdup(av[oi]); + + cur = my_stat(fullname, fullname); + if (!cur) + continue; + cur->next = dn; + dn = cur; + nfiles++; + } + + /* now that we know how many files there are + ** allocate memory for an array to hold dnode pointers + */ + dnp = dnalloc(nfiles); + for (i = 0, cur = dn; i < nfiles; i++) { + dnp[i] = cur; /* save pointer to node in array */ + cur = cur->next; + } + + if (all_fmt & DISP_NOLIST) { +#ifdef CONFIG_FEATURE_LS_SORTFILES + shellsort(dnp, nfiles); +#endif + if (nfiles > 0) + showfiles(dnp, nfiles); + } else { + dnd = splitdnarray(dnp, nfiles, SPLIT_DIR); + dnf = splitdnarray(dnp, nfiles, SPLIT_FILE); + dndirs = countdirs(dnp, nfiles); + dnfiles = nfiles - dndirs; + if (dnfiles > 0) { +#ifdef CONFIG_FEATURE_LS_SORTFILES + shellsort(dnf, dnfiles); +#endif + showfiles(dnf, dnfiles); + } + if (dndirs > 0) { +#ifdef CONFIG_FEATURE_LS_SORTFILES + shellsort(dnd, dndirs); +#endif + showdirs(dnd, dndirs, dnfiles == 0); + } + } + return (status); +} diff --git a/busybox/coreutils/md5_sha1_sum.c b/busybox/coreutils/md5_sha1_sum.c new file mode 100644 index 000000000..bd1c9fc29 --- /dev/null +++ b/busybox/coreutils/md5_sha1_sum.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003-2004 Erik Andersen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + + +#define FLAG_SILENT 1 +#define FLAG_CHECK 2 +#define FLAG_WARN 4 + +/* This might be useful elsewhere */ +static unsigned char *hash_bin_to_hex(unsigned char *hash_value, + unsigned char hash_length) +{ + int x, len, max; + unsigned char *hex_value; + + max = (hash_length * 2) + 2; + hex_value = xmalloc(max); + for (x = len = 0; x < hash_length; x++) { + len += snprintf(hex_value + len, max - len, "%02x", hash_value[x]); + } + return (hex_value); +} + +static uint8_t *hash_file(const char *filename, uint8_t hash_algo) +{ + uint8_t *hash_value_bin; + uint8_t *hash_value = NULL; + uint8_t hash_length; + int src_fd; + + if (strcmp(filename, "-") == 0) { + src_fd = STDIN_FILENO; + } else { + src_fd = open(filename, O_RDONLY); + } + + if (hash_algo == HASH_MD5) { + hash_length = 16; + } else { + hash_length = 20; + } + + hash_value_bin = xmalloc(hash_length); + + if ((src_fd != -1) && (hash_fd(src_fd, -1, hash_algo, hash_value_bin) != -2)) { + hash_value = hash_bin_to_hex(hash_value_bin, hash_length); + } else { + bb_perror_msg("%s", filename); + } + + close(src_fd); + + return(hash_value); +} + +/* This could become a common function for md5 as well, by using md5_stream */ +extern int hash_files(int argc, char **argv, const uint8_t hash_algo) +{ + int return_value = EXIT_SUCCESS; + uint8_t *hash_value; + +#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK + unsigned int flags; + + flags = bb_getopt_ulflags(argc, argv, "scw"); +#endif + +#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK + if (!(flags & FLAG_CHECK)) { + if (flags & FLAG_SILENT) { + bb_error_msg_and_die + ("the -s option is meaningful only when verifying checksums"); + } else if (flags & FLAG_WARN) { + bb_error_msg_and_die + ("the -w option is meaningful only when verifying checksums"); + } + } +#endif + + if (argc == optind) { + argv[argc++] = "-"; + } +#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK + if (flags & FLAG_CHECK) { + FILE *pre_computed_stream; + int count_total = 0; + int count_failed = 0; + unsigned char *file_ptr = argv[optind]; + char *line; + + if (optind + 1 != argc) { + bb_error_msg_and_die + ("only one argument may be specified when using -c"); + } + + if (strcmp(file_ptr, "-") == 0) { + pre_computed_stream = stdin; + } else { + pre_computed_stream = bb_xfopen(file_ptr, "r"); + } + + while ((line = bb_get_chomped_line_from_file(pre_computed_stream)) != NULL) { + char *filename_ptr; + + count_total++; + filename_ptr = strstr(line, " "); + if (filename_ptr == NULL) { + if (flags & FLAG_WARN) { + bb_error_msg("Invalid format"); + } + free(line); + continue; + } + *filename_ptr = '\0'; + filename_ptr += 2; + + hash_value = hash_file(filename_ptr, hash_algo); + + if (hash_value && (strcmp(hash_value, line) == 0)) { + if (!(flags & FLAG_SILENT)) + printf("%s: OK\n", filename_ptr); + } else { + if (!(flags & FLAG_SILENT)) + printf("%s: FAILED\n", filename_ptr); + count_failed++; + return_value = EXIT_FAILURE; + } + /* possible free(NULL) */ + free(hash_value); + free(line); + } + if (count_failed && !(flags & FLAG_SILENT)) { + bb_error_msg("WARNING: %d of %d computed checksums did NOT match", + count_failed, count_total); + } + if (bb_fclose_nonstdin(pre_computed_stream) == EOF) { + bb_perror_msg_and_die("Couldnt close file %s", file_ptr); + } + } else +#endif + { + uint8_t hash_length; + + if (hash_algo == HASH_MD5) { + hash_length = 16; + } else { + hash_length = 20; + } + hash_value = xmalloc(hash_length); + + while (optind < argc) { + unsigned char *file_ptr = argv[optind++]; + + hash_value = hash_file(file_ptr, hash_algo); + if (hash_value == NULL) { + return_value = EXIT_FAILURE; + } else { + printf("%s %s\n", hash_value, file_ptr); + free(hash_value); + } + } + } + return (return_value); +} + +#ifdef CONFIG_MD5SUM +extern int md5sum_main(int argc, char **argv) +{ + return(hash_files(argc, argv, HASH_MD5)); +} +#endif + +#ifdef CONFIG_SHA1SUM +extern int sha1sum_main(int argc, char **argv) +{ + return(hash_files(argc, argv, HASH_SHA1)); +} +#endif diff --git a/busybox/coreutils/mkdir.c b/busybox/coreutils/mkdir.c new file mode 100644 index 000000000..50364f17f --- /dev/null +++ b/busybox/coreutils/mkdir.c @@ -0,0 +1,75 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Fixed broken permission setting when -p was used; especially in + * conjunction with -m. + */ + +#include +#include +#include +#include "busybox.h" + +static const struct option mkdir_long_options[] = { + { "mode", 1, NULL, 'm' }, + { "parents", 0, NULL, 'p' }, + { 0, 0, 0, 0 } +}; + +extern int mkdir_main (int argc, char **argv) +{ + mode_t mode = (mode_t)(-1); + int status = EXIT_SUCCESS; + int flags = 0; + unsigned long opt; + char *smode; + + bb_applet_long_options = mkdir_long_options; + opt = bb_getopt_ulflags(argc, argv, "m:p", &smode); + if(opt & 1) { + mode = 0777; + if (!bb_parse_mode (smode, &mode)) { + bb_error_msg_and_die ("invalid mode `%s'", smode); + } + } + if(opt & 2) + flags |= FILEUTILS_RECUR; + + if (optind == argc) { + bb_show_usage(); + } + + argv += optind; + + do { + if (bb_make_directory(*argv, mode, flags)) { + status = EXIT_FAILURE; + } + } while (*++argv); + + return status; +} diff --git a/busybox/coreutils/mkfifo.c b/busybox/coreutils/mkfifo.c new file mode 100644 index 000000000..77e0e6dd8 --- /dev/null +++ b/busybox/coreutils/mkfifo.c @@ -0,0 +1,51 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkfifo implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */ + +#include +#include +#include +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +extern int mkfifo_main(int argc, char **argv) +{ + mode_t mode; + int retval = EXIT_SUCCESS; + + mode = getopt_mk_fifo_nod(argc, argv); + + if (!*(argv += optind)) { + bb_show_usage(); + } + + do { + if (mkfifo(*argv, mode) < 0) { + bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */ + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} diff --git a/busybox/coreutils/mknod.c b/busybox/coreutils/mknod.c new file mode 100644 index 000000000..7b2467b8f --- /dev/null +++ b/busybox/coreutils/mknod.c @@ -0,0 +1,63 @@ +/* vi: set sw=4 ts=4: */ +/* + * mknod implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include +#include +#include +#include +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +static const char modes_chars[] = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 }; +static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK }; + +extern int mknod_main(int argc, char **argv) +{ + mode_t mode; + dev_t dev; + const char *name; + + mode = getopt_mk_fifo_nod(argc, argv); + argv += optind; + argc -= optind; + + if ((argc >= 2) && ((name = strchr(modes_chars, argv[1][0])) != NULL)) { + mode |= modes_cubp[(int)(name[4])]; + + dev = 0; + if ((*name != 'p') && ((argc -= 2) == 2)) { + dev = (bb_xgetularg10_bnd(argv[2], 0, 255) << 8) + + bb_xgetularg10_bnd(argv[3], 0, 255); + } + + if (argc == 2) { + name = *argv; + if (mknod(name, mode, dev) == 0) { + return EXIT_SUCCESS; + } + bb_perror_msg_and_die("%s", name); + } + } + bb_show_usage(); +} diff --git a/busybox/coreutils/mv.c b/busybox/coreutils/mv.c new file mode 100644 index 000000000..4f08dedc0 --- /dev/null +++ b/busybox/coreutils/mv.c @@ -0,0 +1,139 @@ +/* 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 + * + */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction and improved error checking. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +static const struct option mv_long_options[] = { + { "interactive", 0, NULL, 'i' }, + { "force", 0, NULL, 'f' }, + { 0, 0, 0, 0 } +}; + +#define OPT_FILEUTILS_FORCE 1 +#define OPT_FILEUTILS_INTERACTIVE 2 + +static const char fmt[] = "cannot overwrite %sdirectory with %sdirectory"; + +extern int mv_main(int argc, char **argv) +{ + struct stat dest_stat; + const char *last; + const char *dest; + unsigned long flags; + int dest_exists; + int status = 0; + + bb_applet_long_options = mv_long_options; + bb_opt_complementaly = "f-i:i-f"; + flags = bb_getopt_ulflags(argc, argv, "fi"); + if (optind + 2 > argc) { + bb_show_usage(); + } + + last = argv[argc - 1]; + argv += optind; + + if (optind + 2 == argc) { + if ((dest_exists = cp_mv_stat(last, &dest_stat)) < 0) { + return 1; + } + + if (!(dest_exists & 2)) { + dest = last; + goto DO_MOVE; + } + } + + do { + dest = concat_path_file(last, bb_get_last_path_component(*argv)); + + if ((dest_exists = cp_mv_stat(dest, &dest_stat)) < 0) { + goto RET_1; + } + +DO_MOVE: + + if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) && + ((access(dest, W_OK) < 0 && isatty(0)) || + (flags & OPT_FILEUTILS_INTERACTIVE))) { + if (fprintf(stderr, "mv: overwrite `%s'? ", dest) < 0) { + goto RET_1; /* Ouch! fprintf failed! */ + } + if (!bb_ask_confirmation()) { + goto RET_0; + } + } + if (rename(*argv, dest) < 0) { + struct stat source_stat; + int source_exists; + + if (errno != EXDEV) { + bb_perror_msg("unable to rename `%s'", *argv); + } + else if ((source_exists = cp_mv_stat(*argv, &source_stat)) >= 0) { + if (dest_exists) { + if (dest_exists == 3) { + if (source_exists != 3) { + bb_error_msg(fmt, "", "non-"); + goto RET_1; + } + } else { + if (source_exists == 3) { + bb_error_msg(fmt, "non-", ""); + goto RET_1; + } + } + if (unlink(dest) < 0) { + bb_perror_msg("cannot remove `%s'", dest); + goto RET_1; + } + } + if ((copy_file(*argv, dest, + FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS) >= 0) && + (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0)) { + goto RET_0; + } + } +RET_1: + status = 1; + } +RET_0: + if (dest != last) { + free((void *) dest); + } + } while (*++argv != last); + + return (status); +} diff --git a/busybox/coreutils/od.c b/busybox/coreutils/od.c new file mode 100644 index 000000000..6a138e838 --- /dev/null +++ b/busybox/coreutils/od.c @@ -0,0 +1,231 @@ +/* + * od implementation for busybox + * Based on code from util-linux v 2.11l + * + * Copyright (c) 1990 + * The Regents of the University of California. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Original copyright notice is retained at the end of this file. + */ + +#include +#include +#include +#include +#include "busybox.h" +#include "dump.h" + +#define isdecdigit(c) (isdigit)(c) +#define ishexdigit(c) (isxdigit)(c) + +static void +odoffset(int argc, char ***argvp) +{ + register char *num, *p; + int base; + char *end; + + /* + * The offset syntax of od(1) was genuinely bizarre. First, if + * it started with a plus it had to be an offset. Otherwise, if + * there were at least two arguments, a number or lower-case 'x' + * followed by a number makes it an offset. By default it was + * octal; if it started with 'x' or '0x' it was hex. If it ended + * in a '.', it was decimal. If a 'b' or 'B' was appended, it + * multiplied the number by 512 or 1024 byte units. There was + * no way to assign a block count to a hex offset. + * + * We assumes it's a file if the offset is bad. + */ + p = **argvp; + + if (!p) { + /* hey someone is probably piping to us ... */ + return; + } + + if ((*p != '+') + && (argc < 2 + || (!isdecdigit(p[0]) + && ((p[0] != 'x') || !ishexdigit(p[1]))))) + return; + + base = 0; + /* + * bb_dump_skip over leading '+', 'x[0-9a-fA-f]' or '0x', and + * set base. + */ + if (p[0] == '+') + ++p; + if (p[0] == 'x' && ishexdigit(p[1])) { + ++p; + base = 16; + } else if (p[0] == '0' && p[1] == 'x') { + p += 2; + base = 16; + } + + /* bb_dump_skip over the number */ + if (base == 16) + for (num = p; ishexdigit(*p); ++p); + else + for (num = p; isdecdigit(*p); ++p); + + /* check for no number */ + if (num == p) + return; + + /* if terminates with a '.', base is decimal */ + if (*p == '.') { + if (base) + return; + base = 10; + } + + bb_dump_skip = strtol(num, &end, base ? base : 8); + + /* if end isn't the same as p, we got a non-octal digit */ + if (end != p) + bb_dump_skip = 0; + else { + if (*p) { + if (*p == 'b') { + bb_dump_skip *= 512; + ++p; + } else if (*p == 'B') { + bb_dump_skip *= 1024; + ++p; + } + } + if (*p) + bb_dump_skip = 0; + else { + ++*argvp; + /* + * If the offset uses a non-octal base, the base of + * the offset is changed as well. This isn't pretty, + * but it's easy. + */ +#define TYPE_OFFSET 7 + { + char x_or_d; + if (base == 16) { + x_or_d = 'x'; + goto DO_X_OR_D; + } + if (base == 10) { + x_or_d = 'd'; + DO_X_OR_D: + bb_dump_fshead->nextfu->fmt[TYPE_OFFSET] + = bb_dump_fshead->nextfs->nextfu->fmt[TYPE_OFFSET] + = x_or_d; + } + } + } + } +} + +static const char * const add_strings[] = { + "16/1 \"%3_u \" \"\\n\"", /* a */ + "8/2 \" %06o \" \"\\n\"", /* B, o */ + "16/1 \"%03o \" \"\\n\"", /* b */ + "16/1 \"%3_c \" \"\\n\"", /* c */ + "8/2 \" %05u \" \"\\n\"", /* d */ + "4/4 \" %010u \" \"\\n\"", /* D */ + "2/8 \" %21.14e \" \"\\n\"", /* e (undocumented in od), F */ + "4/4 \" %14.7e \" \"\\n\"", /* f */ + "4/4 \" %08x \" \"\\n\"", /* H, X */ + "8/2 \" %04x \" \"\\n\"", /* h, x */ + "4/4 \" %11d \" \"\\n\"", /* I, L, l */ + "8/2 \" %6d \" \"\\n\"", /* i */ + "4/4 \" %011o \" \"\\n\"", /* O */ +}; + +static const signed char od_opts[] = "aBbcDdeFfHhIiLlOoXxv"; + +static const signed char od_o2si[] = { + 0, 1, 2, 3, 5, + 4, 6, 6, 7, 8, + 9, 0xa, 0xb, 0xa, 0xa, + 0xb, 1, 8, 9, +}; + +int od_main(int argc, char **argv) +{ + int ch; + int first = 1; + signed char *p; + bb_dump_vflag = FIRST; + bb_dump_length = -1; + + while ((ch = getopt(argc, argv, od_opts)) > 0) { + if (ch == 'v') { + bb_dump_vflag = ALL; + } else if (((p = strchr(od_opts, ch)) != NULL) && (*p >= 0)) { + if (first) { + first = 0; + bb_dump_add("\"%07.7_Ao\n\""); + bb_dump_add("\"%07.7_ao \""); + } else { + bb_dump_add("\" \""); + } + bb_dump_add(add_strings[od_o2si[(int)(p-od_opts)]]); + } else { /* P, p, s, w, or other unhandled */ + bb_show_usage(); + } + } + if (!bb_dump_fshead) { + bb_dump_add("\"%07.7_Ao\n\""); + bb_dump_add("\"%07.7_ao \" 8/2 \"%06o \" \"\\n\""); + } + + argc -= optind; + argv += optind; + + odoffset(argc, &argv); + + return(bb_dump_dump(argv)); +} + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/busybox/coreutils/printf.c b/busybox/coreutils/printf.c new file mode 100644 index 000000000..da5e46a58 --- /dev/null +++ b/busybox/coreutils/printf.c @@ -0,0 +1,316 @@ +/* vi: set sw=4 ts=4: */ +/* printf - format and print data + Copyright (C) 90, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Usage: printf format [argument...] + + A front end to the printf function that lets it be used from the shell. + + Backslash escapes: + + \" = double quote + \\ = backslash + \a = alert (bell) + \b = backspace + \c = produce no further output + \f = form feed + \n = new line + \r = carriage return + \t = horizontal tab + \v = vertical tab + \0ooo = octal number (ooo is 0 to 3 digits) + \xhhh = hexadecimal number (hhh is 1 to 3 digits) + + Additional directive: + + %b = print an argument string, interpreting backslash escapes + + The `format' argument is re-used as many times as necessary + to convert all of the given arguments. + + David MacKenzie */ + + +// 19990508 Busy Boxed! Dave Cinege + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +static double xstrtod __P((char *s)); +static long xstrtol __P((char *s)); +static unsigned long xstrtoul __P((char *s)); +static void print_esc_string __P((char *str)); +static int print_formatted __P((char *format, int argc, char **argv)); +static void print_direc __P( (char *start, size_t length, + int field_width, int precision, char *argument)); + +int printf_main(int argc, char **argv) +{ + char *format; + int args_used; + + if (argc <= 1 || **(argv + 1) == '-') { + bb_show_usage(); + } + + format = argv[1]; + argc -= 2; + argv += 2; + + do { + args_used = print_formatted(format, argc, argv); + argc -= args_used; + argv += args_used; + } + while (args_used > 0 && argc > 0); + +/* + if (argc > 0) + fprintf(stderr, "excess args ignored"); +*/ + + return EXIT_SUCCESS; +} + +/* Print the text in FORMAT, using ARGV (with ARGC elements) for + arguments to any `%' directives. + Return the number of elements of ARGV used. */ + +static int print_formatted(char *format, int argc, char **argv) +{ + int save_argc = argc; /* Preserve original value. */ + char *f; /* Pointer into `format'. */ + char *direc_start; /* Start of % directive. */ + size_t direc_length; /* Length of % directive. */ + int field_width; /* Arg to first '*', or -1 if none. */ + int precision; /* Arg to second '*', or -1 if none. */ + + for (f = format; *f; ++f) { + switch (*f) { + case '%': + direc_start = f++; + direc_length = 1; + field_width = precision = -1; + if (*f == '%') { + putchar('%'); + break; + } + if (*f == 'b') { + if (argc > 0) { + print_esc_string(*argv); + ++argv; + --argc; + } + break; + } + if (strchr("-+ #", *f)) { + ++f; + ++direc_length; + } + if (*f == '*') { + ++f; + ++direc_length; + if (argc > 0) { + field_width = xstrtoul(*argv); + ++argv; + --argc; + } else + field_width = 0; + } else + while (isdigit(*f)) { + ++f; + ++direc_length; + } + if (*f == '.') { + ++f; + ++direc_length; + if (*f == '*') { + ++f; + ++direc_length; + if (argc > 0) { + precision = xstrtoul(*argv); + ++argv; + --argc; + } else + precision = 0; + } else + while (isdigit(*f)) { + ++f; + ++direc_length; + } + } + if (*f == 'l' || *f == 'L' || *f == 'h') { + ++f; + ++direc_length; + } + /* + if (!strchr ("diouxXfeEgGcs", *f)) + fprintf(stderr, "%%%c: invalid directive", *f); + */ + ++direc_length; + if (argc > 0) { + print_direc(direc_start, direc_length, field_width, + precision, *argv); + ++argv; + --argc; + } else + print_direc(direc_start, direc_length, field_width, + precision, ""); + break; + + case '\\': + if (*++f == 'c') + exit(0); + putchar(bb_process_escape_sequence((const char **)&f)); + f--; + break; + + default: + putchar(*f); + } + } + + return save_argc - argc; +} + +static void +print_direc(char *start, size_t length, int field_width, int precision, + char *argument) +{ + char *p; /* Null-terminated copy of % directive. */ + + p = xmalloc((unsigned) (length + 1)); + strncpy(p, start, length); + p[length] = 0; + + switch (p[length - 1]) { + case 'd': + case 'i': + if (field_width < 0) { + if (precision < 0) + printf(p, xstrtol(argument)); + else + printf(p, precision, xstrtol(argument)); + } else { + if (precision < 0) + printf(p, field_width, xstrtol(argument)); + else + printf(p, field_width, precision, xstrtol(argument)); + } + break; + + case 'o': + case 'u': + case 'x': + case 'X': + if (field_width < 0) { + if (precision < 0) + printf(p, xstrtoul(argument)); + else + printf(p, precision, xstrtoul(argument)); + } else { + if (precision < 0) + printf(p, field_width, xstrtoul(argument)); + else + printf(p, field_width, precision, xstrtoul(argument)); + } + break; + + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + if (field_width < 0) { + if (precision < 0) + printf(p, xstrtod(argument)); + else + printf(p, precision, xstrtod(argument)); + } else { + if (precision < 0) + printf(p, field_width, xstrtod(argument)); + else + printf(p, field_width, precision, xstrtod(argument)); + } + break; + + case 'c': + printf(p, *argument); + break; + + case 's': + if (field_width < 0) { + if (precision < 0) + printf(p, argument); + else + printf(p, precision, argument); + } else { + if (precision < 0) + printf(p, field_width, argument); + else + printf(p, field_width, precision, argument); + } + break; + } + + free(p); +} + +static unsigned long xstrtoul(char *arg) +{ + unsigned long result; + if (safe_strtoul(arg, &result)) + fprintf(stderr, "%s", arg); + return result; +} + +static long xstrtol(char *arg) +{ + long result; + if (safe_strtol(arg, &result)) + fprintf(stderr, "%s", arg); + return result; +} + +static double xstrtod(char *arg) +{ + double result; + if (safe_strtod(arg, &result)) + fprintf(stderr, "%s", arg); + return result; +} + +static void print_esc_string(char *str) +{ + for (; *str; str++) { + if (*str == '\\') { + str++; + putchar(bb_process_escape_sequence((const char **)&str)); + } else { + putchar(*str); + } + + } +} diff --git a/busybox/coreutils/pwd.c b/busybox/coreutils/pwd.c new file mode 100644 index 000000000..7e0dc056a --- /dev/null +++ b/busybox/coreutils/pwd.c @@ -0,0 +1,37 @@ +/* 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 + * + */ + +#include +#include +#include "busybox.h" + +extern int pwd_main(int argc, char **argv) +{ + char *buf; + + if ((buf = xgetcwd(NULL)) != NULL) { + puts(buf); + bb_fflush_stdout_and_exit(EXIT_SUCCESS); + } + + return EXIT_FAILURE; +} diff --git a/busybox/coreutils/realpath.c b/busybox/coreutils/realpath.c new file mode 100644 index 000000000..ec98221ad --- /dev/null +++ b/busybox/coreutils/realpath.c @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Now does proper error checking on output and returns a failure exit code + * if one or more paths can not be resolved. + */ + +#include +#include +#include "busybox.h" + +int realpath_main(int argc, char **argv) +{ + int retval = EXIT_SUCCESS; + + RESERVE_CONFIG_BUFFER(resolved_path, PATH_MAX); + + if (--argc == 0) { + bb_show_usage(); + } + + do { + argv++; + if (realpath(*argv, resolved_path) != NULL) { + puts(resolved_path); + } else { + retval = EXIT_FAILURE; + bb_perror_msg("%s", *argv); + } + } while (--argc); + +#ifdef CONFIG_FEATURE_CLEAN_UP + RELEASE_CONFIG_BUFFER(resolved_path); +#endif + + bb_fflush_stdout_and_exit(retval); +} diff --git a/busybox/coreutils/rm.c b/busybox/coreutils/rm.c new file mode 100644 index 000000000..39609e7b8 --- /dev/null +++ b/busybox/coreutils/rm.c @@ -0,0 +1,66 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. + */ + +#include +#include "busybox.h" + +extern int rm_main(int argc, char **argv) +{ + int status = 0; + int flags = 0; + unsigned long opt; + + bb_opt_complementaly = "f-i:i-f"; + opt = bb_getopt_ulflags(argc, argv, "fiRr"); + if(opt & 1) + flags |= FILEUTILS_FORCE; + if(opt & 2) + flags |= FILEUTILS_INTERACTIVE; + if(opt & 12) + flags |= FILEUTILS_RECUR; + + if (*(argv += optind) != NULL) { + do { + const char *base = bb_get_last_path_component(*argv); + + if ((base[0] == '.') && (!base[1] || ((base[1] == '.') && !base[2]))) { + bb_error_msg("cannot remove `.' or `..'"); + } else if (remove_file(*argv, flags) >= 0) { + continue; + } + status = 1; + } while (*++argv); + } else if (!(flags & FILEUTILS_FORCE)) { + bb_show_usage(); + } + + return status; +} diff --git a/busybox/coreutils/rmdir.c b/busybox/coreutils/rmdir.c new file mode 100644 index 000000000..a10e5bb4f --- /dev/null +++ b/busybox/coreutils/rmdir.c @@ -0,0 +1,73 @@ +/* vi: set sw=4 ts=4: */ +/* + * rmdir implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */ + +#include +#include +#include +#include "busybox.h" + +extern int rmdir_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int flags; + int do_dot; + char *path; + + flags = bb_getopt_ulflags(argc, argv, "p"); + + argv += optind; + + if (!*argv) { + bb_show_usage(); + } + + do { + path = *argv; + + /* Record if the first char was a '.' so we can use dirname later. */ + do_dot = (*path == '.'); + + do { + if (rmdir(path) < 0) { + bb_perror_msg("`%s'", path); /* Match gnu rmdir msg. */ + status = EXIT_FAILURE; + } else if (flags) { + /* Note: path was not empty or null since rmdir succeeded. */ + path = dirname(path); + /* Path is now just the parent component. Note that dirname + * returns "." if there are no parents. We must distinguish + * this from the case of the original path starting with '.'. + */ + if (do_dot || (*path != '.') || path[1]) { + continue; + } + } + break; + } while (1); + + } while (*++argv); + + return status; +} diff --git a/busybox/coreutils/seq.c b/busybox/coreutils/seq.c new file mode 100644 index 000000000..8006be83d --- /dev/null +++ b/busybox/coreutils/seq.c @@ -0,0 +1,51 @@ +/* vi: set sw=4 ts=4: */ +/* + * seq implementation for busybox + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "busybox.h" + +extern int seq_main(int argc, char **argv) +{ + double last; + double first = 1; + double increment = 1; + double i; + + if (argc == 4) { + first = atof(argv[1]); + increment = atof(argv[2]); + } else if (argc == 3) { + first = atof(argv[1]); + } else if (argc != 2) { + bb_show_usage(); + } + last = atof(argv[argc - 1]); + + /* You should note that this is pos-5.0.91 semantics, -- FK. */ + if ((first > last) && (increment > 0)) { + return EXIT_SUCCESS; + } + + for (i = first; ((first <= last) ? (i <= last) : (i >= last)); + i += increment) { + printf("%g\n", i); + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/sleep.c b/busybox/coreutils/sleep.c new file mode 100644 index 000000000..506192dd3 --- /dev/null +++ b/busybox/coreutils/sleep.c @@ -0,0 +1,86 @@ +/* vi: set sw=4 ts=4: */ +/* + * sleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Rewritten to do proper arg and error checking. + * Also, added a 'fancy' configuration to accept multiple args with + * time suffixes for seconds, minutes, hours, and days. + */ + +#include +#include +#include +#include "busybox.h" + +#ifdef CONFIG_FEATURE_FANCY_SLEEP +static const struct suffix_mult sleep_suffixes[] = { + { "s", 1 }, + { "m", 60 }, + { "h", 60*60 }, + { "d", 24*60*60 }, + { NULL, 0 } +}; +#endif + +extern int sleep_main(int argc, char **argv) +{ + unsigned int duration; + +#ifdef CONFIG_FEATURE_FANCY_SLEEP + + if (argc < 2) { + bb_show_usage(); + } + + ++argv; + duration = 0; + do { + duration += bb_xgetularg_bnd_sfx(*argv, 10, + 0, UINT_MAX-duration, + sleep_suffixes); + } while (*++argv); + +#else /* CONFIG_FEATURE_FANCY_SLEEP */ + + if (argc != 2) { + bb_show_usage(); + } + +#if UINT_MAX == ULONG_MAX + duration = bb_xgetularg10(argv[1]); +#else + duration = bb_xgetularg10_bnd(argv[1], 0, UINT_MAX); +#endif + +#endif /* CONFIG_FEATURE_FANCY_SLEEP */ + + if (sleep(duration)) { + bb_perror_nomsg_and_die(); + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/sort.c b/busybox/coreutils/sort.c new file mode 100644 index 000000000..8cc4d8886 --- /dev/null +++ b/busybox/coreutils/sort.c @@ -0,0 +1,100 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- a number of options are not supported. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Now does proper error checking on i/o. Plus some space savings. + */ + +#include +#include +#include +#include +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +static int compare_ascii(const void *x, const void *y) +{ + return strcmp(*(char **)x, *(char **)y); +} + +static int compare_numeric(const void *x, const void *y) +{ + int z = atoi(*(char **)x) - atoi(*(char **)y); + return z ? z : strcmp(*(char **)x, *(char **)y); +} + +int sort_main(int argc, char **argv) +{ + FILE *fp; + char *line, **lines = NULL; + int i, nlines = 0, inc; + int (*compare)(const void *, const void *) = compare_ascii; + + int flags; + + bb_default_error_retval = 2; + + flags = bb_getopt_ulflags(argc, argv, "nru"); + if (flags & 1) { + compare = compare_numeric; + } + + argv += optind; + if (!*argv) { + *--argv = "-"; + } + + do { + fp = xgetoptfile_sort_uniq(argv, "r"); + while ((line = bb_get_chomped_line_from_file(fp)) != NULL) { + lines = xrealloc(lines, sizeof(char *) * (nlines + 1)); + lines[nlines++] = line; + } + bb_xferror(fp, *argv); + bb_fclose_nonstdin(fp); + } while (*++argv); + + /* sort it */ + qsort(lines, nlines, sizeof(char *), compare); + + /* print it */ + i = 0; + --nlines; + if ((inc = 1 - (flags & 2)) < 0) { /* reverse */ + i = nlines; + } + flags &= 4; + + while (nlines >= 0) { + if (!flags || !nlines || strcmp(lines[i+inc], lines[i])) { + puts(lines[i]); + } + i += inc; + --nlines; + } + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/stty.c b/busybox/coreutils/stty.c new file mode 100644 index 000000000..d62068668 --- /dev/null +++ b/busybox/coreutils/stty.c @@ -0,0 +1,1314 @@ +/* vi: set sw=4 ts=4: */ +/* stty -- change and print terminal line settings + Copyright (C) 1990-1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Usage: stty [-ag] [-F device] [setting...] + + Options: + -a Write all current settings to stdout in human-readable form. + -g Write all current settings to stdout in stty-readable form. + -F Open and use the specified device instead of stdin + + If no args are given, write to stdout the baud rate and settings that + have been changed from their defaults. Mode reading and changes + are done on the specified device, or stdin if none was specified. + + David MacKenzie + + 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 +}; + +/* Which member(s) of `struct termios' a mode uses. */ +enum mode_type { + /* Do NOT change the order or values, as mode_type_flag() + * depends on them. */ + control, input, output, local, combination +}; + + +static const char evenp [] = "evenp"; +static const char raw [] = "raw"; +static const char stty_min [] = "min"; +static const char stty_time [] = "time"; +static const char stty_swtch[] = "swtch"; +static const char stty_eol [] = "eol"; +static const char stty_eof [] = "eof"; +static const char parity [] = "parity"; +static const char stty_oddp [] = "oddp"; +static const char stty_nl [] = "nl"; +static const char stty_ek [] = "ek"; +static const char stty_sane [] = "sane"; +static const char cbreak [] = "cbreak"; +static const char stty_pass8[] = "pass8"; +static const char litout [] = "litout"; +static const char cooked [] = "cooked"; +static const char decctlq [] = "decctlq"; +static const char stty_tabs [] = "tabs"; +static const char stty_lcase[] = "lcase"; +static const char stty_LCASE[] = "LCASE"; +static const char stty_crt [] = "crt"; +static const char stty_dec [] = "dec"; + + +/* Flags for `struct mode_info'. */ +#define SANE_SET 1 /* Set in `sane' mode. */ +#define SANE_UNSET 2 /* Unset in `sane' mode. */ +#define REV 4 /* Can be turned off by prepending `-'. */ +#define OMIT 8 /* Don't display value. */ + +/* Each mode. */ +struct mode_info { + const char *name; /* Name given on command line. */ + /* enum mode_type type; */ + char type; /* Which structure element to change. */ + char flags; /* Setting and display options. */ + unsigned short mask; /* Other bits to turn off for this mode. */ + unsigned long bits; /* Bits to set for this mode. */ +}; + +#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B } + +static const struct mode_info mode_info[] = { + MI_ENTRY("parenb", control, REV, PARENB, 0 ), + MI_ENTRY("parodd", control, REV, PARODD, 0 ), + MI_ENTRY("cs5", control, 0, CS5, CSIZE), + MI_ENTRY("cs6", control, 0, CS6, CSIZE), + MI_ENTRY("cs7", control, 0, CS7, CSIZE), + MI_ENTRY("cs8", control, 0, CS8, CSIZE), + MI_ENTRY("hupcl", control, REV, HUPCL, 0 ), + MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ), + MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ), + MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ), + MI_ENTRY("clocal", control, REV, CLOCAL, 0 ), +#ifdef CRTSCTS + MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ), +#endif + MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ), + MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ), + MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ), + MI_ENTRY("parmrk", input, REV, PARMRK, 0 ), + MI_ENTRY("inpck", input, REV, INPCK, 0 ), + MI_ENTRY("istrip", input, REV, ISTRIP, 0 ), + MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ), + MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ), + MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ), + MI_ENTRY("ixon", input, REV, IXON, 0 ), + MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ), + MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ), +#ifdef IUCLC + MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ), +#endif +#ifdef IXANY + MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ), +#endif +#ifdef IMAXBEL + MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ), +#endif + MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ), +#ifdef OLCUC + MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ), +#endif +#ifdef OCRNL + MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ), +#endif +#ifdef ONLCR + MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ), +#endif +#ifdef ONOCR + MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ), +#endif +#ifdef ONLRET + MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ), +#endif +#ifdef OFILL + MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ), +#endif +#ifdef OFDEL + MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ), +#endif +#ifdef NLDLY + MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY), + MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY), +#endif +#ifdef CRDLY + MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY), + MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY), + MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY), + MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY), +#endif + +#ifdef TABDLY + MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY), + MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY), + MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY), + MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY), +#else +# ifdef OXTABS + MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ), +# endif +#endif + +#ifdef BSDLY + MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY), + MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY), +#endif +#ifdef VTDLY + MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY), + MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY), +#endif +#ifdef FFDLY + MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY), + MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY), +#endif + MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ), + MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ), +#ifdef IEXTEN + MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ), +#endif + MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ), + MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ), + MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ), + MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ), + MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ), + MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ), +#ifdef XCASE + MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ), +#endif +#ifdef TOSTOP + MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ), +#endif +#ifdef ECHOPRT + MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ), + MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ), +#endif +#ifdef ECHOCTL + MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ), + MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ), +#endif +#ifdef ECHOKE + MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ), + MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ), +#endif + MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ), + MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ), + MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ), +#ifdef IXANY + MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ), +#endif +#if defined (TABDLY) || defined (OXTABS) + MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ), +#endif +#if defined(XCASE) && defined(IUCLC) && defined(OLCUC) + MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ), +#endif + MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ), + MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ), +}; + +static const int NUM_mode_info = + + (sizeof(mode_info) / sizeof(struct mode_info)); + +/* Control character settings. */ +struct control_info { + const char *name; /* Name given on command line. */ + unsigned char saneval; /* Value to set for `stty sane'. */ + unsigned char offset; /* Offset in c_cc. */ +}; + +/* Control characters. */ + +static const struct control_info control_info[] = { + {"intr", CINTR, VINTR}, + {"quit", CQUIT, VQUIT}, + {"erase", CERASE, VERASE}, + {"kill", CKILL, VKILL}, + {stty_eof, CEOF, VEOF}, + {stty_eol, CEOL, VEOL}, +#ifdef VEOL2 + {"eol2", CEOL2, VEOL2}, +#endif +#ifdef VSWTCH + {stty_swtch, CSWTCH, VSWTCH}, +#endif + {"start", CSTART, VSTART}, + {"stop", CSTOP, VSTOP}, + {"susp", CSUSP, VSUSP}, +#ifdef VDSUSP + {"dsusp", CDSUSP, VDSUSP}, +#endif +#ifdef VREPRINT + {"rprnt", CRPRNT, VREPRINT}, +#endif +#ifdef VWERASE + {"werase", CWERASE, VWERASE}, +#endif +#ifdef VLNEXT + {"lnext", CLNEXT, VLNEXT}, +#endif +#ifdef VFLUSHO + {"flush", CFLUSHO, VFLUSHO}, +#endif +#ifdef VSTATUS + {"status", CSTATUS, VSTATUS}, +#endif + /* These must be last because of the display routines. */ + {stty_min, 1, VMIN}, + {stty_time, 0, VTIME}, +}; + +static const int NUM_control_info = + (sizeof(control_info) / sizeof(struct control_info)); + + +static const char * visible(unsigned int ch); +static int recover_mode(char *arg, struct termios *mode); +static int screen_columns(void); +static int set_mode(const struct mode_info *info, + int reversed, struct termios *mode); +static speed_t string_to_baud(const char *arg); +static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode); +static void display_all(struct termios *mode, int fd); +static void display_changed(struct termios *mode, int fd); +static void display_recoverable(struct termios *mode, int fd); +static void display_speed(struct termios *mode, int fancy); +static void display_window_size(int fancy, int fd); +static void sane_mode(struct termios *mode); +static void set_control_char(const struct control_info *info, + const char *arg, struct termios *mode); +static void set_speed(enum speed_setting type, + const char *arg, struct termios *mode); +static void set_window_size(int rows, int cols, int fd); + +static const char *device_name; + +static __attribute__ ((noreturn)) void perror_on_device(const char *fmt) +{ + bb_perror_msg_and_die(fmt, device_name); +} + + +/* The width of the screen, for output wrapping. */ +static int max_col; + +/* Current position, to know when to wrap. */ +static int current_col; + +/* Print format string MESSAGE and optional args. + Wrap to next line first if it won't fit. + Print a space first unless MESSAGE will start a new line. */ + +static void wrapf(const char *message, ...) +{ + va_list args; + char buf[1024]; /* Plenty long for our needs. */ + int buflen; + + va_start(args, message); + vsprintf(buf, message, args); + va_end(args); + buflen = strlen(buf); + if (current_col + (current_col > 0) + buflen >= max_col) { + putchar('\n'); + current_col = 0; + } + if (current_col > 0) { + putchar(' '); + current_col++; + } + fputs(buf, stdout); + current_col += buflen; +} + +static const struct suffix_mult stty_suffixes[] = { + {"b", 512 }, + {"k", 1024}, + {"B", 1024}, + {NULL, 0 } +}; + +#ifndef TEST +extern int stty_main(int argc, char **argv) +#else +extern int main(int argc, char **argv) +#endif +{ + struct termios mode; + void (*output_func)(struct termios *, int); + int optc; + int require_set_attr; + int speed_was_set; + int verbose_output; + int recoverable_output; + int k; + int noargs = 1; + char * file_name = NULL; + int fd; + + + output_func = display_changed; + verbose_output = 0; + recoverable_output = 0; + + /* Don't print error messages for unrecognized options. */ + opterr = 0; + + while ((optc = getopt(argc, argv, "agF:")) != -1) { + switch (optc) { + case 'a': + verbose_output = 1; + output_func = display_all; + break; + + case 'g': + recoverable_output = 1; + output_func = display_recoverable; + break; + + case 'F': + if (file_name) + bb_error_msg_and_die("only one device may be specified"); + file_name = optarg; + break; + + default: /* unrecognized option */ + noargs = 0; + break; + } + + if (noargs == 0) + break; + } + + if (optind < argc) + noargs = 0; + + /* Specifying both -a and -g gets an error. */ + if (verbose_output & recoverable_output) + bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive"); + + /* Specifying any other arguments with -a or -g gets an error. */ + if (~noargs & (verbose_output | recoverable_output)) + bb_error_msg_and_die ("modes may not be set when specifying an output style"); + + /* FIXME: it'd be better not to open the file until we've verified + that all arguments are valid. Otherwise, we could end up doing + only some of the requested operations and then failing, probably + leaving things in an undesirable state. */ + + if (file_name) { + int fdflags; + + device_name = file_name; + fd = bb_xopen(device_name, O_RDONLY | O_NONBLOCK); + if ((fdflags = fcntl(fd, F_GETFL)) == -1 + || fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) + perror_on_device("%s: couldn't reset non-blocking mode"); + } else { + fd = 0; + device_name = bb_msg_standard_input; + } + + /* Initialize to all zeroes so there is no risk memcmp will report a + spurious difference in an uninitialized portion of the structure. */ + memset(&mode, 0, sizeof(mode)); + if (tcgetattr(fd, &mode)) + perror_on_device("%s"); + + if (verbose_output | recoverable_output | noargs) { + max_col = screen_columns(); + current_col = 0; + output_func(&mode, fd); + return EXIT_SUCCESS; + } + + speed_was_set = 0; + require_set_attr = 0; + k = 0; + while (++k < argc) { + int match_found = 0; + int reversed = 0; + int i; + + if (argv[k][0] == '-') { + char *find_dev_opt; + + ++argv[k]; + + /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc. + Find the options that have been parsed. This is really + gross, but it's needed because stty SETTINGS look like options to + getopt(), so we need to work around things in a really horrible + way. If any new options are ever added to stty, the short option + MUST NOT be a letter which is the first letter of one of the + possible stty settings. + */ + find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */ + if(find_dev_opt) { + if(find_dev_opt[1]==0) /* -*F /dev/foo */ + k++; /* skip /dev/foo */ + continue; /* else -*F/dev/foo - no skip */ + } + if(argv[k][0]=='a' || argv[k][0]=='g') + continue; + /* Is not options - is reverse params */ + reversed = 1; + } + for (i = 0; i < NUM_mode_info; ++i) + if (STREQ(argv[k], mode_info[i].name)) { + match_found = set_mode(&mode_info[i], reversed, &mode); + require_set_attr = 1; + break; + } + + if (match_found == 0 && reversed) + bb_error_msg_and_die("invalid argument `%s'", --argv[k]); + + if (match_found == 0) + for (i = 0; i < NUM_control_info; ++i) + if (STREQ(argv[k], control_info[i].name)) { + if (k == argc - 1) + bb_error_msg_and_die("missing argument to `%s'", argv[k]); + match_found = 1; + ++k; + set_control_char(&control_info[i], argv[k], &mode); + require_set_attr = 1; + break; + } + + if (match_found == 0) { + if (STREQ(argv[k], "ispeed")) { + if (k == argc - 1) + bb_error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + set_speed(input_speed, argv[k], &mode); + speed_was_set = 1; + require_set_attr = 1; + } else if (STREQ(argv[k], "ospeed")) { + if (k == argc - 1) + bb_error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + set_speed(output_speed, argv[k], &mode); + speed_was_set = 1; + require_set_attr = 1; + } +#ifdef TIOCGWINSZ + else if (STREQ(argv[k], "rows")) { + if (k == argc - 1) + bb_error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + set_window_size((int) bb_xparse_number(argv[k], stty_suffixes), + -1, fd); + } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) { + if (k == argc - 1) + bb_error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + set_window_size(-1, + (int) bb_xparse_number(argv[k], stty_suffixes), + fd); + } else if (STREQ(argv[k], "size")) { + max_col = screen_columns(); + current_col = 0; + display_window_size(0, fd); + } +#endif +#ifdef HAVE_C_LINE + else if (STREQ(argv[k], "line")) { + if (k == argc - 1) + bb_error_msg_and_die("missing argument to `%s'", argv[k]); + ++k; + mode.c_line = bb_xparse_number(argv[k], stty_suffixes); + require_set_attr = 1; + } +#endif + else if (STREQ(argv[k], "speed")) { + max_col = screen_columns(); + display_speed(&mode, 0); + } else if (recover_mode(argv[k], &mode) == 1) + require_set_attr = 1; + else if (string_to_baud(argv[k]) != (speed_t) - 1) { + set_speed(both_speeds, argv[k], &mode); + speed_was_set = 1; + require_set_attr = 1; + } else + bb_error_msg_and_die("invalid argument `%s'", argv[k]); + } + } + + if (require_set_attr) { + struct termios new_mode; + + if (tcsetattr(fd, TCSADRAIN, &mode)) + perror_on_device("%s"); + + /* POSIX (according to Zlotnick's book) tcsetattr returns zero if + it performs *any* of the requested operations. This means it + can report `success' when it has actually failed to perform + some proper subset of the requested operations. To detect + this partial failure, get the current terminal attributes and + compare them to the requested ones. */ + + /* Initialize to all zeroes so there is no risk memcmp will report a + spurious difference in an uninitialized portion of the structure. */ + memset(&new_mode, 0, sizeof(new_mode)); + if (tcgetattr(fd, &new_mode)) + perror_on_device("%s"); + + /* Normally, one shouldn't use memcmp to compare structures that + may have `holes' containing uninitialized data, but we have been + careful to initialize the storage of these two variables to all + zeroes. One might think it more efficient simply to compare the + modified fields, but that would require enumerating those fields -- + and not all systems have the same fields in this structure. */ + + if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { +#ifdef CIBAUD + /* SunOS 4.1.3 (at least) has the problem that after this sequence, + tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); + sometimes (m1 != m2). The only difference is in the four bits + of the c_cflag field corresponding to the baud rate. To save + Sun users a little confusion, don't report an error if this + happens. But suppress the error only if we haven't tried to + set the baud rate explicitly -- otherwise we'd never give an + error for a true failure to set the baud rate. */ + + new_mode.c_cflag &= (~CIBAUD); + if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0) +#endif + perror_on_device ("%s: unable to perform all requested operations"); + } + } + + return EXIT_SUCCESS; +} + +/* Return 0 if not applied because not reversible; otherwise return 1. */ + +static int +set_mode(const struct mode_info *info, int reversed, struct termios *mode) +{ + tcflag_t *bitsp; + + if (reversed && (info->flags & REV) == 0) + return 0; + + bitsp = mode_type_flag(info->type, mode); + + if (bitsp == NULL) { + /* Combination mode. */ + if (info->name == evenp || info->name == parity) { + if (reversed) + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + else + mode->c_cflag = + (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7; + } else if (info->name == stty_oddp) { + if (reversed) + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + else + mode->c_cflag = + (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB; + } else if (info->name == stty_nl) { + if (reversed) { + mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR; + mode->c_oflag = (mode->c_oflag +#ifdef ONLCR + | ONLCR +#endif + ) +#ifdef OCRNL + & ~OCRNL +#endif +#ifdef ONLRET + & ~ONLRET +#endif + ; + } else { + mode->c_iflag = mode->c_iflag & ~ICRNL; +#ifdef ONLCR + mode->c_oflag = mode->c_oflag & ~ONLCR; +#endif + } + } else if (info->name == stty_ek) { + mode->c_cc[VERASE] = CERASE; + mode->c_cc[VKILL] = CKILL; + } else if (info->name == stty_sane) + sane_mode(mode); + else if (info->name == cbreak) { + if (reversed) + mode->c_lflag |= ICANON; + else + mode->c_lflag &= ~ICANON; + } else if (info->name == stty_pass8) { + if (reversed) { + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; + mode->c_iflag |= ISTRIP; + } else { + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + mode->c_iflag &= ~ISTRIP; + } + } else if (info->name == litout) { + if (reversed) { + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; + mode->c_iflag |= ISTRIP; + mode->c_oflag |= OPOST; + } else { + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + mode->c_iflag &= ~ISTRIP; + mode->c_oflag &= ~OPOST; + } + } else if (info->name == raw || info->name == cooked) { + if ((info->name[0] == 'r' && reversed) + || (info->name[0] == 'c' && !reversed)) { + /* Cooked mode. */ + mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; + mode->c_oflag |= OPOST; + mode->c_lflag |= ISIG | ICANON; +#if VMIN == VEOF + mode->c_cc[VEOF] = CEOF; +#endif +#if VTIME == VEOL + mode->c_cc[VEOL] = CEOL; +#endif + } else { + /* Raw mode. */ + mode->c_iflag = 0; + mode->c_oflag &= ~OPOST; + mode->c_lflag &= ~(ISIG | ICANON +#ifdef XCASE + | XCASE +#endif + ); + mode->c_cc[VMIN] = 1; + mode->c_cc[VTIME] = 0; + } + } +#ifdef IXANY + else if (info->name == decctlq) { + if (reversed) + mode->c_iflag |= IXANY; + else + mode->c_iflag &= ~IXANY; + } +#endif +#ifdef TABDLY + else if (info->name == stty_tabs) { + if (reversed) + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3; + else + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0; + } +#else +# ifdef OXTABS + else if (info->name == stty_tabs) { + if (reversed) + mode->c_oflag = mode->c_oflag | OXTABS; + else + mode->c_oflag = mode->c_oflag & ~OXTABS; + } +# endif +#endif +#if defined(XCASE) && defined(IUCLC) && defined(OLCUC) + else if (info->name == stty_lcase || info->name == stty_LCASE) { + if (reversed) { + mode->c_lflag &= ~XCASE; + mode->c_iflag &= ~IUCLC; + mode->c_oflag &= ~OLCUC; + } else { + mode->c_lflag |= XCASE; + mode->c_iflag |= IUCLC; + mode->c_oflag |= OLCUC; + } + } +#endif + else if (info->name == stty_crt) + mode->c_lflag |= ECHOE +#ifdef ECHOCTL + | ECHOCTL +#endif +#ifdef ECHOKE + | ECHOKE +#endif + ; + else if (info->name == stty_dec) { + mode->c_cc[VINTR] = 3; /* ^C */ + mode->c_cc[VERASE] = 127; /* DEL */ + mode->c_cc[VKILL] = 21; /* ^U */ + mode->c_lflag |= ECHOE +#ifdef ECHOCTL + | ECHOCTL +#endif +#ifdef ECHOKE + | ECHOKE +#endif + ; +#ifdef IXANY + mode->c_iflag &= ~IXANY; +#endif + } + } else if (reversed) + *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits; + else + *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits; + + return 1; +} + +static void +set_control_char(const struct control_info *info, const char *arg, + struct termios *mode) +{ + unsigned char value; + + if (info->name == stty_min || info->name == stty_time) + value = bb_xparse_number(arg, stty_suffixes); + else if (arg[0] == '\0' || arg[1] == '\0') + value = arg[0]; + else if (STREQ(arg, "^-") || STREQ(arg, "undef")) + value = _POSIX_VDISABLE; + else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */ + if (arg[1] == '?') + value = 127; + else + value = arg[1] & ~0140; /* Non-letters get weird results. */ + } else + value = bb_xparse_number(arg, stty_suffixes); + mode->c_cc[info->offset] = value; +} + +static void +set_speed(enum speed_setting type, const char *arg, struct termios *mode) +{ + speed_t baud; + + baud = string_to_baud(arg); + + if (type != output_speed) { /* either input or both */ + cfsetispeed(mode, baud); + } + if (type != input_speed) { /* either output or both */ + cfsetospeed(mode, baud); + } +} + +#ifdef TIOCGWINSZ + +static int get_win_size(int fd, struct winsize *win) +{ + int err = ioctl(fd, TIOCGWINSZ, (char *) win); + + return err; +} + +static void +set_window_size(int rows, int cols, int fd) +{ + struct winsize win; + + if (get_win_size(fd, &win)) { + if (errno != EINVAL) + perror_on_device("%s"); + memset(&win, 0, sizeof(win)); + } + + if (rows >= 0) + win.ws_row = rows; + if (cols >= 0) + win.ws_col = cols; + +# ifdef TIOCSSIZE + /* Alexander Dupuy wrote: + The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel. + This comment from sys/ttold.h describes Sun's twisted logic - a better + test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0). + At any rate, the problem is gone in Solaris 2.x. */ + + if (win.ws_row == 0 || win.ws_col == 0) { + struct ttysize ttysz; + + ttysz.ts_lines = win.ws_row; + ttysz.ts_cols = win.ws_col; + + win.ws_row = win.ws_col = 1; + + if ((ioctl(fd, TIOCSWINSZ, (char *) &win) != 0) + || (ioctl(fd, TIOCSSIZE, (char *) &ttysz) != 0)) { + perror_on_device("%s"); + } + return; + } +# endif + + if (ioctl(fd, TIOCSWINSZ, (char *) &win)) + perror_on_device("%s"); +} + +static void display_window_size(int fancy, int fd) +{ + const char *fmt_str = "%s" "\0" "%s: no size information for this device"; + struct winsize win; + + if (get_win_size(fd, &win)) { + if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) { + perror_on_device(fmt_str); + } + } else { + wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n", + win.ws_row, win.ws_col); + if (!fancy) + current_col = 0; + } +} +#endif + +static int screen_columns(void) +{ + int columns; + const char *s; + +#ifdef TIOCGWINSZ + struct winsize win; + + /* With Solaris 2.[123], this ioctl fails and errno is set to + EINVAL for telnet (but not rlogin) sessions. + On ISC 3.0, it fails for the console and the serial port + (but it works for ptys). + It can also fail on any system when stdout isn't a tty. + In case of any failure, just use the default. */ + if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0) + return win.ws_col; +#endif + + columns = 80; + if ((s = getenv("COLUMNS"))) { + columns = atoi(s); + } + return columns; +} + +static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode) +{ + static const unsigned char tcflag_offsets[] = { + offsetof(struct termios, c_cflag), /* control */ + offsetof(struct termios, c_iflag), /* input */ + offsetof(struct termios, c_oflag), /* output */ + offsetof(struct termios, c_lflag) /* local */ + }; + + if (((unsigned int) type) <= local) { + return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]); + } + return NULL; +} + +static void display_changed(struct termios *mode, int fd) +{ + int i; + int empty_line; + tcflag_t *bitsp; + unsigned long mask; + enum mode_type prev_type = control; + + display_speed(mode, 1); +#ifdef HAVE_C_LINE + wrapf("line = %d;", mode->c_line); +#endif + putchar('\n'); + current_col = 0; + + empty_line = 1; + for (i = 0; control_info[i].name != stty_min; ++i) { + if (mode->c_cc[control_info[i].offset] == control_info[i].saneval) + continue; + /* If swtch is the same as susp, don't print both. */ +#if VSWTCH == VSUSP + if (control_info[i].name == stty_swtch) + continue; +#endif + /* If eof uses the same slot as min, only print whichever applies. */ +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0 + && (control_info[i].name == stty_eof + || control_info[i].name == stty_eol)) continue; +#endif + + empty_line = 0; + wrapf("%s = %s;", control_info[i].name, + visible(mode->c_cc[control_info[i].offset])); + } + if ((mode->c_lflag & ICANON) == 0) { + wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN], + (int) mode->c_cc[VTIME]); + } else if (empty_line == 0) + putchar('\n'); + current_col = 0; + + empty_line = 1; + for (i = 0; i < NUM_mode_info; ++i) { + if (mode_info[i].flags & OMIT) + continue; + if (mode_info[i].type != prev_type) { + if (empty_line == 0) { + putchar('\n'); + current_col = 0; + empty_line = 1; + } + prev_type = mode_info[i].type; + } + + bitsp = mode_type_flag(mode_info[i].type, mode); + mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; + if ((*bitsp & mask) == mode_info[i].bits) { + if (mode_info[i].flags & SANE_UNSET) { + wrapf("%s", mode_info[i].name); + empty_line = 0; + } + } + else if ((mode_info[i].flags & (SANE_SET | REV)) == + (SANE_SET | REV)) { + wrapf("-%s", mode_info[i].name); + empty_line = 0; + } + } + if (empty_line == 0) + putchar('\n'); + current_col = 0; +} + +static void +display_all(struct termios *mode, int fd) +{ + int i; + tcflag_t *bitsp; + unsigned long mask; + enum mode_type prev_type = control; + + display_speed(mode, 1); +#ifdef TIOCGWINSZ + display_window_size(1, fd); +#endif +#ifdef HAVE_C_LINE + wrapf("line = %d;", mode->c_line); +#endif + putchar('\n'); + current_col = 0; + + for (i = 0; control_info[i].name != stty_min; ++i) { + /* If swtch is the same as susp, don't print both. */ +#if VSWTCH == VSUSP + if (control_info[i].name == stty_swtch) + continue; +#endif + /* If eof uses the same slot as min, only print whichever applies. */ +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0 + && (control_info[i].name == stty_eof + || control_info[i].name == stty_eol)) continue; +#endif + wrapf("%s = %s;", control_info[i].name, + visible(mode->c_cc[control_info[i].offset])); + } +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0) +#endif + wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]); + if (current_col != 0) + putchar('\n'); + current_col = 0; + + for (i = 0; i < NUM_mode_info; ++i) { + if (mode_info[i].flags & OMIT) + continue; + if (mode_info[i].type != prev_type) { + putchar('\n'); + current_col = 0; + prev_type = mode_info[i].type; + } + + bitsp = mode_type_flag(mode_info[i].type, mode); + mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; + if ((*bitsp & mask) == mode_info[i].bits) + wrapf("%s", mode_info[i].name); + else if (mode_info[i].flags & REV) + wrapf("-%s", mode_info[i].name); + } + putchar('\n'); + current_col = 0; +} + +static void display_speed(struct termios *mode, int fancy) +{ + unsigned long ispeed, ospeed; + const char *fmt_str = + "%lu %lu\n\0" "ispeed %lu baud; ospeed %lu baud;\0" + "%lu\n\0" "\0\0\0\0" "speed %lu baud;"; + + ospeed = ispeed = cfgetispeed(mode); + if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) { + ispeed = ospeed; /* in case ispeed was 0 */ + fmt_str += 43; + } + if (fancy) { + fmt_str += 9; + } + wrapf(fmt_str, bb_baud_to_value(ispeed), bb_baud_to_value(ospeed)); + if (!fancy) + current_col = 0; +} + +static void display_recoverable(struct termios *mode, int fd) +{ + int i; + + printf("%lx:%lx:%lx:%lx", + (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag, + (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag); + for (i = 0; i < NCCS; ++i) + printf(":%x", (unsigned int) mode->c_cc[i]); + putchar('\n'); +} + +static int recover_mode(char *arg, struct termios *mode) +{ + int i, n; + unsigned int chr; + unsigned long iflag, oflag, cflag, lflag; + + /* Scan into temporaries since it is too much trouble to figure out + the right format for `tcflag_t'. */ + if (sscanf(arg, "%lx:%lx:%lx:%lx%n", + &iflag, &oflag, &cflag, &lflag, &n) != 4) + return 0; + mode->c_iflag = iflag; + mode->c_oflag = oflag; + mode->c_cflag = cflag; + mode->c_lflag = lflag; + arg += n; + for (i = 0; i < NCCS; ++i) { + if (sscanf(arg, ":%x%n", &chr, &n) != 1) + return 0; + mode->c_cc[i] = chr; + arg += n; + } + + /* Fail if there are too many fields. */ + if (*arg != '\0') + return 0; + + return 1; +} + +static speed_t string_to_baud(const char *arg) +{ + return bb_value_to_baud(bb_xparse_number(arg, 0)); +} + +static void sane_mode(struct termios *mode) +{ + int i; + tcflag_t *bitsp; + + for (i = 0; i < NUM_control_info; ++i) { +#if VMIN == VEOF + if (control_info[i].name == stty_min) + break; +#endif + mode->c_cc[control_info[i].offset] = control_info[i].saneval; + } + + for (i = 0; i < NUM_mode_info; ++i) { + if (mode_info[i].flags & SANE_SET) { + bitsp = mode_type_flag(mode_info[i].type, mode); + *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask)) + | mode_info[i].bits; + } else if (mode_info[i].flags & SANE_UNSET) { + bitsp = mode_type_flag(mode_info[i].type, mode); + *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask) + & ~mode_info[i].bits; + } + } +} + +/* Return a string that is the printable representation of character CH. */ +/* Adapted from `cat' by Torbjorn Granlund. */ + +static const char *visible(unsigned int ch) +{ + static char buf[10]; + char *bpout = buf; + + if (ch == _POSIX_VDISABLE) { + return ""; + } + + if (ch >= 128) { + ch -= 128; + *bpout++ = 'M'; + *bpout++ = '-'; + } + + if (ch < 32) { + *bpout++ = '^'; + *bpout++ = ch + 64; + } else if (ch < 127) { + *bpout++ = ch; + } else { + *bpout++ = '^'; + *bpout++ = '?'; + } + + *bpout = '\0'; + return (const char *) buf; +} + +#ifdef TEST + +const char *bb_applet_name = "stty"; + +#endif diff --git a/busybox/coreutils/sync.c b/busybox/coreutils/sync.c new file mode 100644 index 000000000..84746311f --- /dev/null +++ b/busybox/coreutils/sync.c @@ -0,0 +1,36 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include +#include +#include "busybox.h" + +extern int sync_main(int argc, char **argv) +{ + bb_warn_ignoring_args(argc - 1); + + sync(); + + return(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/tail.c b/busybox/coreutils/tail.c new file mode 100644 index 000000000..e3f89d2ee --- /dev/null +++ b/busybox/coreutils/tail.c @@ -0,0 +1,330 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 compliant (need fancy for -c) */ +/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Pretty much rewritten to fix numerous bugs and reduce realloc() calls. + * Bugs fixed (although I may have forgotten one or two... it was pretty bad) + * 1) mixing printf/write without fflush()ing stdout + * 2) no check that any open files are present + * 3) optstring had -q taking an arg + * 4) no error checking on write in some cases, and a warning even then + * 5) q and s interaction bug + * 6) no check for lseek error + * 7) lseek attempted when count==0 even if arg was +0 (from top) + */ + +#include +#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 int status +#if EXIT_SUCCESS != 0 + = EXIT_SUCCESS /* If it is 0 (paranoid check), let bss initialize it. */ +#endif + ; + +static void tail_xprint_header(const char *fmt, const char *filename) +{ + /* If we get an output error, there is really no sense in continuing. */ + if (dprintf(STDOUT_FILENO, fmt, filename) < 0) { + bb_perror_nomsg_and_die(); + } +} + +/* len should probably be size_t */ +static void tail_xbb_full_write(const char *buf, size_t len) +{ + /* If we get a write error, there is really no sense in continuing. */ + if (bb_full_write(STDOUT_FILENO, buf, len) < 0) { + bb_perror_nomsg_and_die(); + } +} + +static ssize_t tail_read(int fd, char *buf, size_t count) +{ + ssize_t r; + + if ((r = safe_read(fd, buf, count)) < 0) { + bb_perror_msg("read"); + status = EXIT_FAILURE; + } + + return r; +} + +static const char tail_opts[] = + "fn:c:" +#ifdef CONFIG_FEATURE_FANCY_TAIL + "qs:v" +#endif + ; + +static const char header_fmt[] = "\n==> %s <==\n"; + +int tail_main(int argc, char **argv) +{ + long count = 10; + unsigned int sleep_period = 1; + int from_top = 0; + int follow = 0; + int header_threshhold = 1; + int count_bytes = 0; + + char *tailbuf; + size_t tailbufsize; + int taillen = 0; + int newline = 0; + + int *fds, nfiles, nread, nwrite, seen, i, opt; + char *s, *buf; + const char *fmt; + + /* Allow legacy syntax of an initial numeric option without -n. */ + if (argc >=2 && ((argv[1][0] == '+') || ((argv[1][0] == '-') + /* && (isdigit)(argv[1][1]) */ + && (((unsigned int)(argv[1][1] - '0')) <= 9)))) + { + optind = 2; + optarg = argv[1]; + goto GET_COUNT; + } + + while ((opt = getopt(argc, argv, tail_opts)) > 0) { + switch (opt) { + case 'f': + follow = 1; + break; + case 'c': + count_bytes = 1; + /* FALLS THROUGH */ + case 'n': + GET_COUNT: + count = bb_xgetlarg10_sfx(optarg, tail_suffixes); + /* Note: Leading whitespace is an error trapped above. */ + if (*optarg == '+') { + from_top = 1; + } else { + from_top = 0; + } + if (count < 0) { + count = -count; + } + break; +#ifdef CONFIG_FEATURE_FANCY_TAIL + case 'q': + header_threshhold = INT_MAX; + break; + case 's': + sleep_period =bb_xgetularg10_bnd(optarg, 0, UINT_MAX); + break; + case 'v': + header_threshhold = 0; + break; +#endif + default: + bb_show_usage(); + } + } + + /* open all the files */ + fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1)); + + argv += optind; + nfiles = i = 0; + + if ((argc -= optind) == 0) { + struct stat statbuf; + + if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) { + follow = 0; + } + /* --argv; */ + *argv = (char *) bb_msg_standard_input; + goto DO_STDIN; + } + + do { + if ((argv[i][0] == '-') && !argv[i][1]) { + DO_STDIN: + fds[nfiles] = STDIN_FILENO; + } else if ((fds[nfiles] = open(argv[i], O_RDONLY)) < 0) { + bb_perror_msg("%s", argv[i]); + status = EXIT_FAILURE; + continue; + } + argv[nfiles] = argv[i]; + ++nfiles; + } while (++i < argc); + + if (!nfiles) { + bb_error_msg_and_die("no files"); + } + + tailbufsize = BUFSIZ; + + /* tail the files */ + if (from_top < count_bytes) { /* Each is 0 or 1, so true iff 0 < 1. */ + /* Hence, !from_top && count_bytes */ + if (tailbufsize < count) { + tailbufsize = count + BUFSIZ; + } + } + + buf = tailbuf = xmalloc(tailbufsize); + + fmt = header_fmt + 1; /* Skip header leading newline on first output. */ + i = 0; + do { + /* Be careful. It would be possible to optimize the count-bytes + * case if the file is seekable. If you do though, remember that + * starting file position may not be the beginning of the file. + * Beware of backing up too far. See example in wc.c. + */ + if ((!(count|from_top)) && (lseek(fds[i], 0, SEEK_END) >= 0)) { + continue; + } + + if (nfiles > header_threshhold) { + tail_xprint_header(fmt, argv[i]); + fmt = header_fmt; + } + + buf = tailbuf; + taillen = 0; + seen = 1; + newline = 0; + + while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) { + if (from_top) { + nwrite = nread; + if (seen < count) { + if (count_bytes) { + nwrite -= (count - seen); + seen = count; + } else { + s = buf; + do { + --nwrite; + if ((*s++ == '\n') && (++seen == count)) { + break; + } + } while (nwrite); + } + } + tail_xbb_full_write(buf + nread - nwrite, nwrite); + } else if (count) { + if (count_bytes) { + taillen += nread; + if (taillen > count) { + memmove(tailbuf, tailbuf + taillen - count, count); + taillen = count; + } + } else { + int k = nread; + int nbuf = 0; + + while (k) { + --k; + if (buf[k] == '\n') { + ++nbuf; + } + } + + if (newline + nbuf < count) { + newline += nbuf; + taillen += nread; + + } else { + int extra = 0; + if (buf[nread-1] != '\n') { + extra = 1; + } + + k = newline + nbuf + extra - count; + s = tailbuf; + while (k) { + if (*s == '\n') { + --k; + } + ++s; + } + + taillen += nread - (s - tailbuf); + memmove(tailbuf, s, taillen); + newline = count - extra; + } + if (tailbufsize < taillen + BUFSIZ) { + tailbufsize = taillen + BUFSIZ; + tailbuf = xrealloc(tailbuf, tailbufsize); + } + } + buf = tailbuf + taillen; + } + } + + if (!from_top) { + tail_xbb_full_write(tailbuf, taillen); + } + + taillen = 0; + } while (++i < nfiles); + + buf = xrealloc(tailbuf, BUFSIZ); + + fmt = NULL; + + while (follow) { + sleep(sleep_period); + i = 0; + do { + if (nfiles > header_threshhold) { + fmt = header_fmt; + } + while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) { + if (fmt) { + tail_xprint_header(fmt, argv[i]); + fmt = NULL; + } + tail_xbb_full_write(buf, nread); + } + } while (++i < nfiles); + } + + return status; +} diff --git a/busybox/coreutils/tee.c b/busybox/coreutils/tee.c new file mode 100644 index 000000000..ba2e10f90 --- /dev/null +++ b/busybox/coreutils/tee.c @@ -0,0 +1,120 @@ +/* vi: set sw=4 ts=4: */ +/* + * tee implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */ + +#include +#include +#include +#include +#include "busybox.h" + +int tee_main(int argc, char **argv) +{ + const char *mode = "w\0a"; + FILE **files; + FILE **p; + char **filenames; + int flags; + int retval = EXIT_SUCCESS; +#ifdef CONFIG_FEATURE_TEE_USE_BLOCK_IO + ssize_t c; + RESERVE_CONFIG_BUFFER(buf, BUFSIZ); +#else + int c; +#endif + + flags = bb_getopt_ulflags(argc, argv, "ia"); /* 'a' must be 2nd */ + + mode += (flags & 2); /* Since 'a' is the 2nd option... */ + + if (flags & 1) { + signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction.*/ + } + + /* gnu tee ignores SIGPIPE in case one of the output files is a pipe + * that doesn't consume all its input. Good idea... */ + signal(SIGPIPE, SIG_IGN); /* TODO - switch to sigaction.*/ + + /* Allocate an array of FILE *'s, with one extra for a sentinal. */ + p = files = (FILE **)xmalloc(sizeof(FILE *) * (argc - optind + 2)); + *p = stdout; + argv += optind - 1; + filenames = argv - 1; + *filenames = (char *) bb_msg_standard_input; /* for later */ + goto GOT_NEW_FILE; + + do { + if ((*p = bb_wfopen(*argv, mode)) == NULL) { + retval = EXIT_FAILURE; + continue; + } + filenames[(int)(p - files)] = *argv; + GOT_NEW_FILE: + setbuf(*p, NULL); /* tee must not buffer output. */ + ++p; + } while (*++argv); + + *p = NULL; /* Store the sentinal value. */ + +#ifdef CONFIG_FEATURE_TEE_USE_BLOCK_IO + while ((c = safe_read(STDIN_FILENO, buf, BUFSIZ)) > 0) { + for (p=files ; *p ; p++) { + fwrite(buf, 1, c, *p); + } + } + + if (c < 0) { /* Make sure read errors are signaled. */ + retval = EXIT_FAILURE; + } + +#ifdef CONFIG_FEATURE_CLEAN_UP + RELEASE_CONFIG_BUFFER(buf); +#endif + +#else + setvbuf(stdout, NULL, _IONBF, 0); + while ((c = getchar()) != EOF) { + for (p=files ; *p ; p++) { + putc(c, *p); + } + } +#endif + + /* Now we need to check for i/o errors on stdin and the various + * output files. Since we know that the first entry in the output + * file table is stdout, we can save one "if ferror" test by + * setting the first entry to stdin and checking stdout error + * status with bb_fflush_stdout_and_exit()... although fflush()ing + * is unnecessary here. */ + + p = files; + *p = stdin; + do { /* Now check for (input and) output errors. */ + /* Checking ferror should be sufficient, but we may want to fclose. + * If we do, remember not to close stdin! */ + bb_xferror(*p, filenames[(int)(p - files)]); + } while (*++p); + + bb_fflush_stdout_and_exit(retval); +} diff --git a/busybox/coreutils/test.c b/busybox/coreutils/test.c new file mode 100644 index 000000000..8fa6d166f --- /dev/null +++ b/busybox/coreutils/test.c @@ -0,0 +1,559 @@ +/* vi: set sw=4 ts=4: */ +/* + * test implementation for busybox + * + * Copyright (c) by a whole pile of folks: + * + * test(1); version 7-like -- author Erik Baalbergen + * modified by Eric Gisin to be used as built-in. + * modified by Arnold Robbins to add SVR3 compatibility + * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). + * modified by J.T. Conklin for NetBSD. + * modified by Herbert Xu to be used as built-in in ash. + * modified by Erik Andersen 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} +}; + +#ifdef CONFIG_FEATURE_TEST_64 +typedef int64_t arith_t; +#else +typedef int arith_t; +#endif + +static char **t_wp; +static struct t_op const *t_wp_op; +static gid_t *group_array = NULL; +static int ngroups; + +static enum token t_lex(char *s); +static arith_t oexpr(enum token n); +static arith_t aexpr(enum token n); +static arith_t nexpr(enum token n); +static int binop(void); +static arith_t primary(enum token n); +static int filstat(char *nm, enum token mode); +static arith_t getn(const char *s); +static int newerf(const char *f1, const char *f2); +static int olderf(const char *f1, const char *f2); +static int equalf(const char *f1, const char *f2); +static void syntax(const char *op, const char *msg); +static int test_eaccess(char *path, int mode); +static int is_a_group_member(gid_t gid); +static void initialize_group_array(void); + +extern int test_main(int argc, char **argv) +{ + int res; + + if (strcmp(bb_applet_name, "[") == 0) { + if (strcmp(argv[--argc], "]")) + bb_error_msg_and_die("missing ]"); + argv[argc] = NULL; + } + /* Implement special cases from POSIX.2, section 4.62.4 */ + switch (argc) { + case 1: + exit(1); + case 2: + exit(*argv[1] == '\0'); + case 3: + if (argv[1][0] == '!' && argv[1][1] == '\0') { + exit(!(*argv[2] == '\0')); + } + break; + case 4: + if (argv[1][0] != '!' || argv[1][1] != '\0') { + if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) { + t_wp = &argv[1]; + exit(binop() == 0); + } + } + break; + case 5: + if (argv[1][0] == '!' && argv[1][1] == '\0') { + if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) { + t_wp = &argv[2]; + exit(!(binop() == 0)); + } + } + break; + } + + t_wp = &argv[1]; + res = !oexpr(t_lex(*t_wp)); + + if (*t_wp != NULL && *++t_wp != NULL) + syntax(*t_wp, "unknown operand"); + + return (res); +} + +static void syntax(const char *op, const char *msg) +{ + if (op && *op) { + bb_error_msg_and_die("%s: %s", op, msg); + } else { + bb_error_msg_and_die("%s", msg); + } +} + +static arith_t oexpr(enum token n) +{ + arith_t res; + + res = aexpr(n); + if (t_lex(*++t_wp) == BOR) { + return oexpr(t_lex(*++t_wp)) || res; + } + t_wp--; + return res; +} + +static arith_t aexpr(enum token n) +{ + arith_t res; + + res = nexpr(n); + if (t_lex(*++t_wp) == BAND) + return aexpr(t_lex(*++t_wp)) && res; + t_wp--; + return res; +} + +static arith_t nexpr(enum token n) +{ + if (n == UNOT) + return !nexpr(t_lex(*++t_wp)); + return primary(n); +} + +static arith_t primary(enum token n) +{ + arith_t res; + + if (n == EOI) { + syntax(NULL, "argument expected"); + } + if (n == LPAREN) { + res = oexpr(t_lex(*++t_wp)); + if (t_lex(*++t_wp) != RPAREN) + syntax(NULL, "closing paren expected"); + return res; + } + if (t_wp_op && t_wp_op->op_type == UNOP) { + /* unary expression */ + if (*++t_wp == NULL) + syntax(t_wp_op->op_text, "argument expected"); + switch (n) { + case STREZ: + return strlen(*t_wp) == 0; + case STRNZ: + return strlen(*t_wp) != 0; + case FILTT: + return isatty(getn(*t_wp)); + default: + return filstat(*t_wp, n); + } + } + + if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { + return binop(); + } + + return strlen(*t_wp) > 0; +} + +static int binop() +{ + const char *opnd1, *opnd2; + struct t_op const *op; + + opnd1 = *t_wp; + (void) t_lex(*++t_wp); + op = t_wp_op; + + if ((opnd2 = *++t_wp) == (char *) 0) + syntax(op->op_text, "argument expected"); + + switch (op->op_num) { + case STREQ: + return strcmp(opnd1, opnd2) == 0; + case STRNE: + return strcmp(opnd1, opnd2) != 0; + case STRLT: + return strcmp(opnd1, opnd2) < 0; + case STRGT: + return strcmp(opnd1, opnd2) > 0; + case INTEQ: + return getn(opnd1) == getn(opnd2); + case INTNE: + return getn(opnd1) != getn(opnd2); + case INTGE: + return getn(opnd1) >= getn(opnd2); + case INTGT: + return getn(opnd1) > getn(opnd2); + case INTLE: + return getn(opnd1) <= getn(opnd2); + case INTLT: + return getn(opnd1) < getn(opnd2); + case FILNT: + return newerf(opnd1, opnd2); + case FILOT: + return olderf(opnd1, opnd2); + case FILEQ: + return equalf(opnd1, opnd2); + } + /* NOTREACHED */ + return 1; +} + +static int filstat(char *nm, enum token mode) +{ + struct stat s; + unsigned int i; + + if (mode == FILSYM) { +#ifdef S_IFLNK + if (lstat(nm, &s) == 0) { + i = S_IFLNK; + goto filetype; + } +#endif + return 0; + } + + if (stat(nm, &s) != 0) + return 0; + + switch (mode) { + case FILRD: + return test_eaccess(nm, R_OK) == 0; + case FILWR: + return test_eaccess(nm, W_OK) == 0; + case FILEX: + return test_eaccess(nm, X_OK) == 0; + case FILEXIST: + return 1; + case FILREG: + i = S_IFREG; + goto filetype; + case FILDIR: + i = S_IFDIR; + goto filetype; + case FILCDEV: + i = S_IFCHR; + goto filetype; + case FILBDEV: + i = S_IFBLK; + goto filetype; + case FILFIFO: +#ifdef S_IFIFO + i = S_IFIFO; + goto filetype; +#else + return 0; +#endif + case FILSOCK: +#ifdef S_IFSOCK + i = S_IFSOCK; + goto filetype; +#else + return 0; +#endif + case FILSUID: + i = S_ISUID; + goto filebit; + case FILSGID: + i = S_ISGID; + goto filebit; + case FILSTCK: + i = S_ISVTX; + goto filebit; + case FILGZ: + return s.st_size > 0L; + case FILUID: + return s.st_uid == geteuid(); + case FILGID: + return s.st_gid == getegid(); + default: + return 1; + } + + filetype: + return ((s.st_mode & S_IFMT) == i); + + filebit: + return ((s.st_mode & i) != 0); +} + +static enum token t_lex(char *s) +{ + struct t_op const *op = ops; + + if (s == 0) { + t_wp_op = (struct t_op *) 0; + return EOI; + } + while (op->op_text) { + if (strcmp(s, op->op_text) == 0) { + t_wp_op = op; + return op->op_num; + } + op++; + } + t_wp_op = (struct t_op *) 0; + return OPERAND; +} + +/* atoi with error detection */ +static arith_t getn(const char *s) +{ + char *p; +#ifdef CONFIG_FEATURE_TEST_64 + long long r; +#else + long r; +#endif + + errno = 0; +#ifdef CONFIG_FEATURE_TEST_64 + r = strtoll(s, &p, 10); +#else + r = strtol(s, &p, 10); +#endif + + if (errno != 0) + bb_error_msg_and_die("%s: out of range", s); + + /* p = bb_skip_whitespace(p); avoid const warning */ + if (*(bb_skip_whitespace(p))) + bb_error_msg_and_die("%s: bad number", s); + + return r; +} + +static int newerf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); +} + +static int olderf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); +} + +static int equalf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && + b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); +} + +/* Do the same thing access(2) does, but use the effective uid and gid, + and don't make the mistake of telling root that any file is + executable. */ +static int test_eaccess(char *path, int mode) +{ + struct stat st; + unsigned int euid = geteuid(); + + if (stat(path, &st) < 0) + return (-1); + + if (euid == 0) { + /* Root can read or write any file. */ + if (mode != X_OK) + return (0); + + /* Root can execute any file that has any one of the execute + bits set. */ + if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + return (0); + } + + if (st.st_uid == euid) /* owner */ + mode <<= 6; + else if (is_a_group_member(st.st_gid)) + mode <<= 3; + + if (st.st_mode & mode) + return (0); + + return (-1); +} + +static void initialize_group_array() +{ + ngroups = getgroups(0, NULL); + group_array = xrealloc(group_array, ngroups * sizeof(gid_t)); + getgroups(ngroups, group_array); +} + +/* Return non-zero if GID is one that we have in our groups list. */ +static int is_a_group_member(gid_t gid) +{ + register int i; + + /* Short-circuit if possible, maybe saving a call to getgroups(). */ + if (gid == getgid() || gid == getegid()) + return (1); + + if (ngroups == 0) + initialize_group_array(); + + /* Search through the list looking for GID. */ + for (i = 0; i < ngroups; i++) + if (gid == group_array[i]) + return (1); + + return (0); +} diff --git a/busybox/coreutils/touch.c b/busybox/coreutils/touch.c new file mode 100644 index 000000000..645fb2174 --- /dev/null +++ b/busybox/coreutils/touch.c @@ -0,0 +1,76 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini touch implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m, -r, -t not supported. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Previous version called open() and then utime(). While this will be + * be necessary to implement -r and -t, it currently only makes things bigger. + * Also, exiting on a failure was a bug. All args should be processed. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int touch_main(int argc, char **argv) +{ + int fd; + int flags; + int status = EXIT_SUCCESS; + + flags = bb_getopt_ulflags(argc, argv, "c"); + + argv += optind; + + if (!*argv) { + bb_show_usage(); + } + + do { + if (utime(*argv, NULL)) { + if (errno == ENOENT) { /* no such file*/ + if (flags & 1) { /* Creation is disabled, so ignore. */ + continue; + } + /* Try to create the file. */ + fd = open(*argv, O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH + ); + if ((fd >= 0) && !close(fd)) { + continue; + } + } + status = EXIT_FAILURE; + bb_perror_msg("%s", *argv); + } + } while (*++argv); + + return status; +} diff --git a/busybox/coreutils/tr.c b/busybox/coreutils/tr.c new file mode 100644 index 000000000..1325245b8 --- /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 CONFIG_DEBUG and BUFFERS_GO_IN_BSS are + * enabled, we otherwise get a "storage size isn't constant error. */ +#define ASCII 0377 + +/* some "globals" shared across this file */ +static char com_fl, del_fl, sq_fl; +static short in_index, out_index; +/* these last are pointers to static buffers declared in tr_main */ +static unsigned char *poutput, *pinput; +static unsigned char *pvector; +static char *pinvec, *poutvec; + + +static void convert(void) +{ + short read_chars = 0; + short c, coded; + short last = -1; + + for (;;) { + if (in_index == read_chars) { + if ((read_chars = read(0, (char *) pinput, BUFSIZ)) <= 0) { + if (write(1, (char *) poutput, out_index) != out_index) + bb_error_msg(bb_msg_write_error); + exit(0); + } + in_index = 0; + } + c = pinput[in_index++]; + coded = pvector[c]; + if (del_fl && pinvec[c]) + continue; + if (sq_fl && last == coded && (pinvec[c] || poutvec[coded])) + continue; + poutput[out_index++] = last = coded; + if (out_index == BUFSIZ) { + if (write(1, (char *) poutput, out_index) != out_index) + bb_error_msg_and_die(bb_msg_write_error); + out_index = 0; + } + } + + /* NOTREACHED */ +} + +static void map(register unsigned char *string1, unsigned int string1_len, + register unsigned char *string2, unsigned int string2_len) +{ + unsigned char last = '0'; + unsigned int i, j; + + for (j = 0, i = 0; i < string1_len; i++) { + if (string2_len <= j) + pvector[string1[i]] = last; + else + pvector[string1[i]] = last = string2[j++]; + } +} + +/* supported constructs: + * Ranges, e.g., [0-9] ==> 0123456789 + * Escapes, e.g., \a ==> Control-G + */ +static unsigned int expand(const char *arg, register unsigned char *buffer) +{ + unsigned char *buffer_start = buffer; + int i, ac; + + while (*arg) { + if (*arg == '\\') { + arg++; + *buffer++ = bb_process_escape_sequence(&arg); + } else if (*(arg+1) == '-') { + ac = *(arg+2); + if(ac == 0) { + *buffer++ = *arg++; + continue; + } + i = *arg; + while (i <= ac) + *buffer++ = i++; + arg += 3; /* Skip the assumed a-z */ + } else if (*arg == '[') { + arg++; + i = *arg++; + if (*arg++ != '-') { + *buffer++ = '['; + arg -= 2; + continue; + } + ac = *arg++; + while (i <= ac) + *buffer++ = i++; + arg++; /* Skip the assumed ']' */ + } else + *buffer++ = *arg++; + } + + return (buffer - buffer_start); +} + +static int complement(unsigned char *buffer, int buffer_len) +{ + register short i, j, ix; + char conv[ASCII + 2]; + + ix = 0; + for (i = 0; i <= ASCII; i++) { + for (j = 0; j < buffer_len; j++) + if (buffer[j] == i) + break; + if (j == buffer_len) + conv[ix++] = i & ASCII; + } + memcpy(buffer, conv, ix); + return ix; +} + +extern int tr_main(int argc, char **argv) +{ + register unsigned char *ptr; + int output_length=0, input_length; + int idx = 1; + int i; + RESERVE_CONFIG_BUFFER(output, BUFSIZ); + RESERVE_CONFIG_BUFFER(input, BUFSIZ); + RESERVE_CONFIG_UBUFFER(vector, ASCII+1); + RESERVE_CONFIG_BUFFER(invec, ASCII+1); + RESERVE_CONFIG_BUFFER(outvec, ASCII+1); + + /* ... but make them available globally */ + poutput = output; + pinput = input; + pvector = vector; + pinvec = invec; + poutvec = outvec; + + if (argc > 1 && argv[idx][0] == '-') { + for (ptr = (unsigned char *) &argv[idx][1]; *ptr; ptr++) { + switch (*ptr) { + case 'c': + com_fl = TRUE; + break; + case 'd': + del_fl = TRUE; + break; + case 's': + sq_fl = TRUE; + break; + default: + bb_show_usage(); + } + } + idx++; + } + for (i = 0; i <= ASCII; i++) { + vector[i] = i; + invec[i] = outvec[i] = FALSE; + } + + if (argv[idx] != NULL) { + input_length = expand(argv[idx++], input); + if (com_fl) + input_length = complement(input, input_length); + if (argv[idx] != NULL) { + if (*argv[idx] == '\0') + bb_error_msg_and_die("STRING2 cannot be empty"); + output_length = expand(argv[idx], output); + map(input, input_length, output, output_length); + } + for (i = 0; i < input_length; i++) + invec[(unsigned char)input[i]] = TRUE; + for (i = 0; i < output_length; i++) + outvec[(unsigned char)output[i]] = TRUE; + } + convert(); + return (0); +} + +/* + * Copyright (c) 1987,1997, Prentice Hall + * All rights reserved. + * + * Redistribution and use of the MINIX operating system in source and + * binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of Prentice Hall nor the names of the software + * authors or contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + diff --git a/busybox/coreutils/true.c b/busybox/coreutils/true.c new file mode 100644 index 000000000..3e7eb0111 --- /dev/null +++ b/busybox/coreutils/true.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini true implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */ + +#include +#include "busybox.h" + +extern int true_main(int argc, char **argv) +{ + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/tty.c b/busybox/coreutils/tty.c new file mode 100644 index 000000000..cd2c784fd --- /dev/null +++ b/busybox/coreutils/tty.c @@ -0,0 +1,58 @@ +/* vi: set sw=4 ts=4: */ +/* + * tty implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tty.html */ + +#include +#include +#include +#include "busybox.h" + +extern int tty_main(int argc, char **argv) +{ + const char *s; + int silent; /* Note: No longer relevant in SUSv3. */ + int retval; + + bb_default_error_retval = 2; /* SUSv3 requires > 1 for error. */ + + silent = bb_getopt_ulflags(argc, argv, "s"); + + /* gnu tty outputs a warning that it is ignoring all args. */ + bb_warn_ignoring_args(argc - optind); + + retval = 0; + + if ((s = ttyname(0)) == NULL) { + /* According to SUSv3, ttyname can on fail with EBADF or ENOTTY. + * We know the file descriptor is good, so failure means not a tty. */ + s = "not a tty"; + retval = 1; + } + + if (!silent) { + puts(s); + } + + bb_fflush_stdout_and_exit(retval); +} diff --git a/busybox/coreutils/uname.c b/busybox/coreutils/uname.c new file mode 100644 index 000000000..a3e52e39f --- /dev/null +++ b/busybox/coreutils/uname.c @@ -0,0 +1,118 @@ +/* vi: set sw=4 ts=4: */ +/* uname -- print system information + Copyright (C) 1989-1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */ + +/* Option Example + + -s, --sysname SunOS + -n, --nodename rocky8 + -r, --release 4.0 + -v, --version + -m, --machine sun + -a, --all SunOS rocky8 4.0 sun + + The default behavior is equivalent to `-s'. + + David MacKenzie */ + +/* Busyboxed by Erik Andersen */ + +/* Further size reductions by Glenn McGrath and Manuel Novoa III. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Now does proper error checking on i/o. Plus some further space savings. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +typedef struct { + struct utsname name; + char processor[8]; /* for "unknown" */ +} uname_info_t; + +static const char options[] = "snrvmpa"; +static const unsigned short int utsname_offset[] = { + offsetof(uname_info_t,name.sysname), + offsetof(uname_info_t,name.nodename), + offsetof(uname_info_t,name.release), + offsetof(uname_info_t,name.version), + offsetof(uname_info_t,name.machine), + offsetof(uname_info_t,processor) +}; + +int uname_main(int argc, char **argv) +{ + uname_info_t uname_info; +#if defined(__sparc__) && defined(__linux__) + char *fake_sparc = getenv("FAKE_SPARC"); +#endif + const unsigned short int *delta; + char toprint; + + toprint = bb_getopt_ulflags(argc, argv, options); + + if (argc != optind) { + bb_show_usage(); + } + + if (toprint & (1 << 6)) { + toprint = 0x3f; + } + + if (toprint == 0) { + toprint = 1; /* sysname */ + } + + if (uname(&uname_info.name) == -1) { + bb_error_msg_and_die("cannot get system name"); + } + +#if defined(__sparc__) && defined(__linux__) + if ((fake_sparc != NULL) + && ((fake_sparc[0] == 'y') + || (fake_sparc[0] == 'Y'))) { + strcpy(uname_info.name.machine, "sparc"); + } +#endif + + strcpy(uname_info.processor, "unknown"); + + delta=utsname_offset; + do { + if (toprint & 1) { + bb_printf(((char *)(&uname_info)) + *delta); + if (toprint > 1) { + putchar(' '); + } + } + ++delta; + } while (toprint >>= 1); + putchar('\n'); + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/uniq.c b/busybox/coreutils/uniq.c new file mode 100644 index 000000000..6caab5dae --- /dev/null +++ b/busybox/coreutils/uniq.c @@ -0,0 +1,112 @@ +/* vi: set sw=4 ts=4: */ +/* + * uniq implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */ + +#include +#include +#include +#include +#include +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +static const char uniq_opts[] = "f:s:cdu\0\7\3\5\1\2\4"; + +int uniq_main(int argc, char **argv) +{ + FILE *in, *out; + /* Note: Ignore the warning about dups and e0 being used uninitialized. + * They will be initialized on the fist pass of the loop (since s0 is NULL). */ + unsigned long dups, skip_fields, skip_chars, i; + const char *s0, *e0, *s1, *e1, *input_filename; + int opt; + int uniq_flags = 6; /* -u */ + + skip_fields = skip_chars = 0; + + while ((opt = getopt(argc, argv, uniq_opts)) > 0) { + if (opt == 'f') { + skip_fields = bb_xgetularg10(optarg); + } else if (opt == 's') { + skip_chars = bb_xgetularg10(optarg); + } else if ((s0 = strchr(uniq_opts, opt)) != NULL) { + uniq_flags &= s0[4]; + uniq_flags |= s0[7]; + } else { + bb_show_usage(); + } + } + + input_filename = *(argv += optind); + + in = xgetoptfile_sort_uniq(argv, "r"); + if (*argv) { + ++argv; + } + out = xgetoptfile_sort_uniq(argv, "w"); + if (*argv && argv[1]) { + bb_show_usage(); + } + + s0 = NULL; + + /* gnu uniq ignores newlines */ + while ((s1 = bb_get_chomped_line_from_file(in)) != NULL) { + e1 = s1; + for (i=skip_fields ; i ; i--) { + e1 = bb_skip_whitespace(e1); + while (*e1 && !isspace(*e1)) { + ++e1; + } + } + for (i = skip_chars ; *e1 && i ; i--) { + ++e1; + } + if (s0) { + if (strcmp(e0, e1) == 0) { + ++dups; /* Note: Testing for overflow seems excessive. */ + continue; + } + DO_LAST: + if ((dups && (uniq_flags & 2)) || (!dups && (uniq_flags & 4))) { + bb_fprintf(out, "\0%7d\t" + (uniq_flags & 1), dups + 1); + bb_fprintf(out, "%s\n", s0); + } + free((void *)s0); + } + + s0 = s1; + e0 = e1; + dups = 0; + } + + if (s0) { + e1 = NULL; + goto DO_LAST; + } + + bb_xferror(in, input_filename); + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/usleep.c b/busybox/coreutils/usleep.c new file mode 100644 index 000000000..f570f2734 --- /dev/null +++ b/busybox/coreutils/usleep.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ +/* + * usleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ + +#include +#include +#include +#include "busybox.h" + +extern int usleep_main(int argc, char **argv) +{ + if (argc != 2) { + bb_show_usage(); + } + + if (usleep(bb_xgetularg10_bnd(argv[1], 0, UINT_MAX))) { + bb_perror_nomsg_and_die(); + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/uudecode.c b/busybox/coreutils/uudecode.c new file mode 100644 index 000000000..57d4e8371 --- /dev/null +++ b/busybox/coreutils/uudecode.c @@ -0,0 +1,201 @@ +/* + * GPLv2 + * Copyright 2003, Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Based on specification from + * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html + * + * Bugs: the spec doesnt mention anything about "`\n`\n" prior to the "end" line + */ + + +#include +#include +#include +#include +#include + +#include "libbb.h" + +static int read_stduu(FILE *src_stream, FILE *dst_stream) +{ + char *line; + + while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) { + int length; + char *line_ptr = line; + + if (strcmp(line, "end") == 0) { + return(EXIT_SUCCESS); + } + length = ((*line_ptr - 0x20) & 0x3f)* 4 / 3; + + if (length <= 0) { + /* Ignore the "`\n" line, why is it even in the encode file ? */ + continue; + } + if (length > 60) { + bb_error_msg_and_die("Line too long"); + } + + line_ptr++; + /* Tolerate an overly long line to acomadate a possible exta '`' */ + if (strlen(line_ptr) < length) { + bb_error_msg_and_die("Short file"); + } + + while (length > 0) { + /* Merge four 6 bit chars to three 8 bit chars */ + fputc(((line_ptr[0] - 0x20) & 077) << 2 | ((line_ptr[1] - 0x20) & 077) >> 4, dst_stream); + line_ptr++; + length--; + if (length == 0) { + break; + } + + fputc(((line_ptr[0] - 0x20) & 077) << 4 | ((line_ptr[1] - 0x20) & 077) >> 2, dst_stream); + line_ptr++; + length--; + if (length == 0) { + break; + } + + fputc(((line_ptr[0] - 0x20) & 077) << 6 | ((line_ptr[1] - 0x20) & 077), dst_stream); + line_ptr += 2; + length -= 2; + } + free(line); + } + bb_error_msg_and_die("Short file"); +} + +static int read_base64(FILE *src_stream, FILE *dst_stream) +{ + const char *base64_table = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"; + char term_count = 0; + + while (1) { + char translated[4]; + int count = 0; + + while (count < 4) { + char *table_ptr; + char ch; + + /* Get next _valid_ character */ + do { + ch = fgetc(src_stream); + if (ch == EOF) { + bb_error_msg_and_die("Short file"); + } + } while ((table_ptr = strchr(base64_table, ch)) == NULL); + + /* Convert encoded charcter to decimal */ + ch = table_ptr - base64_table; + + if (*table_ptr == '=') { + if (term_count == 0) { + translated[count] = 0; + break; + } + term_count++; + } + else if (*table_ptr == '\n') { + /* Check for terminating line */ + if (term_count == 5) { + return(EXIT_SUCCESS); + } + term_count = 1; + continue; + } else { + translated[count] = ch; + count++; + term_count = 0; + } + } + + /* Merge 6 bit chars to 8 bit */ + fputc(translated[0] << 2 | translated[1] >> 4, dst_stream); + if (count > 2) { + fputc(translated[1] << 4 | translated[2] >> 2, dst_stream); + } + if (count > 3) { + fputc(translated[2] << 6 | translated[3], dst_stream); + } + } +} + +extern int uudecode_main(int argc, char **argv) +{ + int (*decode_fn_ptr) (FILE * src, FILE * dst); + FILE *src_stream; + char *outname = NULL; + char *line; + int opt; + + opt = bb_getopt_ulflags(argc, argv, "o:", &outname); + + if (optind == argc) { + src_stream = stdin; + } else if (optind + 1 == argc) { + src_stream = bb_xfopen(argv[optind], "r"); + } else { + bb_show_usage(); + } + + /* Search for the start of the encoding */ + while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) { + char *line_ptr = NULL; + + if (line == NULL) { + break; + } else if (strncmp(line, "begin-base64 ", 13) == 0) { + line_ptr = line + 13; + decode_fn_ptr = read_base64; + } else if (strncmp(line, "begin ", 6) == 0) { + line_ptr = line + 6; + decode_fn_ptr = read_stduu; + } + + if (line_ptr) { + FILE *dst_stream; + int mode; + int ret; + + mode = strtoul(line_ptr, NULL, 8); + if (outname == NULL) { + outname = strchr(line_ptr, ' '); + if ((outname == NULL) || (*outname == '\0')) { + break; + } + outname++; + } + if (strcmp(outname, "-") == 0) { + dst_stream = stdout; + } else { + dst_stream = bb_xfopen(outname, "w"); + chmod(outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + } + free(line); + ret = decode_fn_ptr(src_stream, dst_stream); + bb_fclose_nonstdin(src_stream); + return(ret); + } + free(line); + } + bb_error_msg_and_die("No `begin' line"); +} diff --git a/busybox/coreutils/uuencode.c b/busybox/coreutils/uuencode.c new file mode 100644 index 000000000..42f629f48 --- /dev/null +++ b/busybox/coreutils/uuencode.c @@ -0,0 +1,149 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2000 by Glenn McGrath + * + * based on the function base64_encode from http.c in wget v1.6 + * Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* Conversion table. for base 64 */ +static const char tbl_base64[65] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', + '=' /* termination character */ +}; + +static const char tbl_std[65] = { + '`', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`' /* termination character */ +}; + +/* + * Encode the string S of length LENGTH to base64 format and place it + * to STORE. STORE will be 0-terminated, and must point to a writable + * buffer of at least 1+BASE64_LENGTH(length) bytes. + * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3)) + */ +static void uuencode (const unsigned char *s, const char *store, const int length, const char *tbl) +{ + int i; + unsigned char *p = (unsigned char *)store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] & 0x3f]; + s += 3; + } + /* Pad the result if necessary... */ + if (i == length + 1) { + *(p - 1) = tbl[64]; + } + else if (i == length + 2) { + *(p - 1) = *(p - 2) = tbl[64]; + } + /* ...and zero-terminate it. */ + *p = '\0'; +} + +#define SRC_BUF_SIZE 45 // This *MUST* be a multiple of 3 +#define DST_BUF_SIZE 4 * ((SRC_BUF_SIZE + 2) / 3) +int uuencode_main(int argc, char **argv) +{ + const int src_buf_size = SRC_BUF_SIZE; + const int dst_buf_size = DST_BUF_SIZE; + int write_size = dst_buf_size; + struct stat stat_buf; + FILE *src_stream = stdin; + const char *tbl; + size_t size; + mode_t mode; + RESERVE_CONFIG_BUFFER(src_buf, SRC_BUF_SIZE + 1); + RESERVE_CONFIG_BUFFER(dst_buf, DST_BUF_SIZE + 1); + + tbl = tbl_std; + if (bb_getopt_ulflags(argc, argv, "m") & 1) { + tbl = tbl_base64; + } + + switch (argc - optind) { + case 2: + src_stream = bb_xfopen(argv[optind], "r"); + if (stat(argv[optind], &stat_buf) < 0) { + bb_perror_msg_and_die("stat"); + } + mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + if (src_stream == stdout) { + puts("NULL"); + } + break; + case 1: + mode = 0666 & ~umask(0666); + break; + default: + bb_show_usage(); + } + + bb_printf("begin%s %o %s", tbl == tbl_std ? "" : "-base64", mode, argv[argc - 1]); + + while ((size = fread(src_buf, 1, src_buf_size, src_stream)) > 0) { + if (size != src_buf_size) { + /* write_size is always 60 until the last line */ + write_size=(4 * ((size + 2) / 3)); + /* pad with 0s so we can just encode extra bits */ + memset(&src_buf[size], 0, src_buf_size - size); + } + /* Encode the buffer we just read in */ + uuencode(src_buf, dst_buf, size, tbl); + + putchar('\n'); + if (tbl == tbl_std) { + putchar(tbl[size]); + } + if (fwrite(dst_buf, 1, write_size, stdout) != write_size) { + bb_perror_msg_and_die(bb_msg_write_error); + } + } + bb_printf(tbl == tbl_std ? "\n`\nend\n" : "\n====\n"); + + bb_xferror(src_stream, "source"); /* TODO - Fix this! */ + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/watch.c b/busybox/coreutils/watch.c new file mode 100644 index 000000000..f9f40189e --- /dev/null +++ b/busybox/coreutils/watch.c @@ -0,0 +1,110 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini watch implementation for busybox + * + * Copyright (C) 2001 by Michael Habermann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 N/A */ +/* BB_AUDIT GNU defects -- only option -n is supported. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Removed dependency on date_main(), added proper error checking, and + * reduced size. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int watch_main(int argc, char **argv) +{ + const int header_len = 40; + time_t t; + pid_t pid; + unsigned period = 2; + int old_stdout; + int len, len2; + char **watched_argv; + char header[header_len + 1]; + + if (argc < 2) { + bb_show_usage(); + } + + /* don't use getopt, because it permutes the arguments */ + ++argv; + if ((argc > 3) && !strcmp(*argv, "-n") + ) { + period = bb_xgetularg10_bnd(argv[1], 1, UINT_MAX); + argv += 2; + } + watched_argv = argv; + + /* create header */ + + len = snprintf(header, header_len, "Every %ds:", period); + /* Don't bother checking for len < 0, as it should never happen. + * But, just to be prepared... */ + assert(len >= 0); + do { + len2 = strlen(*argv); + if (len + len2 >= header_len-1) { + break; + } + header[len] = ' '; + memcpy(header+len+1, *argv, len2); + len += len2+1; + } while (*++argv); + + header[len] = 0; + + /* thanks to lye, who showed me how to redirect stdin/stdout */ + old_stdout = dup(1); + + while (1) { + time(&t); + /* Use dprintf to avoid fflush()ing stdout. */ + if (dprintf(1, "\033[H\033[J%-*s%s\n", header_len, header, ctime(&t)) < 0) { + bb_perror_msg_and_die("printf"); + } + + pid = vfork(); /* vfork, because of ucLinux */ + if (pid > 0) { + //parent + wait(0); + sleep(period); + } else if (0 == pid) { + //child + close(1); + dup(old_stdout); + if (execvp(*watched_argv, watched_argv)) { + bb_error_msg_and_die("Couldn't run command\n"); + } + } else { + bb_error_msg_and_die("Couldn't vfork\n"); + } + } +} diff --git a/busybox/coreutils/wc.c b/busybox/coreutils/wc.c new file mode 100644 index 000000000..0eb795c4b --- /dev/null +++ b/busybox/coreutils/wc.c @@ -0,0 +1,227 @@ +/* vi: set sw=4 ts=4: */ +/* + * wc implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- option -m is not currently supported. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Rewritten to fix a number of problems and do some size optimizations. + * Problems in the previous busybox implementation (besides bloat) included: + * 1) broken 'wc -c' optimization (read note below) + * 2) broken handling of '-' args + * 3) no checking of ferror on EOF returns + * 4) isprint() wasn't considered when word counting. + * + * TODO: + * + * When locale support is enabled, count multibyte chars in the '-m' case. + * + * NOTES: + * + * The previous busybox wc attempted an optimization using stat for the + * case of counting chars only. I omitted that because it was broken. + * It didn't take into account the possibility of input coming from a + * pipe, or input from a file with file pointer not at the beginning. + * + * To implement such a speed optimization correctly, not only do you + * need the size, but also the file position. Note also that the + * file position may be past the end of file. Consider the example + * (adapted from example in gnu wc.c) + * + * echo hello > /tmp/testfile && + * (dd ibs=1k skip=1 count=0 &> /dev/null ; wc -c) < /tmp/testfile + * + * for which 'wc -c' should output '0'. + */ + +#include +#include +#include +#include +#include "busybox.h" + +#ifdef CONFIG_LOCALE_SUPPORT +#include +#include +#define isspace_given_isprint(c) isspace(c) +#else +#undef isspace +#undef isprint +#define isspace(c) ((((c) == ' ') || (((unsigned int)((c) - 9)) <= (13 - 9)))) +#define isprint(c) (((unsigned int)((c) - 0x20)) <= (0x7e - 0x20)) +#define isspace_given_isprint(c) ((c) == ' ') +#endif + +enum { + WC_LINES = 0, + WC_WORDS = 1, + WC_CHARS = 2, + WC_LENGTH = 3 +}; + +/* Note: If this changes, remember to change the initialization of + * 'name' in wc_main. It needs to point to the terminating nul. */ +static const char wc_opts[] = "lwcL"; /* READ THE WARNING ABOVE! */ + +enum { + OP_INC_LINE = 1, /* OP_INC_LINE must be 1. */ + OP_SPACE = 2, + OP_NEWLINE = 4, + OP_TAB = 8, + OP_NUL = 16, +}; + +/* Note: If fmt_str changes, the offsets to 's' in the OUTPUT section + * will need to be updated. */ +static const char fmt_str[] = " %7u\0 %s\n"; +static const char total_str[] = "total"; + +int wc_main(int argc, char **argv) +{ + FILE *fp; + const char *s; + unsigned int *pcounts; + unsigned int counts[4]; + unsigned int totals[4]; + unsigned int linepos; + unsigned int u; + int num_files = 0; + int c; + char status = EXIT_SUCCESS; + char in_word; + char print_type; + + print_type = bb_getopt_ulflags(argc, argv, wc_opts); + + if (print_type == 0) { + print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_CHARS); + } + + argv += optind; + if (!*argv) { + *--argv = (char *) bb_msg_standard_input; + } + + memset(totals, 0, sizeof(totals)); + + pcounts = counts; + + do { + ++num_files; + if (!(fp = bb_wfopen_input(*argv))) { + status = EXIT_FAILURE; + continue; + } + + memset(counts, 0, sizeof(counts)); + linepos = 0; + in_word = 0; + + do { + ++counts[WC_CHARS]; + c = getc(fp); + if (isprint(c)) { + ++linepos; + if (!isspace_given_isprint(c)) { + in_word = 1; + continue; + } + } else if (((unsigned int)(c - 9)) <= 4) { + /* \t 9 + * \n 10 + * \v 11 + * \f 12 + * \r 13 + */ + if (c == '\t') { + linepos = (linepos | 7) + 1; + } else { /* '\n', '\r', '\f', or '\v' */ + DO_EOF: + if (linepos > counts[WC_LENGTH]) { + counts[WC_LENGTH] = linepos; + } + if (c == '\n') { + ++counts[WC_LINES]; + } + if (c != '\v') { + linepos = 0; + } + } + } else if (c == EOF) { + if (ferror(fp)) { + bb_perror_msg("%s", *argv); + status = EXIT_FAILURE; + } + --counts[WC_CHARS]; + goto DO_EOF; /* Treat an EOF as '\r'. */ + } else { + continue; + } + + counts[WC_WORDS] += in_word; + in_word = 0; + if (c == EOF) { + break; + } + } while (1); + + if (totals[WC_LENGTH] < counts[WC_LENGTH]) { + totals[WC_LENGTH] = counts[WC_LENGTH]; + } + totals[WC_LENGTH] -= counts[WC_LENGTH]; + + bb_fclose_nonstdin(fp); + + OUTPUT: + s = fmt_str + 1; /* Skip the leading space on 1st pass. */ + u = 0; + do { + if (print_type & (1 << u)) { + bb_printf(s, pcounts[u]); + s = fmt_str; /* Ok... restore the leading space. */ + } + totals[u] += pcounts[u]; + } while (++u < 4); + + s += 8; /* Set the format to the empty string. */ + + if (*argv != bb_msg_standard_input) { + s -= 3; /* We have a name, so do %s conversion. */ + } + bb_printf(s, *argv); + + } while (*++argv); + + /* If more than one file was processed, we want the totals. To save some + * space, we set the pcounts ptr to the totals array. This has the side + * effect of trashing the totals array after outputting it, but that's + * irrelavent since we no longer need it. */ + if (num_files > 1) { + num_files = 0; /* Make sure we don't get here again. */ + *--argv = (char *) total_str; + pcounts = totals; + goto OUTPUT; + } + + bb_fflush_stdout_and_exit(status); +} diff --git a/busybox/coreutils/who.c b/busybox/coreutils/who.c new file mode 100644 index 000000000..9561db132 --- /dev/null +++ b/busybox/coreutils/who.c @@ -0,0 +1,83 @@ +/* vi: set sw=4 ts=4: */ +/*---------------------------------------------------------------------- + * Mini who is used to display user name, login time, + * idle time and host name. + * + * Author: Da Chen + * + * This is a free document; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation: + * http://www.gnu.org/copyleft/gpl.html + * + * Copyright (c) 2002 AYR Networks, Inc. + *---------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int who_main(int argc, char **argv) +{ + struct utmp *ut; + struct stat st; + int devlen, len; + time_t now, idle; + + if (argc > 1) + bb_show_usage(); + + setutent(); + devlen = sizeof("/dev/") - 1; + printf("USER TTY IDLE FROM HOST\n"); + + while ((ut = getutent()) != NULL) { + char name[40]; + + if (ut->ut_user[0] && ut->ut_type == USER_PROCESS) { + len = strlen(ut->ut_line); + if (ut->ut_line[0] == '/') { + strncpy(name, ut->ut_line, len); + name[len] = '\0'; + strcpy(ut->ut_line, ut->ut_line + devlen); + } else { + strcpy(name, "/dev/"); + strncpy(name+devlen, ut->ut_line, len); + name[devlen+len] = '\0'; + } + + printf("%-10s %-8s ", ut->ut_user, ut->ut_line); + + if (stat(name, &st) == 0) { + now = time(NULL); + idle = now - st.st_atime; + + if (idle < 60) + printf("00:00m "); + else if (idle < (60 * 60)) + printf("00:%02dm ", (int)(idle / 60)); + else if (idle < (24 * 60 * 60)) + printf("%02d:%02dm ", (int)(idle / (60 * 60)), + (int)(idle % (60 * 60)) / 60); + else if (idle < (24 * 60 * 60 * 365)) + printf("%03ddays ", (int)(idle / (24 * 60 * 60))); + else + printf("%02dyears ", (int) (idle / (24 * 60 * 60 * 365))); + } else + printf("%-8s ", "?"); + + printf("%-12.12s %s\n", ctime(&(ut->ut_tv.tv_sec)) + 4, ut->ut_host); + } + } + endutent(); + + return 0; +} diff --git a/busybox/coreutils/whoami.c b/busybox/coreutils/whoami.c new file mode 100644 index 000000000..6a6e2eec9 --- /dev/null +++ b/busybox/coreutils/whoami.c @@ -0,0 +1,38 @@ +/* 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 + * + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include +#include +#include +#include "busybox.h" + +extern int whoami_main(int argc, char **argv) +{ + if (argc > 1) + bb_show_usage(); + + puts(my_getpwuid(NULL, geteuid(), -1)); + /* exits on error */ + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/yes.c b/busybox/coreutils/yes.c new file mode 100644 index 000000000..74f7571cf --- /dev/null +++ b/busybox/coreutils/yes.c @@ -0,0 +1,56 @@ +/* vi: set sw=4 ts=4: */ +/* + * yes implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reductions and removed redundant applet name prefix from error messages. + */ + +#include +#include +#include "busybox.h" + +extern int yes_main(int argc, char **argv) +{ + static const char fmt_str[] = " %s"; + const char *fmt; + char **first_arg; + + *argv = "y"; + if (argc != 1) { + ++argv; + } + + first_arg = argv; + do { + fmt = fmt_str + 1; + do { + bb_printf(fmt, *argv); + fmt = fmt_str; + } while (*++argv); + argv = first_arg; + } while (putchar('\n') != EOF); + + bb_perror_nomsg_and_die(); +} diff --git a/busybox/debian/busybox-cvs-doc.docs b/busybox/debian/busybox-cvs-doc.docs new file mode 100644 index 000000000..e6f531b5b --- /dev/null +++ b/busybox/debian/busybox-cvs-doc.docs @@ -0,0 +1 @@ +docs/busybox.pdf diff --git a/busybox/debian/busybox-cvs-static.dirs b/busybox/debian/busybox-cvs-static.dirs new file mode 100644 index 000000000..f08836524 --- /dev/null +++ b/busybox/debian/busybox-cvs-static.dirs @@ -0,0 +1,2 @@ +bin +usr/share/lintian/overrides diff --git a/busybox/debian/busybox-cvs-static.manpages b/busybox/debian/busybox-cvs-static.manpages new file mode 100644 index 000000000..c28fa9091 --- /dev/null +++ b/busybox/debian/busybox-cvs-static.manpages @@ -0,0 +1 @@ +busybox.1 diff --git a/busybox/debian/busybox-cvs-static.override b/busybox/debian/busybox-cvs-static.override new file mode 100644 index 000000000..480494f6a --- /dev/null +++ b/busybox/debian/busybox-cvs-static.override @@ -0,0 +1 @@ +busybox-cvs-static: statically-linked-binary ./bin/busybox diff --git a/busybox/debian/busybox-cvs.dirs b/busybox/debian/busybox-cvs.dirs new file mode 100644 index 000000000..ba077a403 --- /dev/null +++ b/busybox/debian/busybox-cvs.dirs @@ -0,0 +1 @@ +bin diff --git a/busybox/debian/busybox-cvs.manpages b/busybox/debian/busybox-cvs.manpages new file mode 100644 index 000000000..c28fa9091 --- /dev/null +++ b/busybox/debian/busybox-cvs.manpages @@ -0,0 +1 @@ +busybox.1 diff --git a/busybox/debian/changelog b/busybox/debian/changelog new file mode 100644 index 000000000..d9f82e376 --- /dev/null +++ b/busybox/debian/changelog @@ -0,0 +1,479 @@ +busybox-cvs (20031212-3) unstable; urgency=low + + * debian/config-udeb-linux: + - Enable freeramdisk. (closes: #225360) + + -- Bastian Blank Tue, 30 Dec 2003 22:36:50 +0100 + +busybox-cvs (20031212-2) unstable; urgency=low + + * debian/config-udeb: + - Enable freeramdisk + * archival/libunarchive/data_extract_to_stdout.c: + - Don't extract to much (upstream). + * archival/libunarchive/data_extract_all.c: + - Don't set permissions on symlinks (upstream). + * editors/sed.c: + - Fix (upstream). (closes: #224676) + + -- Bastian Blank Tue, 23 Dec 2003 16:59:13 +0100 + +busybox-cvs (20031212-1) unstable; urgency=low + + * new cvs version + - fixes IOR in fdisk. (closes: #223773) + * debian/config* + - update + - remove mtab support from mount in busybox-static. (closes: #222386) + - enable progress bar for wget. (closes: #223770) + * modutils/obj/obj_s390{,x}.c + - fix abi change, R_390_GOTOFF -> R_390_GOTOFF32. (closes: #216528) + * acknowledge nmu. (closes: #216950, #216756, #215169, #215613) + + -- Bastian Blank Fri, 12 Dec 2003 21:19:41 +0100 + +busybox-cvs (20030926-2.1) unstable; urgency=low + + * NMU + * Remove /sbin/init from the udebs, while still leaving init support + compiled in. rootskel takes over providing init, but then calls bb init. + Remove linuxrc support from the udebs entirely. Closes: #216756 + * config-floppy-udeb-linux: add minimal find, grep. Closes: #215169 + * net-udeb-linux-i386: add loopback mount support. Closes: #215613 + + -- Joey Hess Tue, 21 Oct 2003 12:47:52 -0400 + +busybox-cvs (20030926-2) unstable; urgency=low + + * debian/config-floppy-udeb-linux + - update for new floppy images (closes: #212986, #214102) + + -- Bastian Blank Thu, 09 Oct 2003 12:25:49 +0200 + +busybox-cvs (20030926-1) unstable; urgency=low + + * new cvs version + * Makefile + - fix libpwdgrp link (upstream) (closes: #211675) + * Rules.mak.in + - fix optimization (closes: #212485) + * debian/config* + - update + * debian/config*udeb* + - move linux-i386 to linux (enable modutils on any linux arch) + - rename net to floppy + - enable wget status bar (closes: #211457) + * init/init.c + - workaround race conditions (closes: #212764) + + -- Bastian Blank Fri, 26 Sep 2003 15:10:14 +0200 + +busybox-cvs (0.60.99.cvs20030819-3) unstable; urgency=low + + * shell/ash.c + - fix signal handling (upstream) + + -- Bastian Blank Mon, 15 Sep 2003 18:12:09 +0200 + +busybox-cvs (0.60.99.cvs20030819-2) unstable; urgency=low + + * Fix configure permisions + * Set source Section to embedded + + -- Glenn McGrath Mon, 25 Aug 2003 06:33:19 +0000 + +busybox-cvs (0.60.99.cvs20030819-1) unstable; urgency=low + + * new cvs version + + -- Bastian Blank Tue, 19 Aug 2003 13:18:54 +0200 + +busybox-cvs (0.60.99.cvs20030426-10) unstable; urgency=low + + * archival/libunarchive/* + - add hardlink support (pending) + * debian/config-*udeb* + - add support for oldgnu tar format + + -- Bastian Blank Tue, 10 Jun 2003 12:06:41 +0200 + +busybox-cvs (0.60.99.cvs20030426-9) unstable; urgency=low + + * modutils/depmod.c + - fix base_dir for modules.dep + + -- Bastian Blank Sat, 07 Jun 2003 14:52:54 +0200 + +busybox-cvs (0.60.99.cvs20030426-8) unstable; urgency=low + + * util-linux/fdisk.c + - fix syscalls. (pending) + * libpwd/* + - don't build setgroups.o + + -- Bastian Blank Tue, 03 Jun 2003 11:26:28 +0200 + +busybox-cvs (0.60.99.cvs20030426-7) unstable; urgency=low + + * libbb/*syscallc.c + - fix syscalls. (upstream) (closes: #194631) + * modutils/* + - update complete objects code from upstream. (pending) + * debian/config-*udeb* + - update to busybox-applets.txt:1.14 + + -- Bastian Blank Thu, 29 May 2003 16:17:14 +0200 + +busybox-cvs (0.60.99.cvs20030426-6) unstable; urgency=low + + * modutils/Makefile.in + - don't build anything if deactivated + + -- Bastian Blank Sat, 24 May 2003 18:57:14 +0200 + +busybox-cvs (0.60.99.cvs20030426-5) unstable; urgency=low + + * debian/config-*udeb* + - revert changes (closes: #192717, #192753) + - readd initrd support (workaround) + - make init quiet + * archival/tar.c + - fix usage of tar -O (upstream) (closes: #193788) + + -- Bastian Blank Sat, 24 May 2003 13:25:36 +0200 + +busybox-cvs (0.60.99.cvs20030426-4) unstable; urgency=low + + * debian/config-*udeb* + - update to busybox-applets.txt:1.12 + * debian/rules + - use system instead of arch to determine which config file to use + + -- Bastian Blank Fri, 09 May 2003 11:21:13 +0200 + +busybox-cvs (0.60.99.cvs20030426-3) unstable; urgency=low + + * debian/config-udeb* + - update to busybox-applets.txt:1.11 + + -- Bastian Blank Wed, 07 May 2003 10:37:40 +0200 + +busybox-cvs (0.60.99.cvs20030426-2) unstable; urgency=low + + * modutils/* + - modprobe failes gracefully if the module is already loaded. (pending) + - implement depmod. (pending) + * debian/config* + - update + + -- Bastian Blank Wed, 30 Apr 2003 14:12:36 +0200 + +busybox-cvs (0.60.99.cvs20030426-1) unstable; urgency=low + + * new cvs version + + -- Bastian Blank Sat, 26 Apr 2003 18:33:17 +0200 + +busybox-cvs (0.60.99.cvs20030420-4) unstable; urgency=low + + * network/libiproute/iproute.c + - fix usage of ip route flush (upstream) + + -- Bastian Blank Fri, 25 Apr 2003 16:48:43 +0200 + +busybox-cvs (0.60.99.cvs20030420-3) unstable; urgency=low + + * debian/config-*udeb* + - update to busybox-applets.txt:1.10 + + -- Bastian Blank Fri, 25 Apr 2003 14:03:23 +0200 + +busybox-cvs (0.60.99.cvs20030420-2) unstable; urgency=low + + * archival/tar.c, archival/libunarchive/data_extract_all.c + - unlink files first (upstream patch) + + -- Bastian Blank Mon, 21 Apr 2003 12:42:06 +0200 + +busybox-cvs (0.60.99.cvs20030420-1) unstable; urgency=low + + * new cvs version + * debian/config*-udeb* + - add ifconfig/route for easier transition + * debian/config-net-udeb-i386 + - add logger + * debian/config-udeb* + - add uniq + + -- Bastian Blank Sun, 20 Apr 2003 21:02:58 +0200 + +busybox-cvs (0.60.99.cvs20030405-1) unstable; urgency=low + + * new cvs version + * debian/control + - add busybox-cvs-net-udeb + * debian/rules + - arch depend debs + - new net-udeb + * debian/config* + - cleanup applet list + + -- Bastian Blank Sat, 05 Apr 2003 11:44:50 +0200 + +busybox-cvs (0.60.99.cvs20030221-1) unstable; urgency=low + + * new cvs version + * enable new applets in udeb + + -- Bastian Blank Fri, 21 Feb 2003 23:15:16 +0100 + +busybox-cvs (0.60.99.cvs20030114-1) unstable; urgency=low + + * new cvs version + + -- Bastian Blank Tue, 14 Jan 2003 17:06:43 +0000 + +busybox-cvs (0.60.99.cvs20030105-1) unstable; urgency=low + + * Fix ip command build failure on ia64 (Closes: #172580 + * Dont build with BSD partition table support in fdisk, fails on m68k + + -- Glenn McGrath Sun, 5 Jan 2003 12:48:05 +1100 + +busybox-cvs (0.60.99.cvs20030104-2) unstable; urgency=low + + * floppy-retriever needs the cut command in the udeb + + -- Glenn McGrath Sat, 4 Jan 2003 17:13:05 +1100 + +busybox-cvs (0.60.99.cvs20030104-1) unstable; urgency=low + + * new cvs version + * Include new applets in the static package + * Include ash in the udeb + + -- Glenn McGrath Sat, 4 Jan 2003 13:39:04 +1100 + +busybox-cvs (0.60.99.cvs20021214-1) unstable; urgency=low + + * new cvs version + - udhcp merge. + * fix location of ip link. + + -- Bastian Blank Sat, 14 Dec 2002 13:52:15 +0100 + +busybox-cvs (0.60.99.cvs20021210-1) unstable; urgency=low + + * new cvs version + - various upstream fixes found in the last version. + * busybox-cvs-udeb + - include readlink and realpath. + + -- Bastian Blank Tue, 10 Dec 2002 21:23:40 +0100 + +busybox-cvs (0.60.99.cvs20021209-2) unstable; urgency=low + + * cleanup scripts/config/ within make clean (closes: #172413) + * busybox-cvs-udeb + - include readlink + * include manpages within busybox-cvs and busybox-cvs-static + + -- Bastian Blank Mon, 09 Dec 2002 22:09:52 +0100 + +busybox-cvs (0.60.99.cvs20021209-1) unstable; urgency=low + + * New cvs version. + - fix udhcpc for use with ip. + - klogd supports -c. + * busybox-cvs-udeb + - set priority to extra. (closes: #172302) + - don't longer provide busybox-udeb. + + -- Bastian Blank Mon, 09 Dec 2002 16:22:03 +0100 + +busybox-cvs (0.60.99.cvs20021203-1) unstable; urgency=low + + * New packages based on busybox cvs. + * changes for the udeb + - enable ip applet with address, link and route part. + - enable udhcpc. + - disable ifconfig and route applet. + - disable ls color. + + -- Bastian Blank Tue, 03 Dec 2002 18:51:00 +0100 + +busybox (1:0.60.0-1) unstable; urgency=low + + * New version released. See changelog for details. + + -- Erik Andersen 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/compat b/busybox/debian/compat new file mode 100644 index 000000000..b8626c4cf --- /dev/null +++ b/busybox/debian/compat @@ -0,0 +1 @@ +4 diff --git a/busybox/debian/config-deb b/busybox/debian/config-deb new file mode 100644 index 000000000..9c1034a20 --- /dev/null +++ b/busybox/debian/config-deb @@ -0,0 +1,379 @@ +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y + +# +# General Configuration +# +# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set +CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +# CONFIG_FEATURE_VERBOSE_USAGE is not set +CONFIG_FEATURE_INSTALLER=y +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_FEATURE_DEVFS is not set +# CONFIG_FEATURE_DEVPTS is not set +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_SUID is not set +# CONFIG_SELINUX is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +CONFIG_LFS=y +# USING_CROSS_COMPILER is not set +EXTRA_CFLAGS_OPTIONS="" + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +PREFIX="./_install" + +# +# Archival Utilities +# +CONFIG_AR=y +# CONFIG_FEATURE_AR_LONG_FILENAMES is not set +# CONFIG_BUNZIP2 is not set +# CONFIG_CPIO is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +CONFIG_GUNZIP=y +# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set +CONFIG_GZIP=y +# CONFIG_RPM2CPIO is not set +# CONFIG_RPM is not set +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +# CONFIG_FEATURE_TAR_BZIP2 is not set +# CONFIG_FEATURE_TAR_EXCLUDE is not set +CONFIG_FEATURE_TAR_GZIP=y +# CONFIG_FEATURE_TAR_COMPRESS is not set +# CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY is not set +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +# CONFIG_UNCOMPRESS is not set +# CONFIG_UNZIP is not set + +# +# Common options for cpio and tar +# +# CONFIG_FEATURE_UNARCHIVE_TAPE is not set + +# +# Coreutils +# +# CONFIG_BASENAME is not set +# CONFIG_CAL is not set +CONFIG_CAT=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_CHROOT=y +# CONFIG_CMP is not set +CONFIG_CP=y +CONFIG_CUT=y +# CONFIG_DATE is not set +# CONFIG_DD is not set +CONFIG_DF=y +CONFIG_DIRNAME=y +# CONFIG_DOS2UNIX is not set +# CONFIG_DU is not set +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +# CONFIG_ENV is not set +CONFIG_EXPR=y +CONFIG_FALSE=y +# CONFIG_FOLD is not set +CONFIG_HEAD=y +# CONFIG_FEATURE_FANCY_HEAD is not set +# CONFIG_HOSTID is not set +CONFIG_ID=y +# CONFIG_INSTALL is not set +# CONFIG_LENGTH is not set +CONFIG_LN=y +# CONFIG_LOGNAME is not set +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +CONFIG_FEATURE_LS_COLOR=y +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +# CONFIG_MKFIFO is not set +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_OD is not set +# CONFIG_PRINTF is not set +CONFIG_PWD=y +# CONFIG_REALPATH is not set +CONFIG_RM=y +CONFIG_RMDIR=y +# CONFIG_SHA1SUM is not set +CONFIG_SLEEP=y +# CONFIG_FEATURE_FANCY_SLEEP is not set +CONFIG_SORT=y +# CONFIG_STTY is not set +CONFIG_SYNC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +# CONFIG_TEE is not set +CONFIG_TEST=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +CONFIG_UNIQ=y +# CONFIG_USLEEP is not set +# CONFIG_UUDECODE is not set +# CONFIG_UUENCODE is not set +# CONFIG_WATCH is not set +CONFIG_WC=y +# CONFIG_WHO is not set +CONFIG_WHOAMI=y +# CONFIG_YES is not set + +# +# Common options for cp and mv +# +# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set + +# +# Common options for ls and more +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum +# +# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set + +# +# Console Utilities +# +# CONFIG_CHVT is not set +CONFIG_CLEAR=y +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_LOADACM is not set +# CONFIG_LOADFONT is not set +CONFIG_LOADKMAP=y +# CONFIG_OPENVT is not set +CONFIG_RESET=y +# CONFIG_SETKEYCODES is not set + +# +# Debian Utilities +# +# CONFIG_MKTEMP is not set +# CONFIG_PIPE_PROGRESS is not set +# CONFIG_READLINK is not set +# CONFIG_RUN_PARTS is not set +# CONFIG_START_STOP_DAEMON is not set +CONFIG_WHICH=y + +# +# Editors +# +# CONFIG_AWK is not set +# CONFIG_PATCH is not set +CONFIG_SED=y +# CONFIG_VI is not set + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +# CONFIG_FEATURE_FIND_XDEV is not set +CONFIG_FEATURE_FIND_NEWER=y +CONFIG_FEATURE_FIND_INUM=y +CONFIG_GREP=y +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +CONFIG_FEATURE_GREP_FGREP_ALIAS=y +# CONFIG_FEATURE_GREP_CONTEXT is not set +# CONFIG_XARGS is not set + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +CONFIG_FEATURE_INITRD=y +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_FEATURE_EXTRA_QUIET is not set +CONFIG_HALT=y +CONFIG_POWEROFF=y +CONFIG_REBOOT=y +# CONFIG_MESG is not set + +# +# Login/Password Management Utilities +# +# CONFIG_USE_BB_PWD_GRP is not set +# CONFIG_ADDGROUP is not set +# CONFIG_DELGROUP is not set +# CONFIG_ADDUSER is not set +# CONFIG_DELUSER is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PASSWD is not set +# CONFIG_SU is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Miscellaneous Utilities +# +# CONFIG_ADJTIMEX is not set +# CONFIG_CROND is not set +# CONFIG_CRONTAB is not set +# CONFIG_DC is not set +# CONFIG_DEVFSD is not set +# CONFIG_LAST is not set +# CONFIG_HDPARM is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_MT is not set +# CONFIG_STRINGS is not set +# CONFIG_TIME is not set +# CONFIG_WATCHDOG is not set + +# +# Linux Module Utilities +# +# CONFIG_DEPMOD is not set +# CONFIG_INSMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_MODPROBE is not set +# CONFIG_RMMOD is not set + +# +# Networking Utilities +# +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_ARPING is not set +# CONFIG_FTPGET is not set +# CONFIG_FTPPUT is not set +CONFIG_HOSTNAME=y +# CONFIG_HTTPD is not set +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set +CONFIG_FEATURE_IFCONFIG_HW=y +# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set +# CONFIG_IFUPDOWN is not set +# CONFIG_INETD is not set +# CONFIG_IP is not set +# CONFIG_IPCALC is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_NAMEIF is not set +# CONFIG_NC is not set +# CONFIG_NETSTAT is not set +# CONFIG_NSLOOKUP is not set +CONFIG_PING=y +CONFIG_FEATURE_FANCY_PING=y +CONFIG_ROUTE=y +# CONFIG_TELNET is not set +# CONFIG_TELNETD is not set +# CONFIG_TFTP is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_IP6_LITERAL=y + +# +# udhcp Server/Client +# +# CONFIG_UDHCPD is not set +# CONFIG_UDHCPC is not set + +# +# Process Utilities +# +CONFIG_FREE=y +CONFIG_KILL=y +CONFIG_KILLALL=y +# CONFIG_PIDOF is not set +CONFIG_PS=y +# CONFIG_RENICE is not set +# CONFIG_TOP is not set +CONFIG_UPTIME=y + +# +# Another Bourne-like Shell +# +# CONFIG_FEATURE_SH_IS_ASH is not set +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_LASH is not set +# CONFIG_FEATURE_SH_IS_MSH is not set +CONFIG_FEATURE_SH_IS_NONE=y +# CONFIG_ASH is not set +# CONFIG_HUSH is not set +# CONFIG_LASH is not set +# CONFIG_MSH is not set + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +# CONFIG_FEATURE_ROTATE_LOGFILE is not set +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_KLOGD=y +CONFIG_LOGGER=y + +# +# Linux System Utilities +# +CONFIG_DMESG=y +# CONFIG_FBSET is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +# CONFIG_FREERAMDISK is not set +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_GETOPT is not set +# CONFIG_HEXDUMP is not set +# CONFIG_HWCLOCK is not set +CONFIG_LOSETUP=y +CONFIG_MKSWAP=y +CONFIG_MORE=y +CONFIG_FEATURE_USE_TERMIOS=y +# CONFIG_PIVOT_ROOT is not set +# CONFIG_RDATE is not set +CONFIG_SWAPONOFF=y +CONFIG_MOUNT=y +CONFIG_NFSMOUNT=y +CONFIG_UMOUNT=y +CONFIG_FEATURE_MOUNT_FORCE=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Debugging Options +# +# CONFIG_DEBUG is not set diff --git a/busybox/debian/config-floppy-udeb-linux b/busybox/debian/config-floppy-udeb-linux new file mode 100644 index 000000000..b5f728cef --- /dev/null +++ b/busybox/debian/config-floppy-udeb-linux @@ -0,0 +1,359 @@ +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y + +# +# General Configuration +# +# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set +CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +# CONFIG_FEATURE_VERBOSE_USAGE is not set +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_LOCALE_SUPPORT is not set +CONFIG_FEATURE_DEVFS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_SUID is not set +# CONFIG_SELINUX is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +CONFIG_LFS=y +# USING_CROSS_COMPILER is not set +EXTRA_CFLAGS_OPTIONS="" + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +PREFIX="./_install" + +# +# Archival Utilities +# +# CONFIG_AR is not set +# CONFIG_BUNZIP2 is not set +# CONFIG_CPIO is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +CONFIG_GUNZIP=y +# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set +# CONFIG_GZIP is not set +# CONFIG_RPM2CPIO is not set +# CONFIG_RPM is not set +# CONFIG_TAR is not set +# CONFIG_UNCOMPRESS is not set +# CONFIG_UNZIP is not set + +# +# Coreutils +# +# CONFIG_BASENAME is not set +# CONFIG_CAL is not set +CONFIG_CAT=y +# CONFIG_CHGRP is not set +# CONFIG_CHMOD is not set +# CONFIG_CHOWN is not set +CONFIG_CHROOT=y +# CONFIG_CMP is not set +CONFIG_CP=y +# CONFIG_CUT is not set +# CONFIG_DATE is not set +# CONFIG_DD is not set +# CONFIG_DF is not set +# CONFIG_DIRNAME is not set +# CONFIG_DOS2UNIX is not set +# CONFIG_DU is not set +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +# CONFIG_ENV is not set +# CONFIG_EXPR is not set +# CONFIG_FALSE is not set +# CONFIG_FOLD is not set +# CONFIG_HEAD is not set +# CONFIG_HOSTID is not set +# CONFIG_ID is not set +# CONFIG_INSTALL is not set +# CONFIG_LENGTH is not set +CONFIG_LN=y +# CONFIG_LOGNAME is not set +# CONFIG_LS is not set +# CONFIG_MD5SUM is not set +# CONFIG_MKDIR is not set +# CONFIG_MKFIFO is not set +# CONFIG_MKNOD is not set +# CONFIG_MV is not set +# CONFIG_OD is not set +# CONFIG_PRINTF is not set +# CONFIG_PWD is not set +# CONFIG_REALPATH is not set +CONFIG_RM=y +# CONFIG_RMDIR is not set +# CONFIG_SHA1SUM is not set +CONFIG_SLEEP=y +# CONFIG_FEATURE_FANCY_SLEEP is not set +# CONFIG_SORT is not set +# CONFIG_STTY is not set +# CONFIG_SYNC is not set +# CONFIG_TAIL is not set +# CONFIG_TEE is not set +CONFIG_TEST=y + +# +# test (forced enabled for use with shell) +# +# CONFIG_TOUCH is not set +# CONFIG_TR is not set +# CONFIG_TRUE is not set +# CONFIG_TTY is not set +# CONFIG_UNAME is not set +# CONFIG_UNIQ is not set +# CONFIG_USLEEP is not set +# CONFIG_UUDECODE is not set +# CONFIG_UUENCODE is not set +# CONFIG_WATCH is not set +# CONFIG_WC is not set +# CONFIG_WHO is not set +# CONFIG_WHOAMI is not set +# CONFIG_YES is not set + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_CLEAR is not set +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_LOADACM is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +# CONFIG_RESET is not set +# CONFIG_SETKEYCODES is not set + +# +# Debian Utilities +# +# CONFIG_MKTEMP is not set +# CONFIG_PIPE_PROGRESS is not set +# CONFIG_READLINK is not set +# CONFIG_RUN_PARTS is not set +# CONFIG_START_STOP_DAEMON is not set +# CONFIG_WHICH is not set + +# +# Editors +# +# CONFIG_AWK is not set +# CONFIG_PATCH is not set +# CONFIG_SED is not set +# CONFIG_VI is not set + +# +# Finding Utilities +# +CONFIG_FIND=y +# CONFIG_FEATURE_FIND_MTIME is not set +# CONFIG_FEATURE_FIND_PERM is not set +# CONFIG_FEATURE_FIND_TYPE is not set +# CONFIG_FEATURE_FIND_XDEV is not set +# CONFIG_FEATURE_FIND_NEWER is not set +# CONFIG_FEATURE_FIND_INUM is not set +CONFIG_GREP=y +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_CONTEXT is not set +# CONFIG_XARGS is not set + +# +# Init Utilities +# +# CONFIG_INIT is not set +# CONFIG_HALT is not set +# CONFIG_POWEROFF is not set +# CONFIG_REBOOT is not set +# CONFIG_MINIT is not set +# CONFIG_MESG is not set + +# +# Login/Password Management Utilities +# +CONFIG_USE_BB_PWD_GRP=y +# CONFIG_ADDGROUP is not set +# CONFIG_DELGROUP is not set +# CONFIG_ADDUSER is not set +# CONFIG_DELUSER is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PASSWD is not set +# CONFIG_SU is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Miscellaneous Utilities +# +# CONFIG_ADJTIMEX is not set +# CONFIG_CROND is not set +# CONFIG_CRONTAB is not set +# CONFIG_DC is not set +# CONFIG_DEVFSD is not set +# CONFIG_LAST is not set +# CONFIG_HDPARM is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_MT is not set +# CONFIG_STRINGS is not set +# CONFIG_TIME is not set +# CONFIG_WATCHDOG is not set + +# +# Linux Module Utilities +# +CONFIG_MODUTILS_OBJ=y +# CONFIG_DEPMOD is not set +CONFIG_INSMOD=y +# CONFIG_FEATURE_2_2_MODULES is not set +CONFIG_FEATURE_2_4_MODULES=y +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +# CONFIG_LSMOD is not set +CONFIG_MODPROBE=y +# CONFIG_RMMOD is not set +CONFIG_FEATURE_CHECK_TAINTED_MODULE=y + +# +# Networking Utilities +# +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_ARPING is not set +# CONFIG_FTPGET is not set +# CONFIG_FTPPUT is not set +# CONFIG_HOSTNAME is not set +# CONFIG_HTTPD is not set +# CONFIG_IFCONFIG is not set +# CONFIG_IFUPDOWN is not set +# CONFIG_INETD is not set +# CONFIG_IP is not set +# CONFIG_IPCALC is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_NAMEIF is not set +# CONFIG_NC is not set +# CONFIG_NETSTAT is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_PING is not set +# CONFIG_ROUTE is not set +# CONFIG_TELNET is not set +# CONFIG_TELNETD is not set +# CONFIG_TFTP is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_VCONFIG is not set +# CONFIG_WGET is not set + +# +# udhcp Server/Client +# +# CONFIG_UDHCPD is not set +# CONFIG_UDHCPC is not set + +# +# Process Utilities +# +# CONFIG_FREE is not set +# CONFIG_KILL is not set +# CONFIG_PIDOF is not set +# CONFIG_PS is not set +# CONFIG_RENICE is not set +# CONFIG_TOP is not set +# CONFIG_UPTIME is not set + +# +# Another Bourne-like Shell +# +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_LASH is not set +# CONFIG_FEATURE_SH_IS_MSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +CONFIG_ASH=y + +# +# Ash Shell Options +# +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_MATH_SUPPORT is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +CONFIG_ASH_OPTIMIZE_FOR_SIZE=y +# CONFIG_HUSH is not set +# CONFIG_LASH is not set +# CONFIG_MSH is not set + +# +# Bourne Shell Options +# +# CONFIG_FEATURE_COMMAND_EDITING is not set +# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set +# CONFIG_FEATURE_COMMAND_TAB_COMPLETION is not set +# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set +CONFIG_FEATURE_COMMAND_HISTORY=15 +# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set +# CONFIG_FEATURE_SH_FANCY_PROMPT is not set +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set + +# +# System Logging Utilities +# +# CONFIG_SYSLOGD is not set +# CONFIG_LOGGER is not set + +# +# Linux System Utilities +# +# CONFIG_DMESG is not set +# CONFIG_FBSET is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +# CONFIG_FREERAMDISK is not set +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_GETOPT is not set +# CONFIG_HEXDUMP is not set +# CONFIG_HWCLOCK is not set +# CONFIG_LOSETUP is not set +# CONFIG_MKSWAP is not set +# CONFIG_MORE is not set +CONFIG_PIVOT_ROOT=y +# CONFIG_RDATE is not set +# CONFIG_SWAPONOFF is not set +CONFIG_MOUNT=y +# CONFIG_NFSMOUNT is not set +CONFIG_UMOUNT=y +CONFIG_FEATURE_MOUNT_FORCE=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Debugging Options +# +# CONFIG_DEBUG is not set diff --git a/busybox/debian/config-static b/busybox/debian/config-static new file mode 100644 index 000000000..035354683 --- /dev/null +++ b/busybox/debian/config-static @@ -0,0 +1,503 @@ +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y + +# +# General Configuration +# +# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set +CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +CONFIG_FEATURE_VERBOSE_USAGE=y +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_LOCALE_SUPPORT is not set +CONFIG_FEATURE_DEVFS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_SUID is not set +# CONFIG_SELINUX is not set + +# +# Build Options +# +CONFIG_STATIC=y +CONFIG_LFS=y +# USING_CROSS_COMPILER is not set +EXTRA_CFLAGS_OPTIONS="" + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +PREFIX="./_install" + +# +# Archival Utilities +# +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_BUNZIP2=y +CONFIG_CPIO=y +CONFIG_DPKG=y +CONFIG_DPKG_DEB=y +# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set +CONFIG_FEATURE_DEB_TAR_GZ=y +CONFIG_FEATURE_DEB_TAR_BZ2=y +CONFIG_GUNZIP=y +CONFIG_FEATURE_GUNZIP_UNCOMPRESS=y +CONFIG_GZIP=y +CONFIG_RPM2CPIO=y +CONFIG_RPM=y +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_BZIP2=y +CONFIG_FEATURE_TAR_EXCLUDE=y +CONFIG_FEATURE_TAR_GZIP=y +# CONFIG_FEATURE_TAR_COMPRESS is not set +CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +CONFIG_UNCOMPRESS=y +CONFIG_UNZIP=y + +# +# Common options for cpio and tar +# +CONFIG_FEATURE_UNARCHIVE_TAPE=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAL=y +CONFIG_CAT=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_CHROOT=y +CONFIG_CMP=y +CONFIG_CP=y +CONFIG_CUT=y +CONFIG_DATE=y + +# +# date (forced enabled for use with watch) +# +CONFIG_FEATURE_DATE_ISOFMT=y +CONFIG_DD=y +CONFIG_DF=y +CONFIG_DIRNAME=y +CONFIG_DOS2UNIX=y +CONFIG_UNIX2DOS=y +CONFIG_DU=y +CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K=y +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_EXPR=y +CONFIG_FALSE=y +CONFIG_FOLD=y +CONFIG_HEAD=y +CONFIG_FEATURE_FANCY_HEAD=y +CONFIG_HOSTID=y +CONFIG_ID=y +# CONFIG_INSTALL is not set +CONFIG_LENGTH=y +CONFIG_LN=y +CONFIG_LOGNAME=y +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +CONFIG_FEATURE_LS_COLOR=y +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +CONFIG_OD=y +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +CONFIG_SHA1SUM=y +CONFIG_SLEEP=y +CONFIG_FEATURE_FANCY_SLEEP=y +CONFIG_SORT=y +CONFIG_STTY=y +CONFIG_SYNC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TEST=y + +# +# test (forced enabled for use with shell) +# +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +CONFIG_UNIQ=y +CONFIG_USLEEP=y +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WATCH=y +CONFIG_WC=y +CONFIG_WHO=y +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls and more +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum +# +# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +CONFIG_DUMPKMAP=y +CONFIG_LOADACM=y +CONFIG_LOADFONT=y +CONFIG_LOADKMAP=y +CONFIG_OPENVT=y +CONFIG_RESET=y +CONFIG_SETKEYCODES=y + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +# CONFIG_PIPE_PROGRESS is not set +CONFIG_READLINK=y +CONFIG_RUN_PARTS=y +CONFIG_START_STOP_DAEMON=y +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_AWK=y +CONFIG_FEATURE_AWK_MATH=y +CONFIG_PATCH=y +CONFIG_SED=y +CONFIG_VI=y +CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_YANKMARK=y +CONFIG_FEATURE_VI_SEARCH=y +CONFIG_FEATURE_VI_USE_SIGNALS=y +CONFIG_FEATURE_VI_DOT_CMD=y +CONFIG_FEATURE_VI_READONLY=y +CONFIG_FEATURE_VI_SETOPTS=y +CONFIG_FEATURE_VI_SET=y +CONFIG_FEATURE_VI_WIN_RESIZE=y +CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +CONFIG_FEATURE_FIND_NEWER=y +CONFIG_FEATURE_FIND_INUM=y +CONFIG_GREP=y +CONFIG_FEATURE_GREP_EGREP_ALIAS=y +CONFIG_FEATURE_GREP_FGREP_ALIAS=y +CONFIG_FEATURE_GREP_CONTEXT=y +CONFIG_XARGS=y +# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set +# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set +# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set +# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +CONFIG_FEATURE_INITRD=y +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_FEATURE_EXTRA_QUIET is not set +CONFIG_HALT=y +CONFIG_POWEROFF=y +CONFIG_REBOOT=y +CONFIG_MESG=y + +# +# Login/Password Management Utilities +# +CONFIG_USE_BB_PWD_GRP=y +CONFIG_ADDGROUP=y +CONFIG_DELGROUP=y +CONFIG_ADDUSER=y +CONFIG_DELUSER=y +CONFIG_GETTY=y +CONFIG_LOGIN=y +CONFIG_FEATURE_SECURETTY=y +CONFIG_PASSWD=y +CONFIG_SU=y +CONFIG_SULOGIN=y +CONFIG_VLOCK=y + +# +# Common options for adduser, deluser, login, su +# +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_SHADOW=y + +# +# Miscellaneous Utilities +# +CONFIG_ADJTIMEX=y +CONFIG_CROND=y +CONFIG_FEATURE_CROND_CALL_SENDMAIL=y +CONFIG_CRONTAB=y +CONFIG_DC=y +# CONFIG_DEVFSD is not set +CONFIG_LAST=y +# CONFIG_HDPARM is not set +CONFIG_MAKEDEVS=y +CONFIG_MT=y +CONFIG_STRINGS=y +CONFIG_TIME=y +CONFIG_WATCHDOG=y + +# +# Linux Module Utilities +# +# CONFIG_DEPMOD is not set +# CONFIG_INSMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_MODPROBE is not set +# CONFIG_RMMOD is not set + +# +# Networking Utilities +# +CONFIG_FEATURE_IPV6=y +CONFIG_ARPING=y +CONFIG_FTPGET=y +CONFIG_FTPPUT=y +CONFIG_HOSTNAME=y +CONFIG_HTTPD=y +# CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY is not set +CONFIG_FEATURE_HTTPD_BASIC_AUTH=y +# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set +# CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP is not set +# CONFIG_FEATURE_HTTPD_SETUID is not set +# CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES is not set +# CONFIG_FEATURE_HTTPD_CGI is not set +# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +CONFIG_FEATURE_IFCONFIG_SLIP=y +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +CONFIG_IFUPDOWN=y +# CONFIG_FEATURE_IFUPDOWN_IP is not set +CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y +CONFIG_FEATURE_IFUPDOWN_IPV4=y +CONFIG_FEATURE_IFUPDOWN_IPV6=y +CONFIG_FEATURE_IFUPDOWN_IPX=y +CONFIG_FEATURE_IFUPDOWN_MAPPING=y +# CONFIG_INETD is not set +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y + +# +# address (forced enabled for ipaddr) +# +CONFIG_FEATURE_IP_LINK=y + +# +# link (forced enabled for iplink) +# +CONFIG_FEATURE_IP_ROUTE=y + +# +# route (forced enabled for iproute) +# +CONFIG_FEATURE_IP_TUNNEL=y + +# +# tunnel (forced enabled for iptunnel) +# +CONFIG_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_IPADDR=y +CONFIG_IPLINK=y +CONFIG_IPROUTE=y +CONFIG_IPTUNNEL=y +CONFIG_NAMEIF=y +CONFIG_NC=y +CONFIG_NETSTAT=y +CONFIG_NSLOOKUP=y +CONFIG_PING=y +CONFIG_FEATURE_FANCY_PING=y +CONFIG_PING6=y +CONFIG_FEATURE_FANCY_PING6=y +CONFIG_ROUTE=y +CONFIG_TELNET=y +CONFIG_FEATURE_TELNET_TTYPE=y +CONFIG_TELNETD=y +# CONFIG_FEATURE_TELNETD_INETD is not set +CONFIG_TFTP=y +CONFIG_FEATURE_TFTP_GET=y +CONFIG_FEATURE_TFTP_PUT=y +CONFIG_FEATURE_TFTP_BLOCKSIZE=y +CONFIG_FEATURE_TFTP_DEBUG=y +CONFIG_TRACEROUTE=y +CONFIG_FEATURE_TRACEROUTE_VERBOSE=y +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_IP6_LITERAL=y + +# +# udhcp Server/Client +# +CONFIG_UDHCPD=y +CONFIG_UDHCPC=y +CONFIG_DUMPLEASES=y +CONFIG_FEATURE_UDHCP_SYSLOG=y +CONFIG_FEATURE_UDHCP_DEBUG=y + +# +# Process Utilities +# +CONFIG_FREE=y +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_PIDOF=y +CONFIG_PS=y +CONFIG_RENICE=y +CONFIG_TOP=y +FEATURE_CPU_USAGE_PERCENTAGE=y +CONFIG_UPTIME=y + +# +# Another Bourne-like Shell +# +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_LASH is not set +# CONFIG_FEATURE_SH_IS_MSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +CONFIG_ASH=y + +# +# Ash Shell Options +# +CONFIG_ASH_JOB_CONTROL=y +CONFIG_ASH_ALIAS=y +CONFIG_ASH_MATH_SUPPORT=y +CONFIG_ASH_GETOPTS=y +CONFIG_ASH_CMDCMD=y +CONFIG_ASH_MAIL=y +CONFIG_ASH_OPTIMIZE_FOR_SIZE=y +# CONFIG_HUSH is not set +# CONFIG_LASH is not set +# CONFIG_MSH is not set + +# +# Bourne Shell Options +# +CONFIG_FEATURE_COMMAND_EDITING=y +CONFIG_FEATURE_COMMAND_SAVEHISTORY=y +CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y +CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION=y +CONFIG_FEATURE_COMMAND_HISTORY=15 +CONFIG_FEATURE_SH_STANDALONE_SHELL=y +CONFIG_FEATURE_SH_FANCY_PROMPT=y +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +# CONFIG_FEATURE_ROTATE_LOGFILE is not set +CONFIG_FEATURE_REMOTE_LOG=y +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_LOGREAD=y +CONFIG_KLOGD=y +CONFIG_LOGGER=y + +# +# Linux System Utilities +# +CONFIG_DMESG=y +CONFIG_FBSET=y +CONFIG_FEATURE_FBSET_FANCY=y +CONFIG_FEATURE_FBSET_READMODE=y +CONFIG_FDFLUSH=y +# CONFIG_FDFORMAT is not set +CONFIG_FDISK=y +CONFIG_FEATURE_FDISK_WRITABLE=y +CONFIG_FEATURE_AIX_LABEL=y +CONFIG_FEATURE_SGI_LABEL=y +CONFIG_FEATURE_SUN_LABEL=y +# CONFIG_FEATURE_OSF_LABEL is not set +CONFIG_FEATURE_FDISK_ADVANCED=y +CONFIG_FREERAMDISK=y +CONFIG_FSCK_MINIX=y +CONFIG_MKFS_MINIX=y + +# +# Minix filesystem support +# +CONFIG_FEATURE_MINIX2=y +CONFIG_GETOPT=y +CONFIG_HEXDUMP=y +CONFIG_HWCLOCK=y +CONFIG_FEATURE_HWCLOCK_LONGOPTIONS=y +CONFIG_LOSETUP=y +CONFIG_MKSWAP=y +CONFIG_MORE=y +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_PIVOT_ROOT=y +CONFIG_RDATE=y +CONFIG_SWAPONOFF=y +CONFIG_MOUNT=y +CONFIG_NFSMOUNT=y +CONFIG_UMOUNT=y +CONFIG_FEATURE_MOUNT_FORCE=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Debugging Options +# +# CONFIG_DEBUG is not set diff --git a/busybox/debian/config-udeb b/busybox/debian/config-udeb new file mode 100644 index 000000000..7359abcfe --- /dev/null +++ b/busybox/debian/config-udeb @@ -0,0 +1,410 @@ +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y + +# +# General Configuration +# +# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set +CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +# CONFIG_FEATURE_VERBOSE_USAGE is not set +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_LOCALE_SUPPORT is not set +CONFIG_FEATURE_DEVFS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_SUID is not set +# CONFIG_SELINUX is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +CONFIG_LFS=y +# USING_CROSS_COMPILER is not set +EXTRA_CFLAGS_OPTIONS="" + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +PREFIX="./_install" + +# +# Archival Utilities +# +CONFIG_AR=y +# CONFIG_FEATURE_AR_LONG_FILENAMES is not set +# CONFIG_BUNZIP2 is not set +# CONFIG_CPIO is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +CONFIG_GUNZIP=y +# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set +# CONFIG_GZIP is not set +# CONFIG_RPM2CPIO is not set +# CONFIG_RPM is not set +CONFIG_TAR=y +# CONFIG_FEATURE_TAR_CREATE is not set +# CONFIG_FEATURE_TAR_BZIP2 is not set +# CONFIG_FEATURE_TAR_EXCLUDE is not set +CONFIG_FEATURE_TAR_GZIP=y +# CONFIG_FEATURE_TAR_COMPRESS is not set +CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +# CONFIG_UNCOMPRESS is not set +# CONFIG_UNZIP is not set + +# +# Common options for cpio and tar +# +# CONFIG_FEATURE_UNARCHIVE_TAPE is not set + +# +# Coreutils +# +CONFIG_BASENAME=y +# CONFIG_CAL is not set +CONFIG_CAT=y +# CONFIG_CHGRP is not set +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_CHROOT=y +# CONFIG_CMP is not set +CONFIG_CP=y +CONFIG_CUT=y +# CONFIG_DATE is not set +# CONFIG_DD is not set +CONFIG_DF=y +CONFIG_DIRNAME=y +# CONFIG_DOS2UNIX is not set +# CONFIG_DU is not set +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_EXPR=y +CONFIG_FALSE=y +# CONFIG_FOLD is not set +CONFIG_HEAD=y +# CONFIG_FEATURE_FANCY_HEAD is not set +# CONFIG_HOSTID is not set +CONFIG_ID=y +# CONFIG_INSTALL is not set +# CONFIG_LENGTH is not set +CONFIG_LN=y +# CONFIG_LOGNAME is not set +CONFIG_LS=y +# CONFIG_FEATURE_LS_FILETYPES is not set +CONFIG_FEATURE_LS_FOLLOWLINKS=y +# CONFIG_FEATURE_LS_RECURSIVE is not set +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +# CONFIG_FEATURE_LS_COLOR is not set +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +# CONFIG_MKFIFO is not set +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_OD is not set +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +# CONFIG_SHA1SUM is not set +# CONFIG_SLEEP is not set +CONFIG_SORT=y +# CONFIG_STTY is not set +CONFIG_SYNC=y +CONFIG_TAIL=y +# CONFIG_FEATURE_FANCY_TAIL is not set +# CONFIG_TEE is not set +CONFIG_TEST=y + +# +# test (forced enabled for use with shell) +# +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_TRUE=y +# CONFIG_TTY is not set +CONFIG_UNAME=y +CONFIG_UNIQ=y +# CONFIG_USLEEP is not set +# CONFIG_UUDECODE is not set +# CONFIG_UUENCODE is not set +# CONFIG_WATCH is not set +CONFIG_WC=y +# CONFIG_WHO is not set +# CONFIG_WHOAMI is not set +# CONFIG_YES is not set + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls and more +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum +# +# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_CLEAR is not set +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_LOADACM is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +# CONFIG_RESET is not set +# CONFIG_SETKEYCODES is not set + +# +# Debian Utilities +# +# CONFIG_MKTEMP is not set +# CONFIG_PIPE_PROGRESS is not set +CONFIG_READLINK=y +# CONFIG_RUN_PARTS is not set +# CONFIG_START_STOP_DAEMON is not set +# CONFIG_WHICH is not set + +# +# Editors +# +# CONFIG_AWK is not set +# CONFIG_PATCH is not set +CONFIG_SED=y +# CONFIG_VI is not set + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +CONFIG_FEATURE_FIND_NEWER=y +CONFIG_FEATURE_FIND_INUM=y +CONFIG_GREP=y +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_CONTEXT is not set +# CONFIG_XARGS is not set + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +# CONFIG_FEATURE_INITRD is not set +# CONFIG_FEATURE_INIT_COREDUMPS is not set +CONFIG_FEATURE_EXTRA_QUIET=y +CONFIG_HALT=y +# CONFIG_POWEROFF is not set +CONFIG_REBOOT=y +# CONFIG_MESG is not set + +# +# Login/Password Management Utilities +# +CONFIG_USE_BB_PWD_GRP=y +# CONFIG_ADDGROUP is not set +# CONFIG_DELGROUP is not set +# CONFIG_ADDUSER is not set +# CONFIG_DELUSER is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PASSWD is not set +# CONFIG_SU is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Miscellaneous Utilities +# +# CONFIG_ADJTIMEX is not set +# CONFIG_CROND is not set +# CONFIG_CRONTAB is not set +# CONFIG_DC is not set +# CONFIG_DEVFSD is not set +# CONFIG_LAST is not set +# CONFIG_HDPARM is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_MT is not set +# CONFIG_STRINGS is not set +# CONFIG_TIME is not set +# CONFIG_WATCHDOG is not set + +# +# Linux Module Utilities +# +# CONFIG_DEPMOD is not set +# CONFIG_INSMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_MODPROBE is not set +# CONFIG_RMMOD is not set + +# +# Networking Utilities +# +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_ARPING is not set +# CONFIG_FTPGET is not set +# CONFIG_FTPPUT is not set +# CONFIG_HOSTNAME is not set +# CONFIG_HTTPD is not set +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set +# CONFIG_FEATURE_IFCONFIG_HW is not set +# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set +# CONFIG_IFUPDOWN is not set +# CONFIG_INETD is not set +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_IPCALC is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_NAMEIF is not set +# CONFIG_NC is not set +# CONFIG_NETSTAT is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_PING is not set +CONFIG_ROUTE=y +# CONFIG_TELNET is not set +# CONFIG_TELNETD is not set +# CONFIG_TFTP is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_IP6_LITERAL=y + +# +# udhcp Server/Client +# +# CONFIG_UDHCPD is not set +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCP_SYSLOG=y +# CONFIG_FEATURE_UDHCP_DEBUG is not set + +# +# Process Utilities +# +CONFIG_FREE=y +CONFIG_KILL=y +# CONFIG_KILLALL is not set +CONFIG_PIDOF=y +CONFIG_PS=y +# CONFIG_RENICE is not set +# CONFIG_TOP is not set +# CONFIG_UPTIME is not set + +# +# Another Bourne-like Shell +# +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_LASH is not set +# CONFIG_FEATURE_SH_IS_MSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +CONFIG_ASH=y + +# +# Ash Shell Options +# +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +CONFIG_ASH_MATH_SUPPORT=y +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +CONFIG_ASH_OPTIMIZE_FOR_SIZE=y +# CONFIG_HUSH is not set +# CONFIG_LASH is not set +# CONFIG_MSH is not set + +# +# Bourne Shell Options +# +CONFIG_FEATURE_COMMAND_EDITING=y +# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set +CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y +# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set +CONFIG_FEATURE_COMMAND_HISTORY=15 +CONFIG_FEATURE_SH_STANDALONE_SHELL=y +CONFIG_FEATURE_SH_FANCY_PROMPT=y +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +# CONFIG_FEATURE_ROTATE_LOGFILE is not set +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_KLOGD=y +CONFIG_LOGGER=y + +# +# Linux System Utilities +# +CONFIG_DMESG=y +# CONFIG_FBSET is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +CONFIG_FREERAMDISK=y +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_GETOPT is not set +# CONFIG_HEXDUMP is not set +# CONFIG_HWCLOCK is not set +# CONFIG_LOSETUP is not set +CONFIG_MKSWAP=y +CONFIG_MORE=y +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_PIVOT_ROOT=y +# CONFIG_RDATE is not set +CONFIG_SWAPONOFF=y +CONFIG_MOUNT=y +# CONFIG_NFSMOUNT is not set +CONFIG_UMOUNT=y +CONFIG_FEATURE_MOUNT_FORCE=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Debugging Options +# +# CONFIG_DEBUG is not set diff --git a/busybox/debian/config-udeb-linux b/busybox/debian/config-udeb-linux new file mode 100644 index 000000000..b33fb22b0 --- /dev/null +++ b/busybox/debian/config-udeb-linux @@ -0,0 +1,419 @@ +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y + +# +# General Configuration +# +# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set +CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +# CONFIG_FEATURE_VERBOSE_USAGE is not set +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_LOCALE_SUPPORT is not set +CONFIG_FEATURE_DEVFS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_SUID is not set +# CONFIG_SELINUX is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +CONFIG_LFS=y +# USING_CROSS_COMPILER is not set +EXTRA_CFLAGS_OPTIONS="" + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +PREFIX="./_install" + +# +# Archival Utilities +# +CONFIG_AR=y +# CONFIG_FEATURE_AR_LONG_FILENAMES is not set +# CONFIG_BUNZIP2 is not set +# CONFIG_CPIO is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +CONFIG_GUNZIP=y +# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set +# CONFIG_GZIP is not set +# CONFIG_RPM2CPIO is not set +# CONFIG_RPM is not set +CONFIG_TAR=y +# CONFIG_FEATURE_TAR_CREATE is not set +# CONFIG_FEATURE_TAR_BZIP2 is not set +# CONFIG_FEATURE_TAR_EXCLUDE is not set +CONFIG_FEATURE_TAR_GZIP=y +# CONFIG_FEATURE_TAR_COMPRESS is not set +CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +# CONFIG_UNCOMPRESS is not set +# CONFIG_UNZIP is not set + +# +# Common options for cpio and tar +# +# CONFIG_FEATURE_UNARCHIVE_TAPE is not set + +# +# Coreutils +# +CONFIG_BASENAME=y +# CONFIG_CAL is not set +CONFIG_CAT=y +# CONFIG_CHGRP is not set +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_CHROOT=y +# CONFIG_CMP is not set +CONFIG_CP=y +CONFIG_CUT=y +# CONFIG_DATE is not set +# CONFIG_DD is not set +CONFIG_DF=y +CONFIG_DIRNAME=y +# CONFIG_DOS2UNIX is not set +# CONFIG_DU is not set +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_EXPR=y +CONFIG_FALSE=y +# CONFIG_FOLD is not set +CONFIG_HEAD=y +# CONFIG_FEATURE_FANCY_HEAD is not set +# CONFIG_HOSTID is not set +CONFIG_ID=y +# CONFIG_INSTALL is not set +# CONFIG_LENGTH is not set +CONFIG_LN=y +# CONFIG_LOGNAME is not set +CONFIG_LS=y +# CONFIG_FEATURE_LS_FILETYPES is not set +CONFIG_FEATURE_LS_FOLLOWLINKS=y +# CONFIG_FEATURE_LS_RECURSIVE is not set +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +# CONFIG_FEATURE_LS_COLOR is not set +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +# CONFIG_MKFIFO is not set +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_OD is not set +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +# CONFIG_SHA1SUM is not set +# CONFIG_SLEEP is not set +CONFIG_SORT=y +# CONFIG_STTY is not set +CONFIG_SYNC=y +CONFIG_TAIL=y +# CONFIG_FEATURE_FANCY_TAIL is not set +# CONFIG_TEE is not set +CONFIG_TEST=y + +# +# test (forced enabled for use with shell) +# +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_TRUE=y +# CONFIG_TTY is not set +CONFIG_UNAME=y +CONFIG_UNIQ=y +# CONFIG_USLEEP is not set +# CONFIG_UUDECODE is not set +# CONFIG_UUENCODE is not set +# CONFIG_WATCH is not set +CONFIG_WC=y +# CONFIG_WHO is not set +# CONFIG_WHOAMI is not set +# CONFIG_YES is not set + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls and more +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum +# +# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_CLEAR is not set +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_LOADACM is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +# CONFIG_RESET is not set +# CONFIG_SETKEYCODES is not set + +# +# Debian Utilities +# +# CONFIG_MKTEMP is not set +# CONFIG_PIPE_PROGRESS is not set +CONFIG_READLINK=y +# CONFIG_RUN_PARTS is not set +# CONFIG_START_STOP_DAEMON is not set +# CONFIG_WHICH is not set + +# +# Editors +# +# CONFIG_AWK is not set +# CONFIG_PATCH is not set +CONFIG_SED=y +# CONFIG_VI is not set + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +CONFIG_FEATURE_FIND_NEWER=y +CONFIG_FEATURE_FIND_INUM=y +CONFIG_GREP=y +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_CONTEXT is not set +# CONFIG_XARGS is not set + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +# CONFIG_FEATURE_INITRD is not set +# CONFIG_FEATURE_INIT_COREDUMPS is not set +CONFIG_FEATURE_EXTRA_QUIET=y +CONFIG_HALT=y +# CONFIG_POWEROFF is not set +CONFIG_REBOOT=y +# CONFIG_MESG is not set + +# +# Login/Password Management Utilities +# +CONFIG_USE_BB_PWD_GRP=y +# CONFIG_ADDGROUP is not set +# CONFIG_DELGROUP is not set +# CONFIG_ADDUSER is not set +# CONFIG_DELUSER is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PASSWD is not set +# CONFIG_SU is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Miscellaneous Utilities +# +# CONFIG_ADJTIMEX is not set +# CONFIG_CROND is not set +# CONFIG_CRONTAB is not set +# CONFIG_DC is not set +# CONFIG_DEVFSD is not set +# CONFIG_LAST is not set +# CONFIG_HDPARM is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_MT is not set +# CONFIG_STRINGS is not set +# CONFIG_TIME is not set +# CONFIG_WATCHDOG is not set + +# +# Linux Module Utilities +# +CONFIG_MODUTILS_OBJ=y +CONFIG_DEPMOD=y +CONFIG_INSMOD=y +# CONFIG_FEATURE_2_2_MODULES is not set +CONFIG_FEATURE_2_4_MODULES=y +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +CONFIG_LSMOD=y +CONFIG_FEATURE_QUERY_MODULE_INTERFACE=y +CONFIG_MODPROBE=y +# CONFIG_RMMOD is not set +CONFIG_FEATURE_CHECK_TAINTED_MODULE=y + +# +# Networking Utilities +# +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_ARPING is not set +# CONFIG_FTPGET is not set +# CONFIG_FTPPUT is not set +# CONFIG_HOSTNAME is not set +# CONFIG_HTTPD is not set +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set +# CONFIG_FEATURE_IFCONFIG_HW is not set +# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set +# CONFIG_IFUPDOWN is not set +# CONFIG_INETD is not set +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_IPCALC is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_NAMEIF is not set +# CONFIG_NC is not set +# CONFIG_NETSTAT is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_PING is not set +CONFIG_ROUTE=y +# CONFIG_TELNET is not set +# CONFIG_TELNETD is not set +# CONFIG_TFTP is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_IP6_LITERAL=y + +# +# udhcp Server/Client +# +# CONFIG_UDHCPD is not set +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCP_SYSLOG=y +# CONFIG_FEATURE_UDHCP_DEBUG is not set + +# +# Process Utilities +# +CONFIG_FREE=y +CONFIG_KILL=y +# CONFIG_KILLALL is not set +CONFIG_PIDOF=y +CONFIG_PS=y +# CONFIG_RENICE is not set +# CONFIG_TOP is not set +# CONFIG_UPTIME is not set + +# +# Another Bourne-like Shell +# +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_LASH is not set +# CONFIG_FEATURE_SH_IS_MSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +CONFIG_ASH=y + +# +# Ash Shell Options +# +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +CONFIG_ASH_MATH_SUPPORT=y +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +CONFIG_ASH_OPTIMIZE_FOR_SIZE=y +# CONFIG_HUSH is not set +# CONFIG_LASH is not set +# CONFIG_MSH is not set + +# +# Bourne Shell Options +# +CONFIG_FEATURE_COMMAND_EDITING=y +# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set +CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y +# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set +CONFIG_FEATURE_COMMAND_HISTORY=15 +CONFIG_FEATURE_SH_STANDALONE_SHELL=y +CONFIG_FEATURE_SH_FANCY_PROMPT=y +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +# CONFIG_FEATURE_ROTATE_LOGFILE is not set +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_KLOGD=y +CONFIG_LOGGER=y + +# +# Linux System Utilities +# +CONFIG_DMESG=y +# CONFIG_FBSET is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +CONFIG_FREERAMDISK=y +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_GETOPT is not set +# CONFIG_HEXDUMP is not set +# CONFIG_HWCLOCK is not set +# CONFIG_LOSETUP is not set +CONFIG_MKSWAP=y +CONFIG_MORE=y +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_PIVOT_ROOT=y +# CONFIG_RDATE is not set +CONFIG_SWAPONOFF=y +CONFIG_MOUNT=y +# CONFIG_NFSMOUNT is not set +CONFIG_UMOUNT=y +CONFIG_FEATURE_MOUNT_FORCE=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Debugging Options +# +# CONFIG_DEBUG is not set diff --git a/busybox/debian/control b/busybox/debian/control new file mode 100644 index 000000000..391a02dcf --- /dev/null +++ b/busybox/debian/control @@ -0,0 +1,88 @@ +Source: busybox-cvs +Priority: optional +Maintainer: Debian Install System Team +Uploaders: Erik Andersen , Bastian Blank , Tollef Fog Heen , Glenn McGrath +Build-Depends: debhelper (>= 4.0.0) +Standards-Version: 3.6.0 + +Package: busybox-cvs +Architecture: any +Depends: ${shlibs:Depends} +Conflicts: busybox-cvs-static, busybox, busybox-static +Replaces: busybox-cvs-static, busybox, busybox-static +Section: utils +Description: Tiny utilities for small and embedded systems + BusyBox combines tiny versions of many common UNIX utilities into a single + small executable. It provides minimalist replacements for the most common + utilities you would usually find on your desktop system (i.e., ls, cp, mv, + mount, tar, etc.). The utilities in BusyBox generally have fewer options than + their full-featured GNU cousins; however, the options that are included + provide the expected functionality and behave very much like their GNU + counterparts. + . + This package installs the BusyBox binary but does not install symlinks + for any of the supported utilities. You can use /bin/busybox --install + to install BusyBox to the current directory (you do not want to do this + in / on your Debian system!). + +Package: busybox-cvs-static +Architecture: any +Depends: ${shlibs:Depends} +Conflicts: busybox-cvs, busybox-static, busybox +Replaces: busybox-cvs, busybox-static, busybox +Section: shells +Description: Standalone rescue shell with tons of builtin utilities + BusyBox combines tiny versions of many common UNIX utilities into a single + small executable. It provides minimalist replacements for the most common + utilities you would usually find on your desktop system (i.e., ls, cp, mv, + mount, tar, etc.). The utilities in BusyBox generally have fewer options than + their full-featured GNU cousins; however, the options that are included + provide the expected functionality and behave very much like their GNU + counterparts. + . + BusyBox-static provides you with a statically linked simple stand alone shell + that provides all the utilities available in BusyBox. This package is + intended to be used as a rescue shell, in the event that you screw up your + system. Invoke "busybox sh" and you have a standalone shell ready to save + your system from certain destruction. Invoke "busybox", and it will list the + available builtin commands. + +Package: busybox-cvs-udeb +Architecture: any +Depends: ${shlibs:Depends} +Section: debian-installer +Conflicts: busybox-udeb +Enhances: busybox-cvs-floppy-udeb +Priority: extra +Description: Tiny utilities for the debian-installer + BusyBox combines tiny versions of many common UNIX utilities into a single + small executable. It provides minimalist replacements for the most common + utilities you would usually find on your desktop system (i.e., ls, cp, mv, + mount, tar, etc.). The utilities in BusyBox generally have fewer options than + their full-featured GNU cousins; however, the options that are included + provide the expected functionality and behave very much like their GNU + counterparts. + . + busybox-udeb is used by the debian-installer, so unless you are working + on the debian-installer, this package is not for you. Installing this + on your Debian system is a very, very bad idea. You have been warned. + +Package: busybox-cvs-floppy-udeb +Architecture: i386 +Depends: ${shlibs:Depends} +Section: debian-installer +Conflicts: busybox-udeb +Priority: extra +Description: Tiny utilities for the debian-installer floppy images + BusyBox combines tiny versions of many common UNIX utilities into a single + small executable. It provides minimalist replacements for the most common + utilities you would usually find on your desktop system (i.e., ls, cp, mv, + mount, tar, etc.). The utilities in BusyBox generally have fewer options than + their full-featured GNU cousins; however, the options that are included + provide the expected functionality and behave very much like their GNU + counterparts. + . + busybox-udeb is used by the debian-installer, so unless you are working + on the debian-installer, this package is not for you. Installing this + on your Debian system is a very, very bad idea. You have been warned. + diff --git a/busybox/debian/control-extract b/busybox/debian/control-extract new file mode 100644 index 000000000..ed65f2986 --- /dev/null +++ b/busybox/debian/control-extract @@ -0,0 +1,2 @@ +#!/bin/sh +awk "BEGIN { i = 0; } { if (/^Package: $2/) i = 1; if (/^\s*\$/) i = 0; if (/$1:/ && i) print }" < debian/control | sed -e "s,^$1: ,," diff --git a/busybox/debian/copyright b/busybox/debian/copyright new file mode 100644 index 000000000..cf9f3681c --- /dev/null +++ b/busybox/debian/copyright @@ -0,0 +1,24 @@ +This package was debianized by Erik Andersen on +Sun, 18 Jun 2000 23:31:02 -0600 + +It was downloaded from ftp://ftp.busybox.net/busybox + +Copyright: + + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 dated June, 1991. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + +On Debian GNU/Linux systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. + diff --git a/busybox/debian/rules b/busybox/debian/rules new file mode 100755 index 000000000..e77b4ef96 --- /dev/null +++ b/busybox/debian/rules @@ -0,0 +1,196 @@ +#!/usr/bin/make -f + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) +DEB_HOST_GNU_CPU ?= $(shell dpkg-architecture -qDEB_HOST_GNU_CPU) +DEB_HOST_GNU_SYSTEM ?= $(shell dpkg-architecture -qDEB_HOST_GNU_SYSTEM) + +VERSION = $(shell dpkg-parsechangelog | grep ^Version: | cut -d ' ' -f 2) + +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) + CONFIG_DEBUG = true +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) +endif + +PACKAGE_PREFIX = busybox-cvs + +ARCH_FLOPPY_UDEB = $(shell sh debian/control-extract Architecture $(PACKAGE_PREFIX)-floppy-udeb) + +PACKAGES_DEB = $(PACKAGE_PREFIX) $(PACKAGE_PREFIX)-static +PACKAGES_UDEB = $(PACKAGE_PREFIX)-udeb +ifneq ($(filter $(DEB_HOST_ARCH),$(ARCH_FLOPPY_UDEB)),) +PACKAGES_UDEB += $(PACKAGE_PREFIX)-floppy-udeb +endif +PACKAGES = $(PACKAGES_DEB) $(PACKAGES_UDEB) + +DEBHELPER_PACKAGES_DEB = $(patsubst %,-p%,$(PACKAGES_DEB)) +DEBHELPER_PACKAGES_UDEB = $(patsubst %,-p%,$(PACKAGES_UDEB)) + +CONFIG = $(firstword $(wildcard ./debian/config-$(1)-$(DEB_HOST_GNU_SYSTEM)-$(DEB_HOST_GNU_CPU) ./debian/config-$(1)-$(DEB_HOST_GNU_SYSTEM) ./debian/config-$(1))) +CONFIG_DEB = $(call CONFIG,deb) +CONFIG_STATIC = $(call CONFIG,static) +CONFIG_UDEB = $(call CONFIG,udeb) +CONFIG_FLOPPY_UDEB = $(call CONFIG,floppy-udeb) + +configure: configure-stamp +configure-stamp: + sh ./configure + + touch $@ + +build-arch: build-arch-deb-all build-arch-udeb-all +build-arch-deb-all: build-arch-deb build-arch-static build-arch-doc +build-arch-udeb-all: build-arch-udeb build-arch-floppy-udeb + +build-arch-deb: build-arch-deb-stamp +build-arch-deb-stamp: configure-stamp + $(MAKE) clean + + cp $(CONFIG_DEB) .config + + $(MAKE) dep + $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG) + + install -d install-$(PACKAGE_PREFIX)/bin + install busybox install-$(PACKAGE_PREFIX)/bin/busybox + + touch $@ + +build-arch-static: build-arch-static-stamp +build-arch-static-stamp: configure-stamp + $(MAKE) clean + + cp $(CONFIG_STATIC) .config + + $(MAKE) dep + $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG) + + install -d install-$(PACKAGE_PREFIX)-static/bin + install busybox install-$(PACKAGE_PREFIX)-static/bin/busybox + + touch $@ + +build-arch-udeb: build-arch-udeb-stamp +build-arch-udeb-stamp: configure-stamp + $(MAKE) clean + + cp $(CONFIG_UDEB) .config + + $(MAKE) dep + $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG) + + $(MAKE) PREFIX=$(CURDIR)/install-$(PACKAGE_PREFIX)-udeb install + # Remove init link, but init support is still compiled in to be + # used. + rm -f $(CURDIR)/install-$(PACKAGE_PREFIX)-udeb/sbin/init + + touch $@ + +build-arch-floppy-udeb: build-arch-floppy-udeb-stamp +build-arch-floppy-udeb-stamp: configure-stamp +ifneq ($(filter $(DEB_HOST_ARCH),$(ARCH_FLOPPY_UDEB)),) + $(MAKE) clean + + cp $(CONFIG_FLOPPY_UDEB) .config + + $(MAKE) dep + $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG) + + $(MAKE) PREFIX=$(CURDIR)/install-$(PACKAGE_PREFIX)-floppy-udeb install +endif + + touch $@ + +build-arch-doc: build-arch-doc-stamp +build-arch-doc-stamp: configure-stamp + $(MAKE) docs/BusyBox.1 + + cp docs/BusyBox.1 busybox.1 + + touch $@ + +build: build-arch + +clean: + dh_testdir + dh_testroot + rm -f build-*-stamp configure-stamp debian/files~ + + -$(MAKE) distclean + -rm -rf busybox-deb busybox-static install* busybox.1 + + dh_clean + +install-deb: build-arch-deb-all + dh_testdir + dh_testroot + dh_clean -k $(DEBHELPER_PACKAGES_DEB) + dh_installdirs $(DEBHELPER_PACKAGES_DEB) + + for i in $(PACKAGES_DEB); do \ + ( \ + cd install-$$i; \ + find -type d -exec install -d $(CURDIR)/debian/$$i/{} \;; \ + find \( -type f -o -type l \) -exec cp -a {} $(CURDIR)/debian/$$i/{} \;; \ + ); \ + done + + install -m644 debian/$(PACKAGE_PREFIX)-static.override \ + debian/$(PACKAGE_PREFIX)-static/usr/share/lintian/overrides/$(PACKAGE_PREFIX)-static + +install-udeb: build-arch-udeb-all + dh_testdir + dh_testroot + dh_clean -k $(DEBHELPER_PACKAGES_UDEB) + dh_installdirs $(DEBHELPER_PACKAGES_UDEB) + + for i in $(PACKAGES_UDEB); do \ + ( \ + cd install-$$i; \ + find -type d -exec install -d $(CURDIR)/debian/$$i/{} \;; \ + find \( -type f -o -type l \) -exec cp -a {} $(CURDIR)/debian/$$i/{} \;; \ + ); \ + done + +binary-arch: binary-arch-deb binary-arch-udeb + +# Build architecture-dependent files here. +binary-arch-deb: build-arch-deb-all install-deb + dh_testdir + dh_testroot + dh_installdocs $(DEBHELPER_PACKAGES_DEB) + dh_installman $(DEBHELPER_PACKAGES_DEB) + dh_installchangelogs Changelog $(DEBHELPER_PACKAGES_DEB) + dh_strip $(DEBHELPER_PACKAGES_DEB) + dh_link $(DEBHELPER_PACKAGES_DEB) + dh_compress $(DEBHELPER_PACKAGES_DEB) + dh_fixperms $(DEBHELPER_PACKAGES_DEB) + dh_installdeb $(DEBHELPER_PACKAGES_DEB) + dh_shlibdeps $(DEBHELPER_PACKAGES_DEB) + dh_gencontrol $(DEBHELPER_PACKAGES_DEB) + dh_md5sums $(DEBHELPER_PACKAGES_DEB) + dh_builddeb $(DEBHELPER_PACKAGES_DEB) + +# Build architecture-dependent files here. +binary-arch-udeb: build-arch-udeb-all install-udeb + dh_testdir + dh_testroot + dh_strip $(DEBHELPER_PACKAGES_UDEB) + dh_link $(DEBHELPER_PACKAGES_UDEB) + dh_compress $(DEBHELPER_PACKAGES_UDEB) + dh_fixperms $(DEBHELPER_PACKAGES_UDEB) + dh_installdeb $(DEBHELPER_PACKAGES_UDEB) + dh_shlibdeps $(DEBHELPER_PACKAGES_UDEB) + dh_gencontrol $(DEBHELPER_PACKAGES_DEB) + + $(foreach PACKAGE, $(PACKAGES_UDEB), \ + dh_gencontrol -p$(PACKAGE) -- -fdebian/files~; \ + dpkg-distaddfile $(PACKAGE)_$(VERSION)_$(DEB_HOST_ARCH).udeb debian-installer extra; \ + dh_builddeb -p$(PACKAGE) --filename=$(PACKAGE)_$(VERSION)_$(DEB_HOST_ARCH).udeb; \ + ) + +binary: binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/busybox/debianutils/Config.in b/busybox/debianutils/Config.in new file mode 100644 index 000000000..7cf7cadb5 --- /dev/null +++ b/busybox/debianutils/Config.in @@ -0,0 +1,58 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Debian Utilities" + +config CONFIG_MKTEMP + bool "mktemp" + default n + help + mktemp is used to create unique temporary files + +config CONFIG_PIPE_PROGRESS + bool "pipe_progress" + default n + help + Display a dot to indicate pipe activity. + +config CONFIG_READLINK + bool "readlink" + default n + help + This program reads a symbolic link and returns the name + of the file it points to + +config CONFIG_RUN_PARTS + bool "run-parts" + default n + help + run-parts is a utility designed to run all the scripts in a directory. + + It is useful to set up a directory like cron.daily, where you need to + execute all the scripts in that directory. + + In this implementation of run-parts some features (such as report mode) + are not implemented. + + Unless you know that run-parts is used in some of your scripts + you can safely say N here. + +config CONFIG_START_STOP_DAEMON + bool "start-stop-daemon" + default y + help + start-stop-daemon is used to control the creation and + termination of system-level processes, usually the ones + started during the startup of the system. + +config CONFIG_WHICH + bool "which" + default n + help + which is used to find programs in your PATH and + print out their pathnames. + +endmenu + diff --git a/busybox/debianutils/Makefile b/busybox/debianutils/Makefile new file mode 100644 index 000000000..10ec1cc58 --- /dev/null +++ b/busybox/debianutils/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/debianutils +DEBIANUTILS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/debianutils/Makefile.in b/busybox/debianutils/Makefile.in new file mode 100644 index 000000000..3a204033e --- /dev/null +++ b/busybox/debianutils/Makefile.in @@ -0,0 +1,41 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +DEBIANUTILS_AR:=debianutils.a +ifndef $(DEBIANUTILS_DIR) +DEBIANUTILS_DIR:=$(top_builddir)/debianutils/ +endif +srcdir=$(top_srcdir)/debianutils + +DEBIANUTILS-y:= +DEBIANUTILS-$(CONFIG_MKTEMP) += mktemp.o +DEBIANUTILS-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o +DEBIANUTILS-$(CONFIG_READLINK) += readlink.o +DEBIANUTILS-$(CONFIG_RUN_PARTS) += run_parts.o +DEBIANUTILS-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o +DEBIANUTILS-$(CONFIG_WHICH) += which.o + +libraries-y+=$(DEBIANUTILS_DIR)$(DEBIANUTILS_AR) + +$(DEBIANUTILS_DIR)$(DEBIANUTILS_AR): $(patsubst %,$(DEBIANUTILS_DIR)%, $(DEBIANUTILS-y)) + $(AR) -ro $@ $(patsubst %,$(DEBIANUTILS_DIR)%, $(DEBIANUTILS-y)) + +$(DEBIANUTILS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/debianutils/mktemp.c b/busybox/debianutils/mktemp.c new file mode 100644 index 000000000..9fdf79bfa --- /dev/null +++ b/busybox/debianutils/mktemp.c @@ -0,0 +1,63 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mktemp implementation for busybox + * + * + * Copyright (C) 2000 by Daniel Jacobowitz + * Written by Daniel Jacobowitz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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) +{ + unsigned char dir_flag = 0; + int opt; + + while ((opt = getopt(argc, argv, "qd")) != -1) { + if (opt == 'd') { + dir_flag = 1; + } + else if (opt != 'q') { + bb_show_usage(); + } + } + + if (optind + 1 != argc) { + bb_show_usage(); + } + + if (dir_flag) { + if (mkdtemp(argv[argc-1]) == NULL) { + return EXIT_FAILURE; + } + } else { + if (mkstemp(argv[argc-1]) < 0) { + return EXIT_FAILURE; + } + } + + (void) puts(argv[argc-1]); + + return EXIT_SUCCESS; +} diff --git a/busybox/debianutils/pipe_progress.c b/busybox/debianutils/pipe_progress.c new file mode 100644 index 000000000..ab05202eb --- /dev/null +++ b/busybox/debianutils/pipe_progress.c @@ -0,0 +1,55 @@ +/* + * Monitor a pipe with a simple progress display. + * + * Copyright (C) 2003 by Rob Landley , Joey Hess + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include "busybox.h" + +#define PIPE_PROGRESS_SIZE 4096 + +/* Read a block of data from stdin, write it to stdout. + * Activity is indicated by a '.' to stderr + */ +extern int pipe_progress_main(int argc, char **argv) +{ + RESERVE_CONFIG_BUFFER(buf, PIPE_PROGRESS_SIZE); + time_t t = time(NULL); + size_t len; + + while ((len = fread(buf, 1, PIPE_PROGRESS_SIZE, stdin)) > 0) { + time_t new_time = time(NULL); + if (new_time != t) { + t = new_time; + fputc('.', stderr); + } + fwrite(buf, len, 1, stdout); + } + + fputc('\n', stderr); + +#ifdef CONFIG_FEATURE_CLEAN_UP + RELEASE_CONFIG_BUFFER(buf); +#endif + return 0; +} diff --git a/busybox/debianutils/readlink.c b/busybox/debianutils/readlink.c new file mode 100644 index 000000000..d8d7e8c2d --- /dev/null +++ b/busybox/debianutils/readlink.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini readlink implementation for busybox + * + * Copyright (C) 2000,2001 Matt Kraai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "busybox.h" + +int readlink_main(int argc, char **argv) +{ + char *buf = NULL; + + /* no options, no getopt */ + + if (argc != 2) + bb_show_usage(); + + buf = xreadlink(argv[1]); + if (!buf) + return EXIT_FAILURE; + puts(buf); +#ifdef CONFIG_FEATURE_CLEAN_UP + free(buf); +#endif + + return EXIT_SUCCESS; +} diff --git a/busybox/debianutils/run_parts.c b/busybox/debianutils/run_parts.c new file mode 100644 index 000000000..6205595bf --- /dev/null +++ b/busybox/debianutils/run_parts.c @@ -0,0 +1,114 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini run-parts implementation for busybox + * + * + * Copyright (C) 2001 by Emanuele Aina + * + * Based on the Debian run-parts program, version 1.15 + * Copyright (C) 1996 Jeff Noxon , + * Copyright (C) 1996-1999 Guy Maor + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + * + */ + +/* This is my first attempt to write a program in C (well, this is my first + * attempt to write a program! :-) . */ + +/* This piece of code is heavily based on the original version of run-parts, + * taken from debian-utils. I've only removed the long options and a the + * report mode. As the original run-parts support only long options, I've + * broken compatibility because the BusyBox policy doesn't allow them. + * The supported options are: + * -t test. Print the name of the files to be executed, without + * execute them. + * -a ARG argument. Pass ARG as an argument the program executed. It can + * be repeated to pass multiple arguments. + * -u MASK umask. Set the umask of the program executed to MASK. */ + +/* TODO + * done - convert calls to error in perror... and remove error() + * done - convert malloc/realloc to their x... counterparts + * done - remove catch_sigchld + * done - use bb's concat_path_file() + * done - declare run_parts_main() as extern and any other function as static? + */ + +#include +#include + +#include "libbb.h" + +static const struct option runparts_long_options[] = { + { "test", 0, NULL, 't' }, + { "umask", 1, NULL, 'u' }, + { "arg", 1, NULL, 'a' }, + { 0, 0, 0, 0 } +}; + +extern char **environ; + +/* run_parts_main */ +/* Process options */ +int run_parts_main(int argc, char **argv) +{ + char **args = xmalloc(2 * sizeof(char *)); + unsigned char test_mode = 0; + unsigned short argcount = 1; + int opt; + + umask(022); + + while ((opt = getopt_long (argc, argv, "tu:a:", + runparts_long_options, NULL)) > 0) + { + switch (opt) { + /* Enable test mode */ + case 't': + test_mode++; + break; + /* Set the umask of the programs executed */ + case 'u': + /* Check and set the umask of the program executed. As stated in the original + * run-parts, the octal conversion in libc is not foolproof; it will take the + * 8 and 9 digits under some circumstances. We'll just have to live with it. + */ + umask(bb_xgetlarg(optarg, 8, 0, 07777)); + break; + /* Pass an argument to the programs */ + case 'a': + /* Add an argument to the commands that we will call. + * Called once for every argument. */ + args = xrealloc(args, (argcount + 2) * (sizeof(char *))); + args[argcount++] = optarg; + break; + default: + bb_show_usage(); + } + } + + /* We require exactly one argument: the directory name */ + if (optind != (argc - 1)) { + bb_show_usage(); + } + + args[0] = argv[optind]; + args[argcount] = 0; + + return(run_parts(args, test_mode, environ)); +} diff --git a/busybox/debianutils/start_stop_daemon.c b/busybox/debianutils/start_stop_daemon.c new file mode 100644 index 000000000..e15944c59 --- /dev/null +++ b/busybox/debianutils/start_stop_daemon.c @@ -0,0 +1,296 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini start-stop-daemon implementation(s) for busybox + * + * Written by Marek Michalkiewicz , + * public domain. + * Adapted for busybox David Kimdon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" +#include "pwd_.h" + +static int signal_nr = 15; +static int user_id = -1; +static int quiet = 0; +static char *userspec = NULL; +static char *cmdname = NULL; +static char *execname = NULL; +static char *pidfile = NULL; + +struct pid_list { + struct pid_list *next; + pid_t pid; +}; + +static struct pid_list *found = NULL; + +static inline void +push(pid_t pid) +{ + struct pid_list *p; + + p = xmalloc(sizeof(*p)); + p->next = found; + p->pid = pid; + found = p; +} + +static int +pid_is_exec(pid_t pid, const char *name) +{ + char buf[32]; + struct stat sb, exec_stat; + + if (name && stat(name, &exec_stat)) + bb_perror_msg_and_die("stat %s", name); + + sprintf(buf, "/proc/%d/exe", pid); + if (stat(buf, &sb) != 0) + return 0; + return (sb.st_dev == exec_stat.st_dev && sb.st_ino == exec_stat.st_ino); +} + +static int +pid_is_user(int pid, int uid) +{ + struct stat sb; + char buf[32]; + + sprintf(buf, "/proc/%d", pid); + if (stat(buf, &sb) != 0) + return 0; + return (sb.st_uid == uid); +} + +static int +pid_is_cmd(pid_t pid, const char *name) +{ + char buf[32]; + FILE *f; + int c; + + sprintf(buf, "/proc/%d/stat", pid); + f = fopen(buf, "r"); + if (!f) + return 0; + while ((c = getc(f)) != EOF && c != '(') + ; + if (c != '(') { + fclose(f); + return 0; + } + /* this hopefully handles command names containing ')' */ + while ((c = getc(f)) != EOF && c == *name) + name++; + fclose(f); + return (c == ')' && *name == '\0'); +} + + +static void +check(int pid) +{ + if (execname && !pid_is_exec(pid, execname)) { + return; + } + if (userspec && !pid_is_user(pid, user_id)) { + return; + } + if (cmdname && !pid_is_cmd(pid, cmdname)) { + return; + } + push(pid); +} + + +static void +do_pidfile(void) +{ + FILE *f; + pid_t pid; + + f = fopen(pidfile, "r"); + if (f) { + if (fscanf(f, "%d", &pid) == 1) + check(pid); + fclose(f); + } else if (errno != ENOENT) + bb_perror_msg_and_die("open pidfile %s", pidfile); + +} + +static void +do_procinit(void) +{ + DIR *procdir; + struct dirent *entry; + int foundany, pid; + + if (pidfile) { + do_pidfile(); + return; + } + + procdir = opendir("/proc"); + if (!procdir) + bb_perror_msg_and_die ("opendir /proc"); + + foundany = 0; + while ((entry = readdir(procdir)) != NULL) { + if (sscanf(entry->d_name, "%d", &pid) != 1) + continue; + foundany++; + check(pid); + } + closedir(procdir); + if (!foundany) + bb_error_msg_and_die ("nothing in /proc - not mounted?"); +} + + +static void +do_stop(void) +{ + char what[1024]; + struct pid_list *p; + int killed = 0; + + do_procinit(); + + if (cmdname) + strcpy(what, cmdname); + else if (execname) + strcpy(what, execname); + else if (pidfile) + sprintf(what, "process in pidfile `%.200s'", pidfile); + else if (userspec) + sprintf(what, "process(es) owned by `%s'", userspec); + else + bb_error_msg_and_die ("internal error, please report"); + + if (!found) { + if (!quiet) + printf("no %s found; none killed.\n", what); + return; + } + for (p = found; p; p = p->next) { + if (kill(p->pid, signal_nr) == 0) { + p->pid = -p->pid; + killed++; + } else { + bb_perror_msg("warning: failed to kill %d", p->pid); + } + } + if (!quiet && killed) { + printf("stopped %s (pid", what); + for (p = found; p; p = p->next) + if(p->pid < 0) + printf(" %d", -p->pid); + printf(").\n"); + } +} + + +static const struct option ssd_long_options[] = { + { "stop", 0, NULL, 'K' }, + { "start", 0, NULL, 'S' }, + { "background", 0, NULL, 'b' }, + { "quiet", 0, NULL, 'q' }, + { "make-pidfile", 0, NULL, 'm' }, + { "startas", 1, NULL, 'a' }, + { "name", 1, NULL, 'n' }, + { "signal", 1, NULL, 's' }, + { "user", 1, NULL, 'u' }, + { "exec", 1, NULL, 'x' }, + { "pidfile", 1, NULL, 'p' }, + { 0, 0, 0, 0 } +}; + +#define SSD_CTX_STOP 1 +#define SSD_CTX_START 2 +#define SSD_OPT_BACKGROUND 4 +#define SSD_OPT_QUIET 8 +#define SSD_OPT_MAKEPID 16 + +int +start_stop_daemon_main(int argc, char **argv) +{ + unsigned long opt; + char *signame = NULL; + char *startas = NULL; + + bb_applet_long_options = ssd_long_options; + + bb_opt_complementaly = "K~S:S~K"; + opt = bb_getopt_ulflags(argc, argv, "KSbqma:n:s:u:x:p:", + &startas, &cmdname, &signame, &userspec, &execname, &pidfile); + + /* Check one and only one context option was given */ + if ((opt & 0x80000000UL) || (opt & (SSD_CTX_STOP | SSD_CTX_START)) == 0) { + bb_show_usage(); + } + + if (signame) { + signal_nr = bb_xgetlarg(signame, 10, 0, NSIG); + } + + if (!execname && !pidfile && !userspec && !cmdname) + bb_error_msg_and_die ("need at least one of -x, -p, -u, or -n"); + + if (!startas) + startas = execname; + + if ((opt & SSD_CTX_START) && !startas) + bb_error_msg_and_die ("-S needs -x or -a"); + + if ((opt & SSD_OPT_MAKEPID) && pidfile == NULL) + bb_error_msg_and_die ("-m needs -p"); + + argc -= optind; + argv += optind; + + if (userspec && sscanf(userspec, "%d", &user_id) != 1) + user_id = my_getpwnam(userspec); + + if (opt & SSD_CTX_STOP) { + do_stop(); + return EXIT_SUCCESS; + } + + do_procinit(); + + if (found) { + if (!quiet) + printf("%s already running.\n%d\n", execname ,found->pid); + return EXIT_SUCCESS; + } + *--argv = startas; + if (opt & SSD_OPT_BACKGROUND) { + if (daemon(0, 0) == -1) + bb_perror_msg_and_die ("unable to fork"); + setsid(); + } + if (opt & SSD_OPT_MAKEPID) { + /* user wants _us_ to make the pidfile */ + FILE *pidf = fopen(pidfile, "w"); + pid_t pidt = getpid(); + if (pidf == NULL) + bb_perror_msg_and_die("Unable to write pidfile '%s'", pidfile); + fprintf(pidf, "%d\n", pidt); + fclose(pidf); + } + execv(startas, argv); + bb_perror_msg_and_die ("unable to start %s", startas); +} diff --git a/busybox/debianutils/which.c b/busybox/debianutils/which.c new file mode 100644 index 000000000..999dded36 --- /dev/null +++ b/busybox/debianutils/which.c @@ -0,0 +1,96 @@ +/* vi: set sw=4 ts=4: */ +/* + * Which implementation for busybox + * + * Copyright (C) 1999-2004 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 + * + * Based on which from debianutils + */ + + +#include +#include +#include +#include + +#include "busybox.h" + + +extern int which_main(int argc, char **argv) +{ + char *path_list; + int i, count=1, status = EXIT_SUCCESS; + + if (argc <= 1 || **(argv + 1) == '-') { + bb_show_usage(); + } + argc--; + + path_list = getenv("PATH"); + if (path_list != NULL) { + for (i=strlen(path_list); i > 0; i--) { + if (path_list[i]==':') { + path_list[i]=0; + count++; + } + } + } else { + path_list = "/bin\0/sbin\0/usr/bin\0/usr/sbin\0/usr/local/bin"; + count = 5; + } + + while (argc-- > 0) { + char *buf; + char *path_n; + char found = 0; + argv++; + + /* + * Check if we were given the full path, first. + * Otherwise see if the file exists in our $PATH. + */ + path_n = path_list; + buf = *argv; + if (access(buf, X_OK) == 0) { + found = 1; + } else { + for (i = 0; i < count; i++) { + buf = concat_path_file(path_n, *argv); + if (access(buf, X_OK) == 0) { + found = 1; + break; + } + free(buf); + path_n += (strlen(path_n) + 1); + } + } + if (found) { + puts(buf); + } else { + status = EXIT_FAILURE; + } + } + return status; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/docs/.cvsignore b/busybox/docs/.cvsignore new file mode 100644 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..eee67cf09 --- /dev/null +++ b/busybox/docs/autodocifier.pl @@ -0,0 +1,274 @@ +#!/usr/bin/perl -w + +use strict; +use Getopt::Long; + +# collect lines continued with a '\' into an array +sub continuation { + my $fh = shift; + my @line; + + while (<$fh>) { + my $s = $_; + $s =~ s/\\\s*$//; + #$s =~ s/#.*$//; + push @line, $s; + last unless (/\\\s*$/); + } + return @line; +} + +# regex && eval away unwanted strings from documentation +sub beautify { + my $text = shift; + $text =~ s/USAGE_NOT\w+\(.*?"\s*\)//sxg; + $text =~ s/USAGE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg; + $text =~ s/"\s*"//sg; + my @line = split("\n", $text); + $text = join('', + map { + s/^\s*"//; + s/"\s*$//; + s/%/%%/g; + s/\$/\\\$/g; + eval qq[ sprintf(qq{$_}) ] + } @line + ); + return $text; +} + +# generate POD for an applet +sub pod_for_usage { + my $name = shift; + my $usage = shift; + + # Sigh. Fixup the known odd-name applets. + $name =~ s/dpkg_deb/dpkg-deb/g; + $name =~ s/fsck_minix/fsck.minix/g; + $name =~ s/mkfs_minix/mkfs.minix/g; + $name =~ s/run_parts/run-parts/g; + $name =~ s/start_stop_daemon/start-stop-daemon/g; + + # make options bold + my $trivial = $usage->{trivial}; + $trivial =~ s/(?/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" + ; +} + +# the keys are applet names, and +# the values will contain hashrefs of the form: +# +# { +# trivial => "...", +# full => "...", +# notes => "...", +# example => "...", +# } +my %docs; + + +# get command-line options + +my %opt; + +GetOptions( + \%opt, + "help|h", + "pod|p", + "verbose|v", +); + +if (defined $opt{help}) { + print + "$0 [OPTION]... [FILE]...\n", + "\t--help\n", + "\t--pod\n", + "\t--verbose\n", + ; + exit 1; +} + + +# collect documenation into %docs + +foreach (@ARGV) { + open(USAGE, $_) || die("$0: $_: $!"); + my $fh = *USAGE; + my ($applet, $type, @line); + while (<$fh>) { + if (/^#define (\w+)_(\w+)_usage/) { + $applet = $1; + $type = $2; + @line = continuation($fh); + my $doc = $docs{$applet} ||= { }; + my $text = join("\n", @line); + $doc->{$type} = beautify($text); + } + } +} + + +# generate structured documentation + +my $generator = \&pod_for_usage; +foreach my $applet (sort keys %docs) { + print $generator->($applet, $docs{$applet}); +} + +exit 0; + +__END__ + +=head1 NAME + +autodocifier.pl - generate docs for busybox based on usage.h + +=head1 SYNOPSIS + +autodocifier.pl [OPTION]... [FILE]... + +Example: + + ( cat docs/busybox_header.pod; \ + docs/autodocifier.pl usage.h; \ + cat docs/busybox_footer.pod ) > docs/busybox.pod + +=head1 DESCRIPTION + +The purpose of this script is to automagically generate +documentation for busybox using its usage.h as the original source +for content. It used to be that same content has to be duplicated +in 3 places in slightly different formats -- F, +F. This was tedious and error-prone, so it was +decided that F would contain all the text in a +machine-readable form, and scripts could be used to transform this +text into other forms if necessary. + +F is one such script. It is 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<--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 CONFIG_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 in F. I + +=item B + +This should be an example of how the command is actually used. +This will not be printed when a B<-h> is given to a command -- it +will only be included in the POD or SGML documentation. I + +=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.26 2004/04/06 15:26:25 andersen Exp $ diff --git a/busybox/docs/busybox.net/.cvsignore b/busybox/docs/busybox.net/.cvsignore new file mode 100644 index 000000000..393b00210 --- /dev/null +++ b/busybox/docs/busybox.net/.cvsignore @@ -0,0 +1,2 @@ +BusyBox.html +busybox.tar.gz diff --git a/busybox/docs/busybox.net/FAQ.html b/busybox/docs/busybox.net/FAQ.html new file mode 100644 index 000000000..a9324ae26 --- /dev/null +++ b/busybox/docs/busybox.net/FAQ.html @@ -0,0 +1,324 @@ + + + +

Frequently Asked Questions

+ +This is a collection of some of the more frequently asked questions +about BusyBox. Some of the questions even have answers. If you +have additions to this FAQ document, we would love to add them, + +
    +
  1. Which Linux kernel versions are supported? +
  2. Which architectures does BusyBox run on? +
  3. Which C libraries are supported? +
  4. Can I include BusyBox as part of the software on my device? +
  5. I think I found a bug in BusyBox! What should I do?! +
  6. Why do I keep getting "sh: can't access tty; job control + turned off" errors? Why doesn't Control-C work within my shell? +
  7. I demand that you to add <favorite feature> right now! How come + you don't answer all my questions on the mailing list instantly? I demand + that you help me with all of my problems Right Now! +
  8. How can I get started using BusyBox? +
  9. I need help with BusyBox! What should I do? +
  10. I need you to add <favorite feature>! Are the BusyBox developers willing to + be paid in order to fix bugs or add in <favorite feature>? Are you willing to provide + support contracts? +
  11. I think you guys are great and I want to help support your work! + + +
+ + +
+

+

Which Linux kernel versions are supported?

+

+ + + Full functionality requires Linux 2.2.x or better. A large fraction of the + code should run on just about anything. While the current code is fairly + Linux specific, it should be fairly easy to port the majority of the code + to support, say, FreeBSD or Solaris, or Mac OS X, or even Windows (if you + are into that sort of thing). + + +


+

+

Which architectures does BusyBox run on?

+

+ + + BusyBox in general will build on any architecture supported by gcc. + Kernel module loading for 2.2 and 2.4 Linux kernels is currently + limited to ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, + S390, SH3/4/5, Sparc, v850e, and x86_64 for 2.4.x kernels. + + With 2.6.x kernels, module loading support should work on all architectures. + + +


+

+

Which C libraries are supported?

+

+ + + uClibc and glibc are supported. People have been looking at newlib and + dietlibc, but they are currently considered unsupported, untested, or + worse. Linux-libc5 is no longer supported. If you require a small C + library, you should probably use uClibc. + + +


+

+

Can I include BusyBox as part of the software on my device?

+ + Yes. As long as you fully comply + with the generous terms of the GPL BusyBox license you can ship BusyBox + as part of the software on your device. + + Please consider sharing some of the money you make. + + +
+

+

I think I found a bug in BusyBox! What should I do?

+

+ + If you find a problem with BusyBox, please submit a detailed bug report to + the BusyBox mailing list at + busybox@mail.busybox.net. Please do not send private email to Erik + (the maintainer of BusyBox) asking for private help unless you are planning + on paying for consulting services. When we answer questions on the BusyBox + mailing list, it helps everyone, while private answers help only you... + +

+ + If you find bugs, please submit a detailed bug report to the BusyBox mailing + list at busybox@mail.busybox.net. A well-written bug report should include a + transcript of a shell session that demonstrates the bad behavior and enables + anyone else to duplicate the bug on their own machine. The following is such + an example: + +

+	To: busybox@mail.busybox.net
+	From: diligent@testing.linux.org
+	Subject: /bin/date doesn't work
+
+	Package: BusyBox
+	Version: 1.00
+
+	When I execute BusyBox 'date' it produces unexpected results.
+	With GNU date I get the following output:
+
+		$ date
+		Fri Oct  8 14:19:41 MDT 2004
+
+	But when I use BusyBox date I get this instead:
+
+		$ date
+		illegal instruction
+
+	I am using Debian unstable, kernel version 2.4.27 on a x86 system,
+	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 proper detail may never be fixed... Thanks for understanding. + +
+

+

Why do I keep getting "sh: can't access tty; job control + turned off" errors? Why doesn't Control-C work within my shell?

+

+ + Job control will be turned off since your shell can not obtain a controlling + terminal. This typically happens when you run your shell on /dev/console. + The kernel will not provide a controlling terminal on the /dev/console + device. Your should run your shell on a normal tty such as tty1 or ttyS0 + and everything will work perfectly. If you REALLY want your shell + to run on /dev/console, then you can hack your kernel (if you are into that + sortof thing) by changing drivers/char/tty_io.c to change the lines where + it sets "noctty = 1;" to instead set it to "0". I recommend you instead + run your shell on a real console... + + +


+

+

How can I get started using BusyBox?

+

+ + An easy method to build your own basic BusyBox based system, is to + follow these simple steps: +

    +
  • Point your web browser here +
  • Click on "Download tarball" +
  • Unpack the tarball on your Linux system somewhere +
  • run 'make' and configure things to taste. +
  • run 'unset CC'. Some Linux systems (i.e. Gentoo) set 'CC' + in the system environment which messes up cross compiles. +
  • run 'make' +
  • go have lunch, drink a pop, call a friend, play a video game, etc + till it finishes downloading software and compiling things. +
  • You should now have a shiny new BusyBox based system. +
+ + +
+

+

I demand that you to add <favorite feature> right now! How come + you don't answer all my questions on the mailing list instantly? I demand + that you help me with all of my problems Right Now!

+

+ + You have not paid us a single cent and yet you still have the product of + many years of our work. We are not your slaves! We work on BusyBox + because we find it useful and interesting. If you go off flaming us, we + will ignore you. + + +


+

+

I need help with BusyBox! What should I do?

+

+ + If you find that you need help with BusyBox, you can ask for help on the + BusyBox mailing list at busybox@mail.busybox.net. In addition to the BusyBox + mailing list, Erik (andersee), Manuel (mjn3) and others are known to hang out + on the uClibc IRC channel: #uclibc on irc.freenode.net. + +

+ + Please do not send private email to Erik, Manuel, or the other BusyBox + contributors asking for private help unless you are planning on paying for + consulting services. + +

+ + When we answer questions on the BusyBox mailing list, it helps everyone + since people with similar problems in the future will be able to get help + by searching the mailing list archives. Private help is reserved as a paid + service. If you need to use private communication, or if you are serious + about getting timely assistance with BusyBox, you should seriously consider + paying for consulting services. + +

+ + + +


+

+

I need you to add <favorite feature>! Are the BusyBox + developers willing to be paid in order to fix bugs or add in <favorite feature>? + Are you willing to provide support contracts?

+

+ + Sure! Now you have our attention! What you should do is contact Erik Andersen of CodePoet Consulting to bid + on your project. If Erik is too busy to personally add your feature, there + are many other active BusyBox contributors who will almost certainly be able + to help you out. Erik can contact them privatly, and may even let you to + post your request for services on the mailing list. + + +


+

+

I think you guys are great and I want to help support your work!

+

+ + Wow, that would be great! Erik personally pays for all the bandwidth, and + all servers used for busybox.net out of his own pocket. If you would like + to make a donation to help support BusyBox, and/or request features, you + can click here: + + +

+
+ + + + + + +
+
+ + + If you prefer to contact Erik directly to make a donation, donate hardware, + request support, etc, you can contact + CodePoet Consulting here. + CodePoet Consulting can accept both Visa and MasterCard for those that do not + trust PayPal... + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/busybox/docs/busybox.net/about.html b/busybox/docs/busybox.net/about.html new file mode 100644 index 000000000..c08626386 --- /dev/null +++ b/busybox/docs/busybox.net/about.html @@ -0,0 +1,63 @@ + + + + + +

BusyBox: The Swiss Army Knife of Embedded Linux

+ + +BusyBox combines tiny versions of many common UNIX utilities into a single +small executable. It provides replacements for most of the utilities you +usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox +generally have fewer options than their full-featured GNU cousins; however, +the options that are included provide the expected functionality and behave +very much like their GNU counterparts. BusyBox provides a fairly complete +environment for any small or embedded system. + +

+ +BusyBox has been written with size-optimization and limited resources in +mind. It is also extremely modular so you can easily include or exclude +commands (or features) at compile time. This makes it easy to customize +your embedded systems. To create a working system, just add some device +nodes in /dev, a few configuration files in /etc, and a Linux kernel. + +

+ +BusyBox is maintained by Erik Andersen, and +licensed under the +GNU GENERAL PUBLIC LICENSE + +

+

+ +

Sponsors

+ +Please visit our sponsors and thank them for their +support! They have provided money for equipment and +bandwidth. Next time you need help with a project, +consider these fine companies! + + +
    +
  • Penguru Consulting
    + Custom development for embedded Linux systems and multimedia platforms +
  • + +
  • opensource.se
    + Embedded open source consulting in Europe. +
  • + +
  • Codepoet Consulting
    + Custom Linux, embedded Linux, BusyBox, and uClibc + development. +
  • + +
+ +If you wish to be a sponsor, or if you have already contributed and would like +your name added here, email Erik. + + + diff --git a/busybox/docs/busybox.net/busybox-growth.ps b/busybox/docs/busybox.net/busybox-growth.ps new file mode 100644 index 000000000..2379defa4 --- /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/copyright.txt b/busybox/docs/busybox.net/copyright.txt new file mode 100644 index 000000000..528338da9 --- /dev/null +++ b/busybox/docs/busybox.net/copyright.txt @@ -0,0 +1,29 @@ + +The code and graphics on this website (and it's mirror sites, if any) are +Copyright (c) 1999-2004 by Erik Andersen. All rights reserved. + +Documents on this Web site including their graphical elements, design, and +layout are protected by trade dress and other laws and MAY BE COPIED OR +IMITATED IN WHOLE OR IN PART. THIS WEBSITE IS LICENSED FREE OF CHARGE, THERE +IS NO WARRANTY FOR THE WEBSITE TO THE EXTENT PERMITTED BY APPLICABLE LAW. +SHOULD THIS WEBSITE PROVE DEFECTIVE, YOU MAY ASSUME THAT SOMEONE MIGHT GET +AROUND TO SERVICING, REPAIRING OR CORRECTING IT SOMETIME WHEN THEY HAVE NOTHING +BETTER TO DO. REGARDLESS, YOU GET TO KEEP BOTH PIECES. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THIS +WEBSITE AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THIS WEBSITE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR +LOSS OF HAIR, LOSS OF LIFE, LOSS OF MEMORY, LOSS OF YOUR CARKEYS, MISPLACEMENT +OF YOUR PAYCHECK, OR COMMANDER DATA BEING RENDERED UNABLE TO ASSIST THE +STARFLEET OFFICERS ABORD THE STARSHIP ENTERPRISE TO RECALIBRATE THE MAIN +DEFLECTOR ARRAY, LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +WEBSITE TO OPERATE WITH YOUR WEBBROWSER), EVEN IF SUCH HOLDER OR OTHER PARTY +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +You have been warned. + +You can contact the webmaster at if you have some sort +of problem with this. + diff --git a/busybox/docs/busybox.net/cvs_anon.html b/busybox/docs/busybox.net/cvs_anon.html new file mode 100644 index 000000000..f823d0535 --- /dev/null +++ b/busybox/docs/busybox.net/cvs_anon.html @@ -0,0 +1,57 @@ + + + +

Anonymous CVS

+ +We allow anonymous (read-only) CVS access to everyone. The first command you +need to run for anonymous CVS access is: +
+cvs -d:pserver:anonymous@busybox.net:/var/cvs login
+

+CVS will prompt you for a password. Just press the Enter key (there is no +password for anonymous access). This step only needs to be done once, the first +time you attempt to access CVS. +

+Once the login is complete, you can then check the list of available +CVS modules by running the following command (all on one line): +

+cvs -z3 -d:pserver:anonymous@busybox.net:/var/cvs co -c 
+ +

+If you wish, you can then check out a local copy of any of the +available modules. The following is an example of how to grab +a copy of busybox and tinylogin: +

+    cvs -z3 -d:pserver:anonymous@busybox.net:/var/cvs co -P busybox tinylogin
+This will create a directory called busybox and a directory called +tinylogin in the current directory. These directories contain the +latest and greatest source code for busybox and tinylogin. + +

+If you are not already familiar with using CVS, I recommend you visit +this quick Introduction to CVS. + +

+I usually create a ~/.cvsrc file with the following things in it, and I +recommend you should use the same: +

+    -z3
+    update -dP
+    rdiff -u
+    diff -ubBwpN
+    checkout -P
+ +

+Once you've checked out a copy of the source tree, you can update your +source tree at any time so it is in sync with the latest and greatest by +running the command: +

+cvs update
+ +Because you've only been granted anonymous access to the tree, you won't be +able to commit any changes. Changes can be submitted for inclusion by posting +them to the appropriate mailing list. For those that are actively contributing +CVS write access can be made available. + + + diff --git a/busybox/docs/busybox.net/cvs_howto.html b/busybox/docs/busybox.net/cvs_howto.html new file mode 100644 index 000000000..837d6cd61 --- /dev/null +++ b/busybox/docs/busybox.net/cvs_howto.html @@ -0,0 +1,44 @@ + + + +

How to use CVS

+ + +If you want to know all the gory details, you will want to visit +the CVS main web page.

+For the impatient, the following is probably about all you need to know: +

+ +

+
cvs checkout -c
+
Will list the modules available for checkout +
cvs checkout < module name >
+
Will checkout the named module +
cvs co < module name >
+
Same thing +
cvs update
+ +
Updates your local archive so it is in sync with the repository + -- your local updates are left intact. Tries to merge upstream updates + into your local updates. You will see the following tags when it is + updating your local repository: C means conflict, U means update, + P means patched, and M means modified. +
cvs up
+
Same thing +
cvs update < file name >
+
Same thing but for just the named file(s)/directory(s). +
cvs commit
+
Will check in all your work. +
cvs add < file name >
+ +
Adds the named file/directory into CVS +
cvs remove < file name >
+
Removes the named file/directory from the upstream repository. +
cvs rm < file name >
+
Same thing +
cvs log < file name >
+
+ + + + diff --git a/busybox/docs/busybox.net/cvs_write.html b/busybox/docs/busybox.net/cvs_write.html new file mode 100644 index 000000000..5c882f48f --- /dev/null +++ b/busybox/docs/busybox.net/cvs_write.html @@ -0,0 +1,32 @@ + + + +

CVS Read/Write Access

+ +If you want to be able to commit things to CVS, first contribute some +stuff to show you are serious. Then, very nicely ask +Erik Andersen if he will set you up with +an account. To access CVS, you will want to add the following to set up your environment: +
+$ export CVS_RSH=/usr/bin/ssh
+$ export CVSROOT='username@cvs.busybox.net:/var/cvs'
+
+It goes without saying you must change username to your own +username... +

+ +To obtain commit access, you will need to demonstrate you are +serious by submitting a few good patches first. Then, you will need to +select a user-name to use when committing stuff, and finally, you will +need to send me the username you have selected, an ssh key, and the email +address where you prefer email to be sent (I will forward any email sent +to you, but not store it). + +

+Note that if you would prefer to keep your communications with me +private, you can encrypt your email using my +public key. + + + + diff --git a/busybox/docs/busybox.net/docs.html b/busybox/docs/busybox.net/docs.html new file mode 100644 index 000000000..fc9ac6d2b --- /dev/null +++ b/busybox/docs/busybox.net/docs.html @@ -0,0 +1,27 @@ + + + +

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.
  • + +
  • If you need more help, the BusyBox mailing list is a good place to + start.
  • +
+ + + diff --git a/busybox/docs/busybox.net/download.html b/busybox/docs/busybox.net/download.html new file mode 100644 index 000000000..a6a86ac33 --- /dev/null +++ b/busybox/docs/busybox.net/download.html @@ -0,0 +1,38 @@ + + + + +

Download

+ +Source for the latest release can always be +downloaded from http://www.busybox.net/downloads. + +

+You can also obtain Daily Snapshots of +the latest stable, and the latest development CVS source trees. + +

+BusyBox now has two CVS trees. The "busybox-stable" tree +contains the older 0.60.x stable series. The "busybox" tree contains +the latest 1.0.0-preX development version of busybox.
+ +

+ + + diff --git a/busybox/docs/busybox.net/footer.html b/busybox/docs/busybox.net/footer.html new file mode 100644 index 000000000..9756f5dde --- /dev/null +++ b/busybox/docs/busybox.net/footer.html @@ -0,0 +1,20 @@ + + + + + + + +
+ +

+ + Copyright © 1999-2003 Erik Andersen +
+ Mail all comments, insults, suggestions and bribes to +
+ Erik Andersen andersen@codepoet.org
+
+ + + diff --git a/busybox/docs/busybox.net/header.html b/busybox/docs/busybox.net/header.html new file mode 100644 index 000000000..77c141832 --- /dev/null +++ b/busybox/docs/busybox.net/header.html @@ -0,0 +1,81 @@ + + + + + BusyBox + + + + + + + + + + + + + + + + + + + + + +R&>bG zN3ek*3FgYv_}uvT^)0Qr@dpp4j6SmM#h(xIo>9y87Z(?gY8g4JW!5O0S?&bM6N0b` zhgzK4zT(-Z4-@3!=pECbM5vN2%%@|zjd^Lz2cLe>ZOoh9PQUc$KIj7H@5@R!!wPJB z&6XoW7;C0Xx@*oHY*W!YFNOh{m^6{+c=FU^Z(@W3MUfJ>!Z0nrOr8B%M)^6A;Xx@w zXav>HAR}6CO?Q?iNHsyL5IKH7Nea^V_=;TWGd@0L@R1`!Zf)B(QtfkZWrL0%{TaJcW$kMdd1t)`BvgBk-n<5w3)CN7**Pj974w(aLk&`hhC7)QB}nl)-9^5m>pqugz{iQ`^8z}G{e!MJ_ImHhC)Zl+vNvy-I2ym= z2Chc8286VyXN`<(vEs7MoH>&e6tsKd;N1=^E)F6|Z?-F{Mso-Tngk^n^UDr8uqf9C z9HecgM@4j%l?F)z`I z?-=vJ>#x7w^@IDncH8*UZ-2kqBG%siOv1#8qlXR|+BkG#Qp3QfP4lu)y0?ELe6EG#U}_utol9~|0@{&z=_OO}A5&8S&ZZAP|WNy$+a z2bprtfB`+5YCnE#nxx0WZ;n|A=Lpa9Cd$nRUAgY>+I8de@AW@5ckTwitj5NPsMkY= z$cYbGISo7`%s(N)zqzQWTsZCa?zH%pR2+7v@%3`=+GgBLy)!CIEhD@MG`-7AWyaLb zFkM2Cfl{EgsJ7Fjd#aD7QDRGi{3I+RQ$98>R)X)%?z_Yi*!kUX5fJFdy9wsVmX}c9$(-#8-5!xGEyleRjfbP)l zu(n7VO-s|pKrA*Lld}p{x+~D$pgoOua;o6#1lf}ai_js|tXj2c#fnXydpPH`jf?Zk zf;GG0my_e-;_sgoJHOA*9-p`r5SP(hWZbV-;2wc5_A(y4sSNk-wkM`+8af+wY@Qp52Ciww`$EA$C; zs;w<5a`D`>X~p(ayH;#kv1e7P=cZJIwo8^@){PrC+|GoB`6q-XBxKq4f8Z`uo41P5 z9PYQ#WNFjVf_DDGGLp|HDNY}!OSmf+vu@1K)% z1A08;ecg1V~$x$(DsELOz2P*Lpn$_9HEc^6TNMJ&oF5}h+6;<<_YR3saa z=Hx`a63j)4WLlYmSR0^S(wz8hSOI*%^vI!_6GJ-?Suvg;o>0MCMSMIev{O-05u$a| zvGcoD2*wq162~fTSGw>-jj^dNIc_&##r)5l3Cl?c_4l`(-{-}<5ZiANW3gO7(`MO) z(s=9mH*Q&c9d zC7(q_OM_)vdeo$)=pce?YnAZ{1Mu={qG9M zd$nS-tk_+}c4=vHZbfP9`lCm)=b-C$=YZ0E;-dih{PWL)9~=)4_jfgs+_-kgMDNhF zK)PmnM06)I_^Nf#23?ZjyuAZVTUMrhKu)@AP2~f%*D0TavRcXxQ7xE-BA12{tO!y8 z46KlMZSma-pcr3Mv*NJ@kD)O0lNL6u+Qg4rShWclUD|QUIm5SUpUH9a3&jfaHwVb_ zW+X|Mtkk;#QbLQ5_vxXi1)FGVMtVpk=YH$XDM-lg;me-2p`> zk*lmgA9aV~p`k<)jCX1ZsBPk`3@NhqU=d_>ZPSWfE9h?aARlf*b;ePGdc&&N4W4m+ zH*z4uB(Ow0Lycju6y?pKMG5{1Sws44y9@tkq)eYWg$V_f?Hx%vaGQYrirO(qlgDUn*bcI= zFrdpE0%Y#UAWzeZwUg3epp%KDle8&{o)1rFmOpSXG{Gfx#ft5#U)a7~SbA=HpK}rv zX^u?%T>L!!+RwCGGMhPIU~<@$8FXH znSPeG&O*CwBWDHK44C!c3jq22My_-DAmOr{Gdw8QBq_m6q%5aYI|1{c^^Nfvp9SIvZEqM zXwU?*u=T=JAauXZ`Q+668Gn9$9P@++Cw9VWQ-!0PG?_#| ziInVaZ`uC?E(9n+#^+n-k5_#Ru#=>W(-cXvs}+KSvmblI#~JR^VBunQN+GP$&6n7kmv$zj$*-b1a~xbZs_8QJ^F3Ihw%%6ZmyzJyzV z=?q0g_IQW!WoZtBcYDhaLP`uo$E@hhA)|xLfXd`k5eBRC^2+#rLBdSagy^J526BrR z56c5;LtVz6w>Y(S?e^^!yUw4V+rNMR)L7=mJ)2hX6b)N+FD{;Ow|SS?iE)&d|re_mACfBuYHGvZR?Y;B=Oo+W6T3d`uH z=65cPYkyYiP^s5ptTp~&L3{!O7rRvW#3`zu)^_w`s2pVb!MYn?*D~#YtKMvB``p3L@Def#R|7U!>w1;{J?=P$n8f5!az zW9MUCxpE~oHa3-~2+H0@mcNTXyJ>BOGCA^GHbiz9+6Q#8&Ry7RcxTj- zHSYYpQx=-7Q)RV4iNIKjA5BooDbWK;zxd@d-_@f$`Q^}jfmhBcVZJYBQ1!PN!Zcddv; z=eJ^R?3J;x^RLXEJ6~4+u^i{f;^|naqD(ajMI@V}Xj}T3Cz+d{@i$L8(~twCp}U*e z1i830d_3dh10UT5ko-{S>uVU>M~^Psn!S~8rsJX_$2$xjZ(Z0^I1ZR~eBV>gX#7-! zZxk1~JD@PosjvyHpC~e4+Lio#DX$b5mC0c^b7ZaRnbTBTgXSHunlISLTZXuk0H;e{5{rZNE5gfWM2`)D*krJg&%P}-e9h=d}7NsOK97#QLZS-9L`Ozp?a(c<^Y*@(w+1YcZ+5jVm zyac)1VLU>2*wz8p2egzup=hUl%1*RK zvP^R~GLuwBTs7Tn9o2~dWxItT9z&GgyMrw4A-eOY7VwkdPJ!JnDDl{N+>XsXcIApd z+;CYw#bT`bFbhkWG5Vd!k<$cSxSP%~g-ezj-vnyuhMth-CkVYZTW(xN^P>eCZ2R2$ z=*T-iALJ8Dq)A*;yatjyntfCmAeI`e(&G%u{6b1{D}!6r8*QE|uJR8LmO_vS?Iz9= ztkGjj;g7E_wBP_ z9G^P1dco>b7DRZA5U*@Hw&%(*smq%-*~iNAvs7<^hZy;p45CR&FNp8%Mq7=ey1BWz z4Ly@YfC&j<3ETn+eMT$)e>U`yi=iKviu|RSUFkXoT#bam!H*DAtYrg@)+`!MV@KYaWp&rRcTA1H3cf0H+ z@99R7v9uhTWI5E$5+G?Hladk=n*9^}`B4`MgDJ`c+iU$lK62oTpAYgLfLxJFgT)Wix|_Ipf78s zMX@kMs7}HIFz!Utk9VB=#D{Fct@FKy?wfmR^=icP>a`FgO!h92gaCnW?lH>m3LwU^ zO51N(wSk4kepBoQ``h5Cec+GZ?N~fTlGHxz!k!^5JS`Vrb~^((CN(7ZLxlWfflwgi z2Jh>m3)of28Vef-I&aIXRod3DB$yvs#DEnzam?v@R_z1;|2nH&c6#efxP0Bv5wS z_}if^JqrT`Bm*2EP07w$bJlF5)1W!6nm6(LxL;+hn#iD_%VXzS5F&2!Hf>OZ60gwU z03=Gwrd5>T?b|qP07*PMc5D@Zg4=C-`#4KKD3SCw+}=Qvu!?+R@)<@sLYxb6jog?Z zA90~>p$X0LMIf2LQ#3~(Sa}yfKDd?An?oOzPj64r%=dOQp8`0;7ucjx2AQ+iCLIw+_NZrouv zuDipM7SD$Q*B-hmZ>rNE$|A|wD-3F^+rWs+s;^wbF!pvFB6|ZD@*^qw z`Po~ZQ49UG8**flr5jH#QyW-JoqTXE1gpH~d$%aIh}7r~4a5yudtJyprb-T*=RS;B(H z`L&z-S+?WiC+mz0Hwe%8hY@5FA0+V&0F6T%&ol_bP+lAO!E?)J0NvKJE za)c@_`BG4<8PnWGkf{(&Qd7P|&`<>P_SNStEVi%RehMlCK~5kncAcLaJAWVFGb>?D zDbg^SNN(U0``AtNKC386_{JOdw=H@4V0*hiPa%{|1uXv@zRwP=U_#EBB-x~C2y37h zS13a%$>zaDNSIk!4M#?QG3YLUyl)AoKe(--AnC~w;TJ@Vt*sXTvIWX)g(SDK;0)V3 zb^MU`Ha_$E#;)DEjd|(4gbTw0p*1GHLb`G5Ldvl$Z9F>_A&Z!%R(V1wypOr%zAIu9 z*Ir${di$x>+u;>Y%{?!2JiliTf;o2XzAO7QjxdZHR@vW%{D@Qdarc0Cc!enA;uzyH zzvVQ%-4iT{k~e>s9F8oq+!`Bsb~R7RY(U-imrE#G?K1D?#>On$s?lHk{C73nbI(dv z8=#y6MUo^HX|w*qvh`{zd0|;g%gFW1)=O|Bx`$28AH4my&wucG*AKdl8MCEjYER{R ztjUlyrch8qnS2j_p%_b=%qAyvE3vy4EV#N_xf_e^78Wo&=g+HzxJO;~#1i2KkYuff z?hwt;Bkp6zvS_1`0ZArAyn_Dvc53V@KhF&tZhLzA;UcHL5hI!!a?HY$hQ@}Z#-xUc zNfVQB4NLGhk@T+hZpgB=opG&UMjCu2=Ev@UNJLKo{%cq#&I9|?27AvF{T)4UY{41vxEP%;g zO`{Evr_^|xY4QA?xmV`SB|@^K6zv()BF7CI2$CdOx$(4pgPc#@?&9ZpJI*gFF3$6| z+EwuMZ+F4fMP_Fp$bD>S6LZ}7PR5~0Lno5t#EI}e&}l+K5vomtZGYIv(Z}z`nWlr5 zD3aI3k|RfoERP;Nx_%i-_J!6(DBdm5BQ!ZoiL%w&;=R|qjp;V#(`k?8w)T|iSsL*P zN0DU{y1_zL^R)6%bIaIc=u`Or>7czqDzxA@_S6+R?)eC1K%5JJsi~>+Nm9C&D-7aT z2J!|e+V;XRmcH9hc4D}xmBn*-Jlj{buTt5OB>nk*ZfO!5a@3OZ3x_sPkSIHlWT+IJ zqELWLuua`Fqha*Q-~VGy|8TuiEE%#`eIgY_nSFHG`eiM+N#$;pVuRSG`(bPsZk=`H zv%hwG=_N#V%u%1fv@*^rtm(wHk1>HB3k!P&2Bww!w{M!ODRHg%Y$|b=#oS$lczzdR zdhYovSLPGl*wok!UlpxvS6MVyTnQIQEbwtKFP9{t5#IA!IYKhTiB z^;R+Jww!RmPh2^QYU8sXft@R{ec{5<^+%~kV8o&4*5p1N3`bnn%1heIaV;sdUI3;ddf*Ino;HT1@pSw$Pqo@xDIKossq8^1Lx1`&( zeQ71h*xMTz@={nN8?v@|iZVSnwDU{~sWa`IE%5+;#}{FE6hx_xIe8YTJ0=u7K<%KC*aJu{+Hq zJ4Iy3_4`q?$q?4ZC%Ls{{nqtIr_NeGY$`w&1{Th0dFRXL|GMR5%KFy8K%P`=?Nrf3 zVO9i`2kknwK>mj@rxxI{Yu7Oe=efLSEene@+LmL|ys*Z^rfPcJlM1VSMHn7aA7R51 z9fxWImT_@$yyHTMdv4G;BFe@7NX0>x%n9>1H}y8;7-o(a{LO`6=x$tCX#{6cZIO#7 zI0EF|2<>}Dt>OPRvDkeHw{C3q?=L=jG<*Mg=En;yFp@2~rSL$gKCr(a{^g9m=fXSn~vHF7`X2qiK-@_$4LV}6w9^s%R5wP9a!N~b+94ZPKm^H>vk(ss_~&NKuIms_1?FNB!z{<3(%C}7;D~*m_d8C{ zuw7eS)2gbjN`I1N*-p}3kDvGd=iABDCs`X4gM$DtHT~Cr`;Y&~3yId(zxecVyPVy4 z^vNbAh3%>UySBR)Ht(!<)2wO1 zTq|B$|Ki|#Vg5`)M0WV-_^^T?cnu)?D`?E{&UOopT!9E?sk_doq*@eHod<97AYZ~y22 z_^q_xyz^j}3h+Ug)xLRi_ml{-;1?FG=qa9FoJXY#PF{Q_HR+SDcCFBy=m60Vf_RBV z1wByTvf{6p_K_dH!h>&R{gQ`o1>S2QO~|&#>LG$}fiz!m^Kiy16+8=thLejSLj0rW z-gy>B4;KLA()Fc7am*!qLF2MXQZm>+>M(Or%xq==B+%@P>^*G{GlQ~k*fAQB-~Q&e zzabI-?tv5r-eG6m16pPvS)#WH5|(lb#t=5a5SsCkWO;JA(grLxz)Yth!A>__vO?kY zbU*IMLSmAy8`pHMytm|o>591I(70h&H%;@EXX>V>GC*d)3L@X&Tb`!n0ce4X0HZ9! z=cLo;k>eiJz63~>|G9o$>J2xRFIOZaY|xetauj?A=1I$jHkK#u%z)DQtcvw+|C(S1qTQ?5Y!w8^fgICoxIefJHTW2 z6pJ?V71zDE{NiCHnaibzBj0k@^VgmKcE~Fer2@v)AcqP?$~fah#M&Gb2a25xNI6>E z%uHrRK6*BP3;x5iMtUHZ8yHZ&4FLonrP4y>c)BZz2Qcw8G}YlD9ZyS-N{`_2tU_QN z@B&0km-yJo4V2P0z5~Swc@6Z{)pVMr!jht-tQ{8+xynjYU}iPNPs^iz1dG8&Hu-3U zLPb)$s+65?SIEe`K~}o?Xw>BvyJ(MqoEc@+<~kmShhHw;Sb9y#!yB|X@Ps-bWQW3C z90DuPcFxZB9zKJM2Pf?1%XbDOA1hJIgVzTx_k+`2RjmReZz|n*fu|7i(c0;n_&w9jgdH|`7H;&J>q%?d(1Ji5YAv-qOD(joFJb%}xDiz^;b0S-?d z7*^Vae0#nCAfr)`(O{jsQgLnDZW`mg0rEeO9*zl=(2gp`*`RqL8A(4!Bs)!QQJNOFhqVLZ zM&ioOc3nTMr4=D%8@>|rAh3MkraE!iLh2I9Y(qKaulw)kMK|UJNxYxW7aD=xOda>2 zKLF>mF&Z0I8*Z>Dd*e$XB=B6i@g>t3h!9sYbpbI$9P77Na6X#91&{l|uV4O}jFhAz z(F1O~G}ZV}C;~lvSFCqQKn$O2ryE_aoa#En2@D)ar>7y%_%uK+0#xWo+g92z6lSSl z|8vP!IKpexO^dbcLc1{EZZjt_&#G&-%wBPWoGi#Y-X)pOXY=#(u-ldPycbMV4db9U zK=wmNE+DSC4kvO)iS$yKmz3(m3Zw0nVumgd>tQQQ1g%`!pL#^4_)-Nf^cY`T6op(| zg)eL}kr#^j?uFWXR9ZunGBxFF07Ixp=*WRn(6Tg8vZo>3(_(1^vhkLSjw` zAs2+{vVcmHMgRaH07*naR40rDSCfncJD<4UY!8?xl#CMLL_VB- z_N+4h%CQ{7SnQ2x_g@(s3kzU%bz_hM5ynRHYC9|BB!DE6(3a%m?Uj#SSS4!1T?LYI zw`optp=sV#FA8NQyhDqqNic8ZF?GYtg7On1OJgImP*!Z z9DyWsWl~ed8$M&82c~ITLDh5{%%C+I%mHN^k_>OSo|LuQ1p{Wg4IJm;Z#;YW@ZpR3 z351p9#6fS2jWF_%O_FqdmM&~I_wK3$Wqs!?v$Hdlp(}aPImr;om9N_I5>3n6+NrSZ z0g$RGhfx8#s~!rS6&wVKf}*O=N(yZ!gBXzsp_9&~jHGx!3?RR>E)O0-;fYmdI7r(PO7L!6 zD*UBdSUxzt#eyJX=?zuuU)_+eO7v=KC8j4K!nSW)0%n?w1eOi2YFQ9LhS~h4X$FpC z>#lNgG*==RmSFLw;WZE)ayY3V-jAD=7tYnd#6YCmP><|-e#Ru)e1q8^BhiDkKRnmw z7$JVbj4o@5OE>D9JF;@FuIe|8vUm7`_>h4Pvyv;9_8w;ABDC-Z*H%f(HL3KKZm9tK zmIyXGPHMZgo3&GF%s4rDYmEQ_NL8?2g$YWFVJ0i3N6$}7?+FPxH9Y08hE?VJb8Et? zo_B#7kknmp5r={;*m)qI$&_y3U8K?ARl2-rm-ZP5^7A~*HBg)djQFb_~ONj z^2GLVYuo5W!wP}i65mHjwQ%xKx&@#y1Tsih7bO}p4I~3I@T!2ZQ3WHjBJR5BO5aaG<3xx?H!@FNR$#i>^$aBU z_W@+J)lBuG&*ClK+OV)Nrc#oE#6aWV<&&Ui}$?EOJRrowY<(ZI-wtt&XSAaVYa%2A?ykOP{nny0rJywFeLx zpPr^wlh`Un7H`vW;bILy)@aCqCmyuOQf#rRWa8A6OlSeluz0}aEbX#I&rxLiyh|v# z+jPpBk|rM~;AUO|sVnVBxz_pKgIEVZu+;>RjAgO~MmPm_moi&H?^5;PSK9D`=$C_* zFJ82_hljUYsa`Ci!g+TYK#pCxen=z*N<8{O^{Yb{Khb%QvIF=>t^#1SqBjtvU~4LRlD zK^1updICp?v1GsrrXJA9nBs_=;T1O$2f7HVImAF)?Mk&KtKx1~YBnI_EO0d1GMk@h zSMLnRwvFC+KX1J?10b)DBC;78Vit%VWw^73k26f{eFLO#0 zmP!{HTiD;zT<$boD^a|2HGl{tZ>`3=(tih^$6?~TJ4GDfO;(`-ovy^yv?mSAIiC_T zo=+)T!+r|7L5!$%f(ZISH8M#7Wf{ou2aswuUAKV`Izk0kTAjlLTDj0<@CZWkx*)D) zFJ9t>U?SiIlOIE{IGLlt zggY>hyZkbD8Wyvw>zty^Ejfh{K0wKc#w(aj7haL{Ln6H4 z#jzD2xgXt6?3cHBc}wpYIR+iMFtl_y1gXBENV%kTAaV^NbtCm8p&U|Q0LjkaxZ%U~ zyE%|!5~9nlPu8E~%;7i^uGC0OiZ()M_%x7bYYK*%5e>OmBA}LR&~~bLZZO?*ZaIp7 zM3OBjBH8$!j4I#}MX~)DFPmIm>t)yzFaGNG#LcYnm4YTSt$|mj)8 zb@3^;h((*R;XBRkRIh-%2Ot?Fa%1tAa2~Hq*?~)UIlg^ed>;j6CNrdF9x|D;y{A!o zdLTy})#Q*Ar<_vPW_nxluwY~wD8^U2PMTqJSDH^s%Dika!&9x=GOFe9z8`vyw9@c< zHV7V=31My^gobkTp$-EpF1Cdg1bX3??bsH+Oqz7gUByU`tZaH^x7nZ_7jXwQn}sr$ zInB=Ic|`^x-QSNkx3^o{sa7vqsCfDz+6&B%vqh5e;SF$;DO}kn012n_Lq_h9k3$_g zlbO!mc;3zpkdOl--$PjWu=7 z4ft>b;oJo5>8T=KX(+(xFG}u~qcoDi1)K;Y1HfqdcG88tQ9zpD9zZL&Wqj3W;UP02 zT;^0L%#w#~BU+K0p#f$y*4l0v+r0wv7k|0%_JZm(1&$nCs@IDyKru79h%Lz9NUj0cm^kV zHRQQ-?xd$}>OyYLhxNwaiQ9-`@Q3+(fE$iLTxjYkGy}#kZcBEXw_3C|EtmUd37uOm z1I(t^YAS=n>k0U18d79?5Xr@{LtY=E>W7=sAPE##c2-uTRb_~9 zsw~is0&?VR{b3vADnY>FY1Ujg@QV-r*K?MmEA7g+cuxqz7BGoLtJPIi?Z?yh@m=1% z>DVCS@bGZjw5#VPqCks@A3{$}3)}@!z$y1~#^o+Llkp70YWhHS%m#|431+sGbVExv zn;<0D-8D_{(I^;|XghBhg}iu?mHl}D*`BEKUYyi+FIJ<4=i|jOUTpJ;N{)w_vz<+1 zd80mgdq?2By)(oI%dwN0OY4ckxl~=4Xvne2k*rMHjVb1KO46-dT8*oe7+3}jm?a~2 zCJnIeHLokV+zwX*9`1`T-N*mL12_wpggv@e7a$TZ)8u2KISX0z|t7 z^$0X+wJFW5Mw6}gUQ;Z1-mOH93Q)}F4F)%}2oY!7&FV0XO{|ANzIE;2FTB0<_S;ft zpvhU;Tp^T2fspGiR*D&XK_oLflRG1mBYPjsPDrM!ltx%`p%uj*cWGDRGQ$d;7)*GG z{sJwRmaCh)EUB_SVEVotuT4+$&eybUSHpxZOb`^Kjo5|deqGaZB`Al0DIT3$%_#ZR zh7`7f05%DRiMgM^a%H^~r-;R}un#iNf}*C7B$zk`-r&cTLbTjY!TpJ~?)*`AEB!y` zudckk@X6cZ%~dyUY;NvsuB>p}W@YDgQ4r-G7T%7UsZXkIi`fR#iKPt($cbwx5g#HJ z0a9$vs!$B_2gy85$9ZW3yi1W;--f+4g_YC1rc~u}EI3bHN3?3dI51%4%N7di6FN+I z1D@p+hZV{bj2`JMS=9+iN8(@b8lLGI5>{&k3A#0pl~-3)U=1n77RX8?+AJI5N2Zp0 z$;h|<&%arKaDVcpR1smhR{-P(XKce?DT-j%WuFb8oTMS&89DoKHao>u<*Duz<2~uS zEztpFZIGFXk_^59(ts?}5$asb4NCRO^v#&>!%pymFTv45oNW=D+v62B|PEzWyO!7)jvRyEmsbrgcHP~HYEFl8#g|vlaVW(lTD7^(DDrJNMs|vIZXH2-iP}IOPXj^9K!Zxfvv=- zk85LKhe1`4)nX4%P?Ay0T1{?t)|0&CCYiKtU!oJ6b8e0en*z~|3#g{1vwcTrC_{Ei zpbhk(D45!5NZGJ~2nIBT&mLseZXfX{T`kF(Sm{KQ#uhFA`h>~difmuqHd3jX9);Q$ z=Rf}2w?Fv=P>O8B&}?pYIuL9!vc99DZ8@3A+nJS}OM4$a%$lrBsSKh-E_}<>js%R1 z3E7nlhe&DzY5Fd&i_(_Q@n74YJ_m|{371+0eF2P%a=$eo$w^^;0*a2*69UgtXu8=d z{#+H>(aTCpq{o~iBs%MbxgcgUpdX1M9~V$oo;{ndJmZoTBAGH$TfK@9U!VW=QUW3=ILIJY-+h*;likq!UUPq?Grtwh|%B`c?Qxmc1bsgq@-m z9iQ682r#)AGksnQn>6v?3|qpsMrPIGu;jLN(Tf5Z2e3?<@#mHnu%kk{_RJ)Fn~RQ8 zc0Xz=Wszqcn<;Y0be^}tHpG5XQ;__`ELM#$zXdfSXqO3KPPu0PW5Cu@xZ_OhdQwWD;V|_qEHaI= zN5z=sK+T1rW>~Lg>`wxlb z$O$C+y&;9A6mH;=e^n?UE(Sy7Ci_x}Ar1~3@*G=mGxgPoBma7va;Ax^zH7G)mKUos+X85b_di z4ohCwLrO~RVKQ?v*mfK^9l#INv1_I2k~E64V-#K?YzEK@58F`eX$rT^C=VeEu(Zw4 zUMJNt>3+js;b%)4e55;t_pa%*-`v{ca%RR=hC?Kme^K;n(oG5-mVPe*0@(eN!&qlS}!iRwfQzJ6K1}hPYr>AY-go%d3Xs0Rv(ya3uWABZLycSCD>WrT%HqcvM@ixIbBL@NM0UrCIQ zM?l4czJr6suLIHYpCBcHp@qufo# z+U)yu(v}G<<2IWaC9i8UQvs!Dv+^)a>BSGH#e?JH z<(dAT%+!4I`R{#WIGuw?DzScY^vjdX(WSGK%t|Iykk`W%ER&O+ucFoTMkxmyJB?>L zZnBFEZ~b(Qg@!bPP;;SsVjLubb-mUa=9^I=ANlGmv-^OXahJN9|J=Q^!Eg!oX@Th z$+=_v8hPd2r6biHSs$uPQSrJcN$SZj5@s4ml2&44034khhR}Xs63!azIlcst#EUS- zi8%0*Vu9_S+vo6pY_e4pfn%vj1yA%MFOSZ%P23b9x!}yzn9L1=mdDsd=7y6t#M^Xb z8xl@aZLYui z&6r<<=0oVBCrMcR39yk#Lo=Y_w4?%PR;eUmg?K57kkC^bq+qIjpFk2oWpg!AnDM}Z zD~?}**HV1}t)$5~$8{AU+htiVb^r0>$M^3af4UgizTB&#^}piui*TPjNkl(ir(iRe z#?cT_lWe^hl1`SRr_GH=>8j}kwrUJb z1|(c3jwbwe4Wu*Ec1$p$g%KV33XiOQVP)Ky--7pPN@Sw}#CcetMgu`1q&oU||Nebo z2ox6&;3<9&=K243mamL0jE$ZgJbt>qzRq}?KxSmDfzIS4_IX#T*X(kt!cW&#w+WvTU(4YAzQVYGGs}BVN>s%#(|uho>0`C3VDon%5||x67%- zk>VwX60O(aW^kNpqs>HhGMyG(>1uJ_c0vBCkKm>Ykwjj%6WgLZ*+5PXtT3^u-=hz zhGLQR?(4tX4K#V_i)hOdcW0U@SzBUl$F3!lH95Xt6N_C_pY$|iKB?~mzp4j0ud+aJ zG4aNXh4e$2%tE+&QaL}m=D$7?$>=g1i$CqMY6v$Gb~%!YMD8Eo-#Y$>-c?q6bnY%6 zk8%*cGq>KkKvFKoIGhw1Z`X&8zN(y4?FEwfk*F?U9Ig9Au@>T&^kpoD$fj#G=5~WB z6eEmGqhTtWp!Q)eue0V*zpN z-k&CjJnw$C1?dKo#RD~FTofkC=*@e4!2g-GSt#&S{m+v!z8wd2p-AJI1eBERxQI1l zao=HoPh$cvRaT^{3Q2%SAhQL}B89<7UjjtF*Iwr?h+;nb>e;jX{kD-ZROol_+7IU` z{vqexep(zz_uP|%^(PST`jCu_$V~)8+@OD!aP72{W`$2iHONd_6+oHPg^eVnUsGwE zID;H^st`h16D1QJo~bk>39V(N12W80K#)aEYPvGszWC;qXD?nnYq$459gHj<^c~Oq zDOr}UIam9^$+3lcv2!qZaMURhJ@#O*J?*`r_v#~i361d?OAAsC5KC-o%;exXttACi zfFf&|X(2eq5rC1zvPC3my;UEx$27Ri7wMr{_Jx>9QIwM6X8CWvGCN=1-+%mg@zcJa z^|Y}69iFS79RtZRaPr__or@S0luAQhe-EO~#A!HN5E@Pa*GNioQr7iwR3-}p)860z6p>5n&R_Rpo!R$w z?%pGkheMl32M1jJ6OJY-HCdlL`@9gkfy_+s48Zkj_U-T2o{tt_gpUU1z!phdk_$kg2!ItXrn5Llpn_<$wD79~zr) zcmCJC#l9;=AbHa1Y^sH)x9R$@XA(N{X`3-0a}_}#+x@FB-c{ap%H?hoVpYU4!{KR^ z)qCz*XE&+qsdiCac6+EIwwvV4wMOq;XA#iN8-~HnGDg9OMiR$~#8zti&ef~$|7ly7 z-*eu*wtU<-R~$Or++5jIZEEcElX8O;WE(BFV3At1+^i-NGS2x{2^n6R5)#@%9Zt@^*dY>}j+~ZFf%}~A26%fqw{|M&kp8BM>mU*=@7)*$}O*-49*%xv+A}~XG0}#( zrq{^2g@$kv)Z^+m?iFSOOVt`~G+WRJ>=6}@(a)=*D+{aumG^QLvh(fP&l4zc+vrqXDd6(kEHp&UB7+u%klkoA~EZ7?Mz@ z>2MXd%VhA2%%&j42VNp=y z+FJzDlHY+=z;6T2V(jwWtN-DL{!jgG&-?vbi=(5Rxsy&NBZXPevIroJNOZgs%}a8D zX)ompx;i`c;(pxLZIhWaKc;itktu~61*DtTl1YONCAe0}DTGO0v6Rx=2s4J2J6m&{*Wq$g8< zVenU(O?>|J;9zhua%bkJ?I`|%=lOEqXy@zKL=V)+m?`-63@N>gp3Q;qquOy#Wv5HFN7zRg!662k| zxhs8JKmN>Yug|sPzEK$Nk-bZo&i2M(KsaAuN~ejNhuzLfCa01ojP$n;4(9sqy^)L# zmr4Kt0X0cPK~!GOH=UW|(Yd3O&feMCrM(XmMxMRhS=TF2FU}jBgl@yOjI&C>yyEs} zf3_{Z|GYcXcjf4)vo~_Kx0k3G`K9AA^Y7clw|I{LdhM^SOGjZ*;ShIXQb;Zjq7m^FXm2O-1jgQg{Bg|JuiYM)40k z(333Db_SzOxnj8-joyEJKec`5`Nx0rL;rvKk)4?V zpZELWPmUf%QrnTOt!3!M?|1TN a_Icc5TG{{```OfpcoM*I`UNnQ~2le&dp2MMO0$kY-1Thr^_h zqlbFSlYXXNKP-}n#(;wDtblWCT3LsIa#chvAk3(YesaOPl5S2iC4F;g zzL$4{h4tREj);u*YdtH|!N;a@Q~37yi*?DQc2k#fjjMHA(b@TZZnd_a#$z-bo^nxp zR7Hi_@>>j8p`4O##I00(qQO+^RN0R#mJ9O5X9jsO5407*naRCt`t znhQYF_qqNt#alI6inR^`m0J^>K>dq&8ey2F9UYU(<;dEThH*%sUFDAk17bN81#ya6 zJj|3)LZ*vUg@Hi>#LKjhDR`OCLc~#~nTSk+4WyWyluOU^{(`l4X}7lLP4Y{kwS0Kq z_kG^?8*p;^&SUQE_YS|bc++?Pg}z-pwrSHo#Om7GbXNG{&;H%q9|q~u1n(W*w6xZ_ zw${VLBb|kdhp~J5Xa0+qR?q6*_udag_ZSkpZ(ps)QjeNjw`x3U{A*aOnwsovqx1CH zpJALnebyt7&)WCc_lh_B!^4|ad(?WYu1T-)@L<(;*Lu|S*KA*1Q&Y1Slt$<7mp=8q zEUtB)HEa5;?&*&#T>L#G9@-s_Uc7qsYUDIK+dsXAkuIG`SJ&*#-pj=58Y64E$Nhiw z($ZPe5lKnYFU*?$_~Rdbw|JY-6&|%vY9u=ylr{c%u+nS%{cDhD52%<88EaTIEF;Ug z`~E-O!O^>FK7Ivlci=~9oy;EMLoPR}OY07+cwHH?su6rdEh z=;Ci=bx;5Q_x=yF7dtzHa1mi%xNxDA6#dn|ChKz_d++e!O^bDm7N9_fU~N@t~0zA5eWw#J`Ti^r$hdsD4*l7sS-fS^ei z9{)C;H20$qpFRBg(xrc1dgRDRb~Y5j#h+j(!~W^f;7#|S)JUwXwGJ?6Ty%4c>ZRv%D-vo2+hwr`j`qCv!j+|Yxcx2=V z;Ewd8Inch;5w$OJ^8KXsSgPwU7rOKWXo!fSis(q`Hh)OPFgT z9Fg?rwF@7g9NtTdsqhwWI(v4>k&(0gBO}bEe+fYnTy`{qx`EQ^&w_YVl8lNNfVCO; zL;pT-8C`S4E^IF?olr9(DI#L+pP}Zjm+t%Z*8v`0 zyzlXSAY5{GN&gZc0`Ca;`j=#9FX=zBWP;H@J0sm6nCa=!*$fKH2rd>YJ=|zCrn8_V ziZY!3#Au`fV7i?kogMAH$peg~rOrvE1bN}gg)3JgB5nZn#*JeDU3=`>eWk+ECu64K+3a-yWkt?--{+C%Yo;B)L6qL8#y(y!u^eelwmZ23uLH6Y!u-H zZA>?M;ICK=QEMPU-9@KI`x}iJ;ebFjW)P*3*yufj2sV-x z0lmAsU#Bngk_WnGx-&UtDIg&w5U(KF$BrTC*NE}W+i%|f)kjZV{SwIe|Nj3ry$-Jb zx_I#uDG4%r2_+gu*`MvAV8z-oE|9_AA@3Tq&K&I^Y{P(~A=&x`8;){?!$p&Qq-HT`Y zL3tO1D8-TNyQoU07NZ-Z86C4h7tPpBbOwTwYGj0?H_VH07GdJTBpRF zEjl9|Hxh3mGBOy(a8R*)7)Itb)hoUspKh!eOKVxsvAf%O`-&B%6qIh{yp$l5*1|P` zd2H?V>$l&06OhMl-~Qb0vG^y8!1$?p>|e4Mko_YN(Z3N7e{>HybRUEPVn#Z!GQvqV zA3(LWg=d8O_&9id!rcMM+?b$Z?VkSFW3}J|V>grpq%-6M;_B_C5f>sNXf#4dQuA@r z@%r^QZ>+uj+-D)@{$$aLk;O~?3uQ@(zPsdZHj!rgk2Ll(6$vHDr;V^k7KDU4$akC3 zcwb2$A0MJ(P<hA5(ar@rw+nIb*dowK_fz%`8sLRLSb|m}S z+Uq}m>j@{PdGi+j@u>;QMT<(|ic7PZqGXzy{CtE43|gkoKbkrl62hZd#y%rchJbAI z@o@(dglr@Im~68gDR<=FNS)6L2PAeOlp~T&oG8W8?M(Nee{f@z1SDDIv1`|^?Z1BQ z+Vv+1Y~HeE!CMx-#z0PAbm8pc#YY%(B))#=I1+t?5TpGY`x^Ub%up>-rpX|~>7H>< z$TpIYr{eUPVDuqILMHw;d|&7sKz4VtZgqFxTCw8R6>1!Z4V5OrguwSW z3Uh7Ju?R5UxDMKD9~?Tp6J(EUD=f_E-Lh~RigM9(kFzwl?>hP>8~#Yi?xP12(bO&( z84mSAs88@Z#D__CxR1LJ!`SW9&0<~v&*<(>E!AcWp@PLg>}IiUQFO1kwVhIq)=8o! zr^2M2k{Uc>?Tte>964XUWf_y?N2atE);FEr@_VPbk33Q{LL;!B65ZI}covY^(IXil z8Q5z2NK3|fLP*N+wvY@`uPwvplW?E7cKh6eBzfK2s9u?_>h8udl2v;PN^ZZ!xOpkl z=43$;Nf#m@;~~b)uirpDA`Ts&7&Si5f39YSpB zm>PY3A(Z0~($dG#%sv@DkkZE|cLLJg-9ZXUccunF*#=Zn9NC7*ICPMj-M6|)$kNgk zWa<}aTf+lJY8~$(#O5qAt+w4s1SVG7<;1cr5oasmXz=n zEXp?~57wbl9Cg+v-AKCe@{MCJKeyxKlfSuhAXdsN-!SvOL;iZowvAqHIoBe7vMBxR z#fuDLbTp)lhKG{@Vb3*Y^yMMlA?Z|xAk1(WV_R+r(UFw#@uVX-yWNQsX{L9%Oh089 z^ZG(U$VDOJ-W4p|Zr!><9@I^X8)QVcR2_Zc&dHN^?(9gG7_w3lx)QcLH0{$9amtja zsIsy{NwaERZ|px3-S3Yy_rr5&&@`f{(b#BXbPfa9))x{!QH;55Z46{C0$lO#Bqc%O z5+6^{;qJM)sFF5gULM&ys~d#QteWk$=GcUF8@%OCzpM z(U*6f-*Egh)8@q~gM*1>WeL~zuACoogj&4czrPVh2_^gb{4@L+8T0a}9?>jqdAV(r z=C(Ev(LG6RT!++4WyUm>ul0^>6TD={Y6<6RjFTe2a$Im?T z#xv_GD@zK-*fMpCn8)LlC$yix`netG>XgC6#IijJ5lccgp`r6=e{}2=P!Zc>9{@AZ z&`fgM+VIGwXLmgL(98JU0Ev%}cR)Ix@HQ6@{EL{}czi2(RqInrci^qqyZ83b?>_m$ zoo5ht)>Rf1jE!YV)jR+m>f!NDl%L;n_5IK7X-*pk-3ABC%DS%qnKqvzFmYyqgpPTX z>gbgysyvYTP-zijTMYFJL_#rAxH}&1@eHK90}_!Bw*1_p{P>XgeS`@yYx*y~{LXXl z?s(%*Z@lr$oyvl-PWITCEHhK=>njlW@^~%2ywLOI=NEpS(dmicZBdC3)80az(-@td z-OreK9tFH7FD5T9H!mc_k>1?g-MP%QjZrI?Scoq_{yrTgJ2yT*AAc5KR8%y7Q+)jV zm^|a&y%(Q+@|9=a0N=WG=Q_v6*z8QUMkCWu`0{4@3dFpQ^XEN1uYO@KNAoP(R+yOB zuxC%t%NOsW7PB3j#Sw-w1h>45n7o(}pO6>_WiFHC`1qn+hhX^=YTgfbMn~N8V`7Tt zuiuo{*tq7&cR&6+_)hMq90MQj9CnM!s%qguLa@8VLF zw`R?nRquZMm%qPpa^2;EP7a5QV53O{S>~bTzAe52Utix7CqU?_A5H^gdwb}YF*-RN z+PJMLF>%nXtit?{i+MQ5^!1%}WPIhy7y$O=#)Qm|kHHNJq7RTnw8idx24^=t=jVTl zk$DHi`Ri9b`y~4JncwVK=gMV+k2A*M)^UZxA%RY%QkhjM>j@sOqodr@bNEELXGN&K zxw(Q_EWRurDjcL9cZ+N`-@WUx$=}#_b_9&kfLs|86VsNLw|jR?ZcKdc?%iZM@w?Fu zx%re&-0_Ctns`eZy5z51^X!LD{O#`$@PNX^*2o~CiKBpu9En7$6Y9)nDKJ}7$~^%$ zJnT8_>6y^nUeVmue)`;8P=PVur3hFq`3^q1z5eV+sKCGMxSYyngN?&6ab*D<6T&je=WB)D zTCG_J$P~RESuXeN8Xg`_NN8_vZ*ESo9sdeOC#SbJZj6eG^74v|6rFhUVjhM>bb4Rk zN^~~zy&HW~1R0@SkxLOIjL!$7OHtA8TvCw$T_z}9PObm&qqjc(7&ESptr+97k!6lr ztqzd;A%=!VNB#UpN0pW=tGGw6PfLS_o}OI^UCjw}OR(GR8@@_N#PN+Aw?#!YBu2TF zrQK+3H1@&QSE6xZ@}S`S-MNf%Nav{{s!PZP+*9B>=)%N72J-N;Z@u#FuFI}6u}lL4 z977p$xE8s@+k2EBH|iG`x8TtQ-hO8UYHM`|#PjUx>H?!@MHe6wpkhUWeZ!Z1d~v@l zw{6=N)tZ>-R#s*|MwDplkPKuPDPCDbRzil7e+mMkSBhL*P8GQv%=d9Qc<|t*gDwYW zKk>w_UGE;aT*)OknaNbg5f1t7_w)1i9t+cIbwn5!=j}($Ebiz4BM8&Fx}aoxMSDAS zb$dd)-L~QFuY+`YXv(&zLYm`lW#!K`Hs+xOL*P2VE1F+4Kc>iKK2vl>`KOAIZI@H0 zPJy#%{hCW}z4h0h{`8g0$#wj5#bZpEII;j!oyo7RuFh{LHa6C~Ds@4eUqFCB06uhc zN=k~pYdE2cKr0ZQcqD+*-roCl*+x7yI5=1s6@{xC`-SHKNr-6e80r#A??hKX!&5H1 zi%y+_gzIPj^{r<<-sQ@XaXIX<*w`wq4oxvC6w2fLfM_z2e%dhH%xW1BeZ|!sC&&*$ zm(W$wg(M^46&0l820U!RU*$jdVjdPEFqS3GN!_vF&Ak{hs7hq^)XGyuxVg+n*&U=J z%s+S#9ys@-znt84U_ok~iO*4tbHJR38AJ|K(Yky_}6 z0)&dE09iEu+1YRX^)EZ(G%_|^MQ$>2EE1(QR_V(J%h`?#I{=O3=Epx;l=@549!~3c==0fZ8fN=+&qI4U&@5QO&DP=}1AVfE6kR zr7;aNA!9QjyU^VZ$chc8KmS?U7vgwuPELbYRD+w_U|GR4JMOMriDF!{9+0Olo!Ux0 z?6T^kS56)%>Ff;Zlt=-|F$@Xhau}cAC>+o`P8&BGrc*&ZAa!&w7f=!*g}x(YcsQ*K z6`9uTfP|9GRFc#*XdAoj^n)0kUTm%3;MI^wbvZbv@}%ppR<2yLX8n4Q9=ycF!T8zF z9Y{s@c1m?Zc=IR}^BX0Xj(c>$sGnJ-RwKJTVkQVGXgH-ukFkOX1!brn>4tb|Y2Zt{ zUzG`VvhnuQ?G?cfgnX*rt3DVhk8*>Sfv&qAU9+ZWJw${jUfR0#;s4lraQrD;p?Wd-lmC;V$p@(=pfOU}BboBJ_PLPh6fWzdD!;C*Bq}>BN zolu@|I^p!`)ArNt8y*Phl+)U36N$W{ya=<|^}x?h%|BJN>e3}N&C^dmed*z?@BM8@ za-B*Whx)4u8yh1B^yb5C)EexAVll6}gUS$ydSazG(UB4aHTC*o{jejt7}Bm!NttlE z{B(l7-1p4r{x2LGz8I}u%a$z*4i-fbW>i9^VE(B~t5!k9g9o=h{P3$Y9-jMeyDHg& zGk#|Ckl8!-Mr>GEnAQLTq{W(tLrOKWoHDFO9d?ju>!D){4;Of2iXNLz*F>Nep`?Sd z9Af$oJ$iEi?0&(63<$B&%S+^Cvjuy3k(P;W!|$v*xb@W2mo7cMb?fhD%$Pdk`31?v z+MzQ;C`7HFh8)4Ixu6LfPD z5?abzV!e|ULX+BT9v%I+$tdaN)#@b@ZSX?xxM2qldUETfr~hZ`!G~Xcc=n8`Q>Xs+ z%(%gdBa=BR3l_~w5t9Wnk&=QVGg+Y+>Be)9xF@AUoT7*E01}7kAoyb!rCc9W9z=FN zCn2cwMnTDD7^=5YE3xRpq}tT4pNSKY^~7m|t2Q(=HMo_{+5e-5fA{LcPyg=K88co5 zRplEEmR8tkEIP?sn3PrwC^eI7U(9d(Y9BEDq3hK!$ zsXS15ZgWX&n93~GNi0&GPAZLibTW{wt)R?l_pGotG(^@99W39fVF6lLc8p7}NvbP9q)=;V;X{BFnqV%Y%Aa!j2vQ;JK1m zAWF4asw}lZnq^gGsunzO;Giaf*whf^mD79LZclWJbc?e2Jx|35J|Vql z2DJg0Sy`Q#DwW^$VX96;dC`i1>gv#k5-_<9 z+^2{BJQUnG`Jth!@9{BWBJJXou$!9?Y~EY~x*JthVX?9BS&jiAWviuGS<*~EdQSp! z8Ra@h6e$WM%D~7-*rn%x&$y@K)Sq>vp#b&6lwI^hXlOMZm#e{vCh6$mQTXyE8k+c! z^R&*Opw6DKo0oSTsD#ZZNJWhIFlAVnL?Rh8WJ!T2HK?<)S~8{H*Cqj3SlHT-*nnCL z3=Fh+ZivK|-u(V=Kgsrw>}f%1@MWg_^r4~XhYo#D5Bav3r$>>7QqT<4cQE-zId&Fo zK5*y2ft&oB1F5FC)YR000Y#OTc95_zDHGV_GP5$p*3Qf@?+0y@Oid1=D03RzB1NJ= zTd1$+oH94J^FN*X+X>DWQ_`A|Zn!lpC52c*dEySH!{TzRhY z+|6_6&J8FO#at#h5{pEq1)vs~IP9{eYIT+xYA#={@*X`inVM{EWfBfUx7%!bUTAZm zo5=RcJ;)8s&Dd*j2a=xB%HxF+BYpIVXU*`i*mI6vW2$q#e0j$X*Gi%z5fvp0g+fA~ zut>NZj)WtWvDs2Kg)jh!Diwy8%4}76drty#Bcz0jdqv{>EwTl+L&`aG+#-2@0^*FR z&;QA{IjwnkxEfOin6J9kKJzX?d=6+U|{pE z-%g$J>i>KY$S8?Jy`zV*HljRJnuql=zUvOx0|$~_U2i74-c-~j*AZX|F)E6Y>VlGj zVu{5vE*Tq>O2)$IoTgGqRq$~GY+R+v%#^Bpt!C|+ul;1e5nDDEwkDDXHF)*zfmP<% z_UzdM%1Gb;{N0R)pSI1Ru4&fmvGNbo0y~`2oDw8cth=*o2lAT=!emt7z|nz$Vr06Y zm;I+Ff6L`sr1t=TO~IfpJP>)>c3UqP0|V#Gsrc#K|5&vrCBY6QL-ox;K^ksx<(+kxcc4X*lU*x;nG7_=I37%< z3&t>;(J#n0S0agJhM*)2NAoel)aeX5l}?3h8?t0Fbyj9)W)Ds!Rc9tqlXC|d4{9xJ zYO3$Gp>HZ|WlfkqbJ9dRpMUF+J2Bl41a! z17m@5e0*Fvt{5LLmJ|cBc$|30!gNw*X2Zr|MVXmYk(rq-Sbm2l0qIn~j2K(%6PwUF zkg_+YNfa3w85pP!jeF-tS9?XdReE$CGQ19mm6g=TuF0tk3l1*aq(HSfjV8Se=GO1LS3EV8PO6xtD#&HTpQBou(Ld%@qoL(FD4N)&P z4!yTZCGc}~-LYfc<;u&Iu9YQ~H_xFA2a=g-Ohu?r98FfF;zbqMARn+e+=(&sNnqj# zg$8E(v2fT1yoQ(>nJ-Qr0wsUu*MFqu2pWs6gNd!6Y;6E!dwZ{!C?~i+GO)9tvy-2? z08L!UFjiIqtss@jGR>5u3Pn|wf~(|HR4w2u`AV*YQ_Sac6%PG4Ts9j+5DySqBnFGr zkfp)6$;y&xG%|tASLQ1e`c8W=qf_v*jQ|89Vi4T}P7&PQYtNjtv2#Zsb-hW9fUIQd zkS6j#GUnbn#&{G}3Wusysi_N6<5Csle8-HT3!hYE6s1HnHEDrIs%b~d%GAhYGEJ66 zzI-xDF2i6fbYKn^4mLE{_cVCrh;nku_Uy?#SGjrfO%x&K@c=PW7h~`xGwlq>@z^RQ z7h?0{`1}Q_3RI(fBHfbv8MkmGY^gQVie90<5n#2*5?H~QsnKLww1P>jYEJWrvZ0lP z#G=uVxW~RnL~_C{dlZ$>&e6xvi%}263||SFx@ufWO7ejj$CtGJE?0rlkX!#f;*tVSgB$j7%p4|q?OCLa;0KisWeEGN~MIa5Gv(rwgFR+axJ7x zOVyNgAR^D!Oc^LUb+O*ONibqAwlYo$LVz|z5~E0D>lFpstCf{Esr1g3C`u^L1H@T} z-3D%hDP4@mcmZuXG_1j=jKyjTSHzAhu?XcBj*w#!8iW?1P-igUjgVFh84SouxfXYJ z_NdMb2_^%1oFEw!DvU}@#G$q!G7`IGugD%atW3U{TtWe_;ZqrM>&^|-(KcHZTT)dF z!yYd{gA{Y&hYaJWL>ucZ8P!UloDdfw2aY2Y4#9RvR=A`TE4eH))2hK->}*kan`b^4 z(aCA&#${1aNH^*+Dhg``j2y=@5p$RbSddI6NPei`)B98Rp#<3VMsnyibNIcw!O-)V{M=TJuwD7F^{U)X4iyM~}dU+*A zfwa{t%FD|w(h*)Zk*9jWqp1_7O>U^DLm;)#tg5(5NwpV?$xYAD4OfXo8wOKX8aN!G zP_5<|22#FhV4I#adaZ$4` zj(P(M&_OOapUdah!Kugj<4R;2om{0*(r5ayT4k&R&!ALVbQXjG#|0t!1%yJX%6nSs zhJ*Ef*;?B`vATmS$E8%ab#RA~YubvT`HE3D%RIhSU2 zaw<(`E-hwsQsH91VV=1r<*3q|FCWzkX((dc&=Z$~zaa&M(2{-z5HO@Ro7FfpsLjGG zfxs6dQ6SJ{wpg`V^W-3%p4wPAWe^i*&mQu~2I!bWFPb8o#i%|co7l#yl;iNnaY>kj z%LdFi9jZ+7I?NimR?d_qJO`mrLO&gj+FDwCw78+oancshER-SVYMENC(Wqrs4Nec< zLz5Xe6wccQ2PxrAm^Mv0O=xj6c^OV-;@GMZbTanZD)b7RyO^wIV)8*Qlb@v<59)+@h0E^Qh@Wi!}{qj$5Y2Dp)==snN~EqHTqPgS7VA z0NFsTO$Xej=4v4}LTXpVcr3L_?0Bq1VNr6pCI@8z$22O(mq+<=NJrQrht07lEwuB% zYJe}4a%dILk~*A7O_P#Mn4KE2RUlZtd@@tf5x?9x1lSD~I?I-k;TeuQA=AvX`<)I}FHYCIWHcP+(81SS5q68hO zNh=UMXy(kFFHU>lkD5M-*QRXT)>@yFV{h-Z2f~0N$$}#1WGM|w4u?-B8OK*z(7zVY zm{QRp90@qt0Resi^g}$_Xoc7y*$|S=HVF+pf+)Y*h)HstoE>4bgTUg*rwhtBVu#7`YQ9}g{C zc7Az9dvCj)oSa6FDAMN3$9AKP;}R4(Iekuomx#F3aX2Be4IMemFN84X1jxwd1R$MK z>=kSdgoKd?2w*<+lRcP^uVCiY^H+~gz7Rhf!CpB$UVB1sZ*NnB$k951)&+d6g^>76 z9dhVmGS#KV$xU)4D$ybzrI*|~)F?rsBiJ&vj3Z@Z2ZDy=`!Cb5N5}m^_jCD!$Ny0m4^ugmHE9A-Nw>!4$D{k?}xEi-{&6 z8&udNNXW4cJdeA3K9{a?X3&Z3OAXe2iW; ztQ|Fg$;9WzIeu@!&t$SF}WQ31vr0Me&JkzGSbGt!M&R~Jx6zq){ABkgExG>cX%#89E&V0{RX zsRLv(4x6cnfB?=IJTqWO0H2w8=+GB`@97Dg%~DiULl;O z4xo;r#LVVsEtq;b29krh2l+U_*Jx0e0cdLu8|$rrclGMky=19cWQ96tNA#WqET@O1VJ4XA`xjKDs@`3&Y;uLk$}bu)7-Eg$2nUj>+G?%cy4)U z5)hv|HuD?(>x%ax_Y|4zCJOrsjLYNZuixhEsTzE8|}m= zj0Q7_sEw6tLC?l!6TpErvgs2utANLwIdl1=fA|*D&22nh zsINeVokgS3$kynNiQ~bx zW=|U+>-++6*kT~-r~=g>l)(ylg8kE8`Cg0gOJZlR-Lu&y(gy?x0_vb8B!qb6HL?!< z`KxDM`ynO#B#zr`p3Q-tf&gq=CdLV=i+z1VADVakhgR@@T;0&yY;Wetq&V`S05w(( z{3tZ^%=h2NUlTvtVzc*pVztJqt;)2j4jnqa^9OL62gHvbsu$T+^kJga*B6caf1?b) z5c6!d<;%^>moI<+J6vi0>G!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/busybox3.jpg b/busybox/docs/busybox.net/images/busybox3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0fab84cf9d0c4236d9b2167653a9cc1486a35447 GIT binary patch literal 3292 zcmb7FX*3&Zw@#?3b4pDWT0^BIlq!nqN6(2838JQ?rK+ihqA|5%x<{*3r9O$FH69_- zP*FlBLQAzDF~=A~3>6wv4WUxQb=JLib)7$Vec$u`*?T>EKWn|u-tT&M26n~(hwb3D zZ~zbp1la5zz|J;6;bJh(I}~^0_+5PHt>c$(;f~wbA{}>z02cw$l9E!A64FvqQZh2q zd-uy7*uQVzex)O_hvdM@YN}vm6_w+fdfLa;p&BYG5R+5T(*{OHMrzvU&YK!q=$$b# z{7VEVBO|ka-+sjd2NVrYsGKnTkFoOs0Fnk|0hYvp>HskiP#gr@`2U3$*|L(*`=xb15> z(;I4q`eZ}j4p1uwU)4`E2J0pXb7Ank^*K&nlV$bILRfRReIyQsV2ZnRo30>wBJjiP z)>%tIN>|Lc*pnP3I02oMMp;K%6#G=)WYP(azz@F$B6%jkhmNqS`KjusAW))k+c7*f z*=M(|GI)-x^!&pZDg;3aH29jP_Le^ZPHg=izj)Z+gqS?f8ku*ui+4MuCDlS&*J#oq z<*notZDN~e`lkc1iKV05%gzN6sI%AmpPE>(zAzKj{bdhZk&?uJ_p_(;jghf((X#qw zj@hT;9@Mm4tAsoXPAP!fmL=XL&4s)}EhpoQTwu*w!@;)}LjuRPZYsMS6ok-W-;ky@ zO;vb>v(sTA=$E6oQ^w1vq7o}qR!~NO@e@B&xM`ytt86U?)%iiQOWwM(QcE?L?}qFb zg7pw>j1su+_i5d)YXK?y zI{)Xac-~t22csW}EttG~(~ zqAyKn2sHH3Yc$lAu4q;J6u(XBv8hkWE{AN&dLepw-Cp$8)nvcd`C9VBGj*?GD}o&Q zuzQ;H`DNhh=3^;GcyKfM06n~?qst3+mi`R!I;3qt)GR zO};gv_|uP?pdWR1e=xLc<1A0W*>2+F?1_i2l|r z(v@zEbrk4tW3~$@253vAW`TU#@(XSVQDDn##=5_CB+wvEDVunWLuSiC;e&e#bb@ED zBDB3I?K^xLl98jLnrDKqFJJ4%}TpnK8})A+mB5 z%&#s-G9dxfNFN8wwT0aO61(*4DQTc{bkTq9%mYpmdC`8u1XGcm$Becn`N=V~CDit0 z+w?tRU%_nL<6s-UMPsMhTbOrh_HPh>I5<=2Y}Ctiv*ZR5&X_HM zU4eLnm*0Yduaa2;p#_`|$%$;{L@WKyJEklJq^dGxy`RfQ5GY+0G<$3Cnvz=|n+}CObU_ENBRg1+uN}3bD z<+q5Xk&6D%dfk%pm)v#3CcDbm$FB-?vaJot-!Ii1FccJy=BqDde@IY0XZ-VD&s_?s zTkM4L%kqeP-$Bl-Qcn!0L3Cu;M(^8$SlRHo`rl}Df(1kG+A!?YhI{er;C9d>R61lL z7h*GSDsLkU?2%Q4M)qhN=Y!yH5|^0i-t2+I6)eqD${|%=MLh<$UWDey*uv6-3?`G3zO!Ey2yw-9Ii6 zwV_naE|UL&3VrHZ|HoxvqhRU_xGmD==%0B@Bmuz_+GE)@F=97`IKA8sig__tf3c&1 z*xR0Ko@f*eaecVD!X(YGdsZ>&IGyy_7b})GDV59X+9cs?Q=ZP!2@! zT>7Q_m9L^om?&Yz*L&=HQPH~ngI8UyTQ@XC3%1SH?o%d=)>lGNX+yWe4&X(W${TE8 zsA{W5+sjdh+weXE;iCSg-baHGs?ruoa9lj(Z!i{j*B|5nQHnqK-wC1g;NshkoAfq%eE4iR7yTkVeLZ#zFP@p}v)yCW)w^n*3pk^QtSUVr5e~q7Ka;hzFl3|ykGy_NY z4!bO1b0(7q2aO-(5yASE0{`|fo!rf|Nl}(+exU&Kvb(aP+{0Itrl#Lf(EC>Q(PAjV zpU!JA61b)jB%tH^vhcm_>%~#)j~TCrc6adj-yD&G&FjYDTwP4>BWUZ*v2;oX$LrZ- z7z^@lQt=*MXfNt>N}8C|%)vA|)J}{P-4U;(pLeq_IE7&oUp@N4OLcD_#|AZsU|do%r>l#C~n;pVShy=VBpJ2$|rkUIe9-|}^4uC@O3Zm#xue*KLEWg6;hDwhh&LAKszd}IBf2mII<9>} z|DH$)=RR3^6uXm*L_URdX+PTb$7w>reCvPeIe%(^8ZQi0KHOc#^ zViR1Kf{-~1ApPBF005}eaP|ZQrQ=9yeByyT0wCyJ7~9Le8X+)qsV#9wUE$CqGHrJN zlIo49`W`tS{O(_!1TrXPLz Rk^g|X{%slljbvx=zX8;`0igf@ literal 0 HcmV?d00001 diff --git a/busybox/docs/busybox.net/images/dir.png b/busybox/docs/busybox.net/images/dir.png new file mode 100644 index 0000000000000000000000000000000000000000..1d633ce4a673207a08d856acdfacb1933c98c3b5 GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz0!VDyh@)w!{DWL$L5Z5#R|NlQT6G+TVGX?^n zu*ETTF(AcQ666=m;PC858jvGa;u=vBoS#-wo>-L1ke-=lRFIdhV5DcFXW$#4rUX^ZoU~&Ke literal 0 HcmV?d00001 diff --git a/busybox/docs/busybox.net/images/donate.png b/busybox/docs/busybox.net/images/donate.png new file mode 100644 index 0000000000000000000000000000000000000000..b55621bb90d526b26582d40f3d8aaf103582cc7f GIT binary patch literal 807 zcmV+?1K9kDP)Fn_U1Uvqp6ZP`)p|HaL`rPBXx7pz4`>i#u!PkeCuGpB9_Tw%FXKO>4SiP#smYUq@@0h0s8v-0~SsZ5D@?W|JBvl%v2FX00001bW%=J z06^y0W&i*H0b)x>L>_VqAS(a>02XvbSaefwW^{L9a%BK#X=XBTZf77eE;KGMO;9Ex z0006;NklSMr|Nj40)iTSf&_JL0qoG43k(4-}PB8-B8U`b>}lcdtt+}Y9@UTbA}W66WWJZ`<7 zJix3V07EDlX$9~}k_$a$WX(ADw$y8X`FnN)ZZX58ZHON*kRw;jOVUnCq)9y1Ipk+c z+4Iod@?LSh)w&2J8Ydbmu4k?F3^YhoJj}QW3n7@Rh;eI)(p?azZjOBb6J5Z%8A3=m zqx5j+?$Vi0Jx(v3$+|hMjr6oy#`$PWh_0*U7m`ztsPOSFRqhg#H{WR)_P1RHrgxQB zb~>Xl7G`si5XhuJa5XxkUN-MJ6wYcbtP?o5hTCTY3G z^lo;iBtMefsZ?*15_9*1^jdcB>xpD?gounJt;11D<%r0pUrBQD#LFyA!y?ziZ?Wi1 l6-%)l$frJ6(?|bl`~kM`8TbW^4#5Bb002ovPDHLkV1fogcE$hz 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..9bad9496aef8c100b351397bdc6f52785a195bb6 GIT binary patch literal 6798 zcmV;98gb=`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=n4s(d-g@2I<5hXXCw8A-FM#yJ9YvXo?&*Pz@`=z$WY2g$Usto9G~7w$H%}j!8O6P zlWW)CeD7=?wnz5wS~Ey#ViYL+<4dosox4z>wq?nYYOO2M>h^KLav@XF>x)j0cY>L* zsorKMm1y6+N9t4CA3_WaiegTXOc?g` zwyl0;sDJB5hoI5<*-t<6MQ7U-5M$5L2Raw8ZnSSrj*JUxhM{=j-~UYtO7c9}+?1W5 zSSl~a;n3V}w?2Wk)Io?FS8iI4U3Lp+PoKTnxxMzkav1i!O7Z69%a?zBawqdw;%NP5 zb86cdo}0(EX-bKJVP(L`X^ACKi{@KrfZ-e6j5`vhLKn5HD zOfUcx0H^-&5Hii4$T1~BtHYZ8iA<4@j8OsrNTT1o{gzwuN2aGdW@Y`j2sB^14$Usk(pd7y zrG;%1_T)s2AmU(CfCL-_pfW%StX^9_zc4+$BWU+6uh#CR9Fy9XExAk;WX@O>KtXOo zp#n(&c?zy`^YkeT8#G5amZi~p|LToGeWcY`Lj~H-0wJZ5v#q=4pi%&tLzb*vx-?oH zih>@pNH>;grW`VnB^G5CW)l1IQlL11lo&9^AOL^>(N9Vx1VRW90>=cU03=8WLV}RMB`BpRMy3rp2Z?|f zu)O^16aR4Ljn@kZw^}Xc+WQ|ka@VK-z}T}J43bD7h++i-lv3jN3wKyy1q9(*twx2C zL@5T?GEBia0B{pqel^j%wip3MNGSvoONjx2$_0SH0Du9E!4k2=5&*R2A6RXa>W-ZT zy$lOVDQN&9fUyDqA&^oSA%qda2rvKuQWy)R6d~GZLm~jA6jBN+q!dvKD}ljqJf3Gb zO_>C7nHg7+8Vsgz1e z^uCYbgKP8v00=mMKw#PqAVel7cOKX`GBP|;sjRK7mul6@%nVG{figgV7`Cy+0MP$6 zA^-raudfRs=!FXx78Vv3&nz&;O0JjZc@o8X;~pWT5GVu)A%&1afD~XUK4{TDGkRZwdy$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-6qHiDbP0l+XkKRKIJp literal 0 HcmV?d00001 diff --git a/busybox/docs/busybox.net/images/vh40.gif b/busybox/docs/busybox.net/images/vh40.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5e9402e73780c2174188f521bdadaf1f5d86e2a GIT binary patch literal 906 zcmV;519kjINk%w1VORhk0Q3L=|Ns9pGc!3kIZ8@OW@cuDgoK%ynas@0nE(K0GXSNG zW_xpHduB7|y<`8(X3Uv0y_`xI7zq0R|1pfU5L$b+*5_lT##)@c7<;Wsl)V6EnS{00 z=kNEuy~dd{W&i*HA^8LW000sIEC2ui09XJY000I5;31A=X`X1Ru52m2?*YtoZQpqA z?0oP4z+mu^fW>miBvQz1I(I~)M5e4(pV%z4Xrx-dI3EPUYm6P^^tgdum(vb2+ZXME z?>67(!-ISR4ts)o41t6^0(*cAABA*WFkX0fdVD*6fMbjiZXXezI}M(H4vdaHb&)h0 z7!@-W8WS8e92gq6xEC`W0Rsmd1vQmvjDVM+t}`3A7~Qwl z9iJZq3lYLSgM!9r$UA?20uB)Yn5B)-kkSC#88jEwG%RSaz=6ZR2>>_@_&}h*rvc}Z zeIVAZUNS(RmeqUqNQxH@tY!fas4L+F4X$ecp#;!@;fwBj{M&06AALj?=fO~{B~$@_94wjH1ccRa009HoNEHtY z)G1w^>KRC20T5ufAbmx8Ak>j%PEy_v?FChUdpn%Rs1Vz!wuMKA;voce*--~+nDI@z zVx?awmIY&-dI}+^hl=V1gQyUSYN)DWA!tXRz6xEeiIE9F3%sK6z^}jtE9|hu7Hce_ g0VJ#JvdlK??6V0(EA6z@R%`7Cwb&_?;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..1bab6b069 --- /dev/null +++ b/busybox/docs/busybox.net/index.html @@ -0,0 +1 @@ + diff --git a/busybox/docs/busybox.net/license.html b/busybox/docs/busybox.net/license.html new file mode 100644 index 000000000..14324f1df --- /dev/null +++ b/busybox/docs/busybox.net/license.html @@ -0,0 +1,135 @@ + + + +

The GPL BusyBox license

+ +There has been some confusion in the past as to exactly what is +required to safely distribute GPL'd software such as BusyBox as +part of a product. To ensure that there is no confusion +whatsoever, this page attempts to summarize what you should do to +ensure you do not accidentally violate the law. + +

+

Complying with the BusyBox license is easy and completely free.

+ +U.S. and International Law protects copyright owners from the unauthorized +reproduction, adaptation, display, distribution, etc of copyright protected +works. Copyright violations (such as shipping BusyBox in a manner contrary to +its license) are subject to severe penalties. The courts can award up to +$150,000 per product shipped without even showing any actual loss by the +copyright holder. Criminal penalties are available for intentional acts +undertaken for purposes of "commercial advantage" or "private financial gain." +In addition, if it comes to my attention that you are violating the BusyBox +license, I will list you on the BusyBox Hall of Shame +webpage. + +

+ +Nobody wants that to happen. Do everyone a favor and don't break the law -- if +you use BusyBox, you must comply with the BusyBox license. + +

+

BusyBox is licensed under the GNU General Public License

+ +BusyBox is licensed under the GNU General Public License , which +is generally just abbreviated as the GPL license, or +just the GPL. +

+Anyone thinking of shipping +BusyBox as part of a product should be familiar with the +licensing terms under which they are allowed to use and +distribute BusyBox. You are advised to take a look over the + +

+to be sure you (and your lawyers) fully understand them. + +

+ +The following is a quick summary for the impatient. If you +carefully follow these steps, it will ensure that you are 100% +authorized to ship BusyBox with your product, and have no reason +to worry about lawsuits or being listed on the BusyBox Hall of Shame page. You will be +able to sleep peacefully at night knowing you have fulfilled all +your licensing obligations. + +

+ +If you distribute a product, it should either be accompanied by +full source for all GPL'd products (including BusyBox) +and/or a written offer to supply the source for all +GPL'd products for the cost of shipping and handling. The source +has to be in its preferred machine readable form, so you cannot +encrypt or obfuscate it. You are not required to provide full +source for all the closed source applications that happen to be +part of the system with BusyBox, though you can certainly do so +if you feel like it. But providing source for the GPL licensed +applications such as BusyBox is mandatory. + +

+ +Accompanied by source generally means you distribute the full +source code for all GPL'd products including BusyBox along with your +product, such as by placing it somewhere on a driver CD. Full source +code includes the BusyBox ".config" file used when your shipping BusyBox +binary was compiled, and any and all modifications you made to the +BusyBox source code. + +

+ +A written offer generally means that somewhere in the +documentation for your product, you write something like + +

+The GPL source code contained in this product is available as a +free download from http://blah.blah.blah/ +
+Alternatively, you can offer the source code by writing +somewhere in the documentation for your product something like +
+If you would like a copy of the GPL source code contained in this +product shipped to you on CD, please send $9.99 to <address> +which covers the cost of preparing and mailing a CD to you. +
+

+ +Keep in mind though that if you distribute GPL'd binaries online (as is often +done when supplying firmware updates), it is highly recommended that you +make the corresponding source available online at the same place. Regardless, +if you distribute a binary copy of BusyBox online (such as part of a firmware +update) you must either make source available online (i.e. +accompanied by source) and/or inform those downloading firmware updates +of their right to obtain source (i.e. a written offer). Failure to do +so is a violation of your licensing obligations. + + +

+ +Some people have the mistaken understanding that if they use unmodified +GPL'd source code, they do not need to distribute anything. This belief +is not correct, and is not supported by the +text of GPL. +Please do re-read it -- you will find there is no such provision. +If you distribute any GPL'd binaries, you must also make source available +as discussed on this webpage. + +

+

A Good Example

+ +These days, Linksys is +doing a good job at complying with the GPL, they get to be an +example of how to do things right. Please take a moment and +check out what they do with + +distributing the firmware for their WRT54G Router. +Following their example would be a fine way to ensure that you +have also fulfilled your licensing obligations. + + + + diff --git a/busybox/docs/busybox.net/lists.html b/busybox/docs/busybox.net/lists.html new file mode 100644 index 000000000..5c50c957c --- /dev/null +++ b/busybox/docs/busybox.net/lists.html @@ -0,0 +1,45 @@ + + + + + +

Mailing List Information

+BusyBox has a mailing list for discussion and +development. You can subscribe by visiting +this page. +Only subscribers to the BusyBox mailing list are allowed to post +to this list. + +

+There is also a mailing list for active developers +wishing to read the complete diff of each and every change to busybox -- not for the +faint of heart. Active developers can subscribe by visiting +this page. +The CVS server is the only one permtted to post to this list. + +

+ + +

Search the List Archives

+Please search the mailing list archives before asking questions on the mailing +list, since there is a good chance someone else has asked the same question +before. Checking the archives is a great way to avoid annoying everyone on the +list with frequently asked questions... +

+ +

+
+ + + +
+ +
+Google +
+ +
+ + + + diff --git a/busybox/docs/busybox.net/news.html b/busybox/docs/busybox.net/news.html new file mode 100644 index 000000000..0d4c81bab --- /dev/null +++ b/busybox/docs/busybox.net/news.html @@ -0,0 +1,52 @@ + + + +
    + +
  • 13 October 2004 -- BusyBox 1.00 released

    + + When you take a careful look at nearly every embedded Linux device or + software distribution shipping today, you will find a copy of BusyBox. + With countless routers, set top boxes, wireless access points, PDAs, and + who knows what else, the future for Linux and BusyBox on embedded devices + is looking very bright. + +

    + + It is therefore with great satisfaction that I declare each and every + device already shipping with BusyBox is now officially out of date. + The highly anticipated release of BusyBox 1.00 has arrived! + +

    + + Over three years in development, BusyBox 1.00 represents a tremendous + improvement over the old 0.60.x stable series. Now featuring a Linux + KernelConf based configuration system (as used by the Linux kernel), + Linux 2.6 kernel support, many many new applets, and the development + work and testing of thousands of people from around the world. + +

    + + If you are already using BusyBox, you are strongly encouraged to upgrade to + BusyBox 1.00. If you are considering developing an embedded Linux device + or software distribution, you may wish to investigate if using BusyBox is + right for your application. If you need help getting started using + BusyBox, if you wish to donate to help cover expenses, or if you find a bug + and need help reporting it, you are invited to visit the BusyBox FAQ. + +

    + + As usual you can download busybox here. + +

    Have Fun! + +

    +

  • Old News

    + Click here to read older news + + +

+ + + diff --git a/busybox/docs/busybox.net/oldnews.html b/busybox/docs/busybox.net/oldnews.html new file mode 100644 index 000000000..83987ecf8 --- /dev/null +++ b/busybox/docs/busybox.net/oldnews.html @@ -0,0 +1,1060 @@ + + + +
    + +
  • 16 August 2004 -- BusyBox 1.0.0-rc3 released

    + + Here goes release candidate 3... +

    + The changelog has all the details. + And as usual you can download busybox here. + +

    Have Fun! + +

    +

  • 26 July 2004 -- BusyBox 1.0.0-rc2 released

    + + Here goes release candidate 2... +

    + The changelog has all the details. + And as usual you can download busybox here. + +

    Have Fun! + +

    +

  • 20 July 2004 -- BusyBox 1.0.0-rc1 released

    + + Here goes release candidate 1... This fixes all (most?) of the problems + that have turned up since -pre10. In particular, loading and unloading of + kernel modules with 2.6.x kernels should be working much better. +

    + + I really want to get BusyBox 1.0.0 released soon and I see no real + reason why the 1.0.0 release shouldn't happen with things pretty much as + is. BusyBox is in good shape at the moment, and it works nicely for + everything that I'm doing with it. And from the reports I've been getting, + it works nicely for what most everyone else is doing with it as well. + There will eventually be a 1.0.1 anyway, so we might as well get on with + it. No, BusyBox is not perfect. No piece of software ever is. And while + there is still plenty that can be done to improve things, most of that work + is waiting till we can get a solid 1.0.0 release out the door.... +

    + + Please do not bother to send in patches adding cool new features at this + time. Only bug-fix patches will be accepted. If you have submitted a + bug-fixing patch to the busybox mailing list and no one has emailed you + explaining why your patch was rejected, it is safe to say that your patch + has been lost or forgotten. That happens sometimes. Please re-submit your + bug-fixing patch to the BusyBox mailing list, and be sure to put "[PATCH]" + at the beginning of the email subject line! + +

    + The changelog has all the details. + And as usual you can download busybox here. + +

    Have Fun! + +

    + On a less happy note, My 92 year old grandmother (my dad's mom) passed away + yesterday (June 19th). The funeral will be Thursday in a little town about + 2 hours south of my home. I've checked and there is absolutely no way I + could be back in time for the funeral if I attend OLS and give my presentation + as scheduled. +

    + As such, it is with great reluctance and sadness that I have come + to the conclusion I will have to make my appologies and skip OLS + this year. +

    + + +

    +

  • 13 April 2004 -- BusyBox 1.0.0-pre10 released

    + + Ok, I lied. It turns out that -pre9 will not be the final BusyBox + pre-release. With any luck however -pre10 will be, since I really + want to get BusyBox 1.0.0 released very soon. As usual, please do not + bother to send in patches adding cool new features at this time. Only + bug-fix patches will be accepted. It would also be very helpful if + people could continue to review the BusyBox documentation and submit + improvements. + +

    + The changelog has all the details. + And as usual you can download busybox here. + +

    Have Fun! +

    + + +

    +

  • 6 April 2004 -- BusyBox 1.0.0-pre9 released

    + + Here goes the final BusyBox pre-release... This is your last chance for + bug fixes. With luck this will be released as BusyBox 1.0.0 later this + week. Please do not bother to send in patches adding cool new features at + this time. Only bug-fix patches will be accepted. It would also be + very helpful if people could help review the BusyBox documentation + and submit improvements. I've spent a lot of time updating the + documentation to make it better match reality, but I could really use some + assistance in checking that the features supported by the various applets + match the features listed in the documentation. + +

    + I had hoped to get this released a month ago, but + + another release on 1 March 2004 has kept me busy... + +

    + The changelog has all the details. + And as usual you can download busybox here. + +

    Have Fun! +

    + + +

    +

  • 23 February 2004 -- BusyBox 1.0.0-pre8 released

    + + Here goes yet another BusyBox pre-release... Please do not bother to send + in patches supplying new features at this time. Only bug-fix patches will + be accepted. If you have a cool new feature you would like to see + supported, or if you have an amazing new applet you would like to submit, + please wait and submit such things later. We really want to get a release + out we can all be proud of. We are still aiming to finish off the -pre + series in February and move on to the final 1.0.0 release... So if you + spot any bugs, now would be an excellent time to send in a fix to the + busybox mailing list. It would also be very helpful if people could + help review the BusyBox documentation and submit improvements. It would be + especially helpful if people could check that the features supported by the + various applets match the features listed in the documentation. + +

    + + The changelog has all the details. + And as usual you can download busybox here. + +

    Have Fun! +

    + + +

  • 4 February 2004 -- BusyBox 1.0.0-pre7 released

    + + There was a bug in -pre6 that broke argument parsing for a + number of applets, since a variable was not being zeroed out + properly. This release is primarily intended to fix that one + problem. In addition, this release fixes several other + problems, including a rewrite by mjn3 of the code for parsing + the busybox.conf file used for suid handling, some shell updates + from vodz, and a scattering of other small fixes. We are still + aiming to finish off the -pre series in February and move on to + the final 1.0.0 release... If you see any problems, of have + suggestions to make, as always, please feel free to email the + busybox mailing list. + +

    + + The changelog has all + the details. And as usual you can + download busybox here. + +

    Have Fun! +

    + + +

    +

  • 30 January 2004 -- BusyBox 1.0.0-pre6 released

    + + Here goes the next pre-release for the new BusyBox stable + series. This release adds a number of size optimizations, + updates udhcp, fixes up 2.6 modutils support, updates ash + and the shell command line editing, and the usual pile of + bug fixes both large and small. Things appear to be + settling down now, so with a bit of luck and some testing + perhaps we can finish off the -pre series in February and + move on to the final 1.0.0 release... If you see any + problems, of have suggestions to make, as always, please + feel free to email the busybox mailing list. + +

    + + People who rely on the daily BusyBox snapshots + should be aware that snapshots of the old busybox 0.60.x + series are no longer available. Daily snapshots are now + only available for the BusyBox 1.0.0 series and now use + the naming scheme "busybox-<date>.tar.bz2". Please + adjust any build scripts using the old naming scheme accordingly. + +

    + + The changelog has all + the details. And as usual you can + download busybox here. + +

    Have Fun! +

    + + +

    +

  • 23 December 2003 -- BusyBox 1.0.0-pre5 released

    + + Here goes the next pre-release for the new BusyBox stable + series. The most obvious thing in this release is a fix for + a terribly stupid bug in mount that prevented it from working + properly unless you specified the filesystem type. This + release also fixes a few compile problems, updates udhcp, + fixes a silly bug in fdisk, fixes ifup/ifdown to behave like + the Debian version, updates devfsd, updates the 2.6.x + modutils support, add a new 'rx' applet, removes the obsolete + 'loadacm' applet, fixes a few tar bugs, fixes a sed bug, and + a few other odd fixes. + +

    + + If you see any problems, of have suggestions to make, as + always, please feel free to send an email to the busybox + mailing list. + +

    + + The changelog has all + the details. And as usual you can + download busybox here. + +

    Have Fun! +

    + + + +

  • 10 December 2003 -- BusyBox 1.0.0-pre4 released

    + + Here goes the fourth pre-release for the new BusyBox stable + series. This release includes major rework to sed, lots of + rework on tar, a new tiny implementation of bunzip2, a new + devfsd applet, support for 2.6.x kernel modules, updates to + the ash shell, sha1sum and md5sum have been merged into a + common applet, the dpkg applets has been cleaned up, and tons + of random bugs have been fixed. Thanks everyone for all the + testing, bug reports, and patches! Once again, a big + thank-you goes to Glenn McGrath (bug1) for stepping in and + helping get patches merged! + +

    + + And of course, if you are reading this, you might have noticed + the busybox website has been completely reworked. Hopefully + things are now somewhat easier to navigate... If you see any + problems, of have suggestions to make, as always, please feel + free to send an email to the busybox mailing list. + +

    + + The changelog has all + the details. And as usual you can + download busybox here. + +

    Have Fun! + + + +

    +

  • 12 Sept 2003 -- BusyBox 1.0.0-pre3 released

    + + Here goes the third pre-release for the new BusyBox stable + series. The last prerelease has held up quite well under + testing, but a number of problems have turned up as the number + of people using it has increased. Thanks everyone for all + the testing, bug reports, and patches! + +

    + + If you have submitted a patch or a bug report to the busybox + mailing list and no one has emailed you explaining why your + patch was rejected, it is safe to say that your patch has + somehow gotten lost or forgotten. That happens sometimes. + Please re-submit your patch or bug report to the BusyBox + mailing list! + +

    + + The point of the "-preX" versions is to get a larger group of + people and vendors testing, so any problems that turn up can be + fixed prior to the final 1.0.0 release. The main feature + (besides additional testing) that is still still on the TODO + list before the final BusyBox 1.0.0 release is sorting out the + modutils issues. For the new 2.6.x kernels, we already have + patches adding insmod and rmmod support and those need to be + integrated. For 2.4.x kernels, for which busybox only supports + a limited number of architectures, we may want to invest a bit + more work before we cut 1.0.0. Or we may just leave 2.4.x + module loading alone. + +

    + + I had hoped this release would be out a month ago. And of + course, it wasn't since Erik became busy getting a release of + uClibc + out the door. Many thanks to Glenn McGrath (bug1) for + stepping in and helping get a bunch of patches merged! I am + not even going to state a date for releasing BusyBox 1.0.0 + -pre4 (or the final 1.0.0). We're aiming for late September... + But if this release proves as to be exceptionally stable (or + exceptionally unstable!), the next release may be very soon + indeed. + +

    + + The changelog has all + the details. And as usual you can + download busybox here. + +

    Have Fun! + + +

    +

  • 30 July 2003 -- BusyBox 1.0.0-pre2 released

    + + Here goes another pre release for the new BusyBox stable + series. The last prerelease (pre1) was given quite a lot of + testing (thanks everyone!) which has helped turn up a number of + bugs, and these problems have now been fixed. + +

    + + Highlights of -pre2 include updating the 'ash' shell to sync up + with the Debian 'dash' shell, a new 'hdparm' applet was added, + init again supports pivot_root, The 'reboot' 'halt' and + 'poweroff' applets can now be used without using busybox init. + an ifconfig buffer overflow was fixed, losetup now allows + read-write loop devices, uClinux daemon support was added, the + 'watchdog', 'fdisk', and 'kill' applets were rewritten, there were + tons of doc updates, and there were many other bugs fixed. +

    + + If you have submitted a patch and it is not included in this + release and Erik has not emailed you explaining why your patch + was rejected, it is safe to say that he has lost your patch. + That happens sometimes. Please re-submit your patch to the + BusyBox mailing list. +

    + + The point of the "-preX" versions is to get a larger group of + people and vendors testing, so any problems that turn up can be + fixed prior to the final 1.0.0 release. The main feature that + is still still on the TODO list before the final BusyBox 1.0.0 + release is adding module support for the new 2.6.x kernels. If + necessary, a -pre3 BusyBox release will happen on August 6th. + Hopefully (i.e. unless some horrible catastrophic problem + turns up) the final BusyBox 1.0.0 release will be ready by + then... +

    + + The changelog has all + the details. As usual you can download busybox here. + +

    Have Fun! +

    + +

    +

  • 15 July 2003 -- BusyBox 1.0.0-pre1 released

    + + The busybox development series has been under construction for + nearly two years now. Which is just entirely too long... So + it is with great pleasure that I announce the imminent release + of a new stable series. Due to the huge number of changes + since the last stable release (and the usual mindless version + number inflation) I am branding this new stable series verison + 1.0.x... +

    + + The point of "-preX" versions is to get a larger group of + people and vendors testing, so any problems that turn up can be + fixed prior to the magic 1.0.0 release (which should happen + later this month)... I plan to release BusyBox 1.0.0-pre2 next + Monday (July 21st), and, if necessary, -pre3 on July 28th. + Hopefully (i.e. unless some horrible catastrophic problem turns + up) the final BusyBox 1.0.0 release should be ready by the end + of July. +

    + + If you have submitted patches, and they are not in this release + and I have not emailed you explaining why your patch was + rejected, it is safe to say that I have lost your patch. That + happens sometimes. Please do NOT send all your patches, + support questions, etc, directly to Erik. I get hundreds of + emails every day (which is why I end up losing patches + sometimes in the flood)... The busybox mailing list is the + right place to send your patches, support questions, etc. +

    + + I would like to especially thank Vladimir Oleynik (vodz), Glenn + McGrath (bug1), Robert Griebl (sandman), and Manuel Novoa III + (mjn3) for their significant efforts and contributions that + have made this release possible. +

    + + As usual you can download busybox here. + You don't really need to bother with the + changelog, as the changes + vs the stable version are way too extensive to easily enumerate. + But you can take a look if you really want too. + +

    Have Fun! +

    + + + +

    +

  • 26 October 2002 -- BusyBox 0.60.5 released

    + + I am very pleased to announce that the BusyBox 0.60.5 (stable) + is now available for download. This is a bugfix release for + the stable series to address all the problems that have turned + up since the last release. Unfortunately, the previous release + had a few nasty bugs (i.e. init could deadlock, gunzip -c tried + to delete source files, cp -a wouldn't copy symlinks, and init + was not always providing controlling ttys when it should have). + I know I said that the previous release would be the end of the + 0.60.x series. Well, it turns out I'm a liar. But this time I + mean it (just like last time ;-). This will be the last + release for the 0.60.x series -- all further development work + will be done for the development busybox tree. Expect the development + version to have its first real release very very soon now... + +

    + The changelog has all + the details. As usual you can download busybox here. +

    Have Fun! +

    + +

    +

  • 18 September 2002 -- BusyBox 0.60.4 released

    + + I am very pleased to announce that the BusyBox 0.60.4 + (stable) is now available for download. This is primarily + a bugfix release for the stable series to address all + the problems that have turned up since the last + release. This will be the last release for the 0.60.x series. + I mean it this time -- all further development work will be done + on the development busybox tree, which is quite solid now and + should soon be getting its first real release. + +

    + The changelog has all + the details. As usual you can download busybox here. +

    Have Fun! +

    + + +

    +

  • 27 April 2002 -- BusyBox 0.60.3 released

    + + I am very pleased to announce that the BusyBox 0.60.3 (stable) is + now available for download. This is primarily a bugfix release + for the stable series. A number of problems have turned up since + the last release, and this should address most of those problems. + This should be the last release for the 0.60.x series. The + development busybox tree has been progressing nicely, and will + hopefully be ready to become the next stable release. + +

    + The changelog has all + the details. As usual you can download busybox here. +

    Have Fun! +

    + + +

    +

  • 6 March 2002 -- busybox.net now has mirrors!

    + + Busybox.net is now much more available, thanks to + the fine folks at http://i-netinnovations.com/ + who are providing hosting for busybox.net and + uclibc.org. In addition, we now have two mirrors: + http://busybox.linuxmagic.com/ + in Canada and + http://busybox.csservers.de/ + in Germany. I hope this makes things much more + accessible for everyone! + + +

  • +3 January 2002 -- Welcome to busybox.net! + +

    Thanks to the generosity of a number of busybox +users, we have been able to purchase busybox.net +(which is where you are probably reading this). +Right now, busybox.net and uclibc.org are both +living on my home system (at the end of my DSL +line). I apologize for the abrupt move off of +busybox.lineo.com. Unfortunately, I no longer have +the access needed to keep that system updated (for +example, you might notice the daily snapshots there +stopped some time ago).

    + +

    Busybox.net is currently hosted on my home +server, at the end of a DSL line. Unfortunately, +the load on them is quite heavy. To address this, +I'm trying to make arrangements to get busybox.net +co-located directly at an ISP. To assist in the +co-location effort, Mark Whitley +(author of busybox sed, cut, and grep) has donated +his NetWinder computer +for hosting busybox.net and uclibc.org. Once this +system is co-located, the current speed problems +should be completely eliminated. Hopefully, too, +some of you will volunteer to set up some mirror +sites, to help to distribute the load a bit.

    + +

    + Since some people expressed concern over BusyBox +donations, let me assure you that no one is getting +rich here. All BusyBox and uClibc donations will be +spent paying for bandwidth and needed hardware +upgrades. For example, Mark's NetWinder currently +has just 64Meg of memory. As demonstrated when +google spidered the site the other day, 64 Megs in +not enough, so I'm going to be ordering 256Megs of +ram and a larger hard drive for the box today. So +far, donations received have been sufficient to +cover almost all expenses. In the future, we may +have co-location fees to worry about, but for now +we are ok. A HUGE thank-you goes out to +everyone that has contributed!
    + -Erik

    +
  • + +
  • +20 November 2001 -- BusyBox 0.60.2 released + +

    We am very pleased to announce that the BusyBox +0.60.2 (stable) is now released to the world. This +one is primarily a bugfix release for the stable +series, and it should take care of most everyone's +needs till we can get the nice new stuff we have +been working on in CVS ready to release (with the +wonderful new buildsystem). The biggest change in +this release (beyond bugfixes) is the fact that msh +(the minix shell) has been re-worked by Vladimir N. +Oleynik (vodz) and so it no longer crashes when +told to do complex things with backticks.

    + +

    This release has been tested on x86, ARM, and +powerpc using glibc 2.2.4, libc5, and uClibc, so it +should work with just about any Linux system you +throw it at. See the changelog for most +of the details. The last release was +very solid for people, and this one should +be even better.

    + +

    As usual BusyBox 0.60.2 can be downloaded from +http://www.busybox.net/downloads.

    + +

    Have Fun.
    + -Erik

    +
  • + +
  • 18 November 2001 -- Help us buy busybox.net! + + +
    +Click here to help buy busybox.net! +
    + + + + + + + +
    + + +I've contacted the current owner of busybox.net and he is willing +to sell the domain name -- for $250. He also owns busybox.org but +will not part with it... I will then need to pay the registry fee +for a couple of years and start paying for bandwidth, so this will +initially cost about $300. I would like to host busybox.net on my +home machine (codepoet.org) so I have full control over the system, +but to do that would require that I increase the level of bandwidth +I am paying for. Did you know that so far this month, there +have been over 1.4 Gigabytes of busybox ftp downloads? I don't +even know how much CVS bandwidth it requires. For the +time being, Lineo has continued to graciously provide this +bandwidth, despite the fact that I no longer work for them. If I +start running this all on my home machine, paying for the needed bandwidth +will start costing some money. +

    + +I was going to pay it all myself, but my wife didn't like that +idea at all (big surprise). It turns out <insert argument +where she wins and I don't> she has better ideas +about what we should spend our money on that don't involve +busybox. She suggested I should ask for contributions on the +mailing list and web page. So... +

    + +I am hoping that if everyone could contribute a bit, we could pick +up the busybox.net domain name and cover the bandwidth costs. I +know that busybox is being used by a lot of companies as well as +individuals -- hopefully people and companies that are willing to +contribute back a bit. So if everyone could please help out, that +would be wonderful! +

    + + +

  • 23 August 2001 -- BusyBox 0.60.1 released +
    + + This is a relatively minor bug fixing release that fixes + up the bugs that have shown up in the stable release in + the last few weeks. Fortunately, nothing too + serious has shown up. This release only fixes bugs -- no + new features, no new applets. So without further ado, + here it is. Come and get it. +

    + The + changelog has all + the details. As usual BusyBox 0.60.1 can be downloaded from + http://busybox.net/downloads. +

    Have Fun! +

    + + +

  • 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 busybox.net. +

    + 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 + http://busybox.net/downloads. +

    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 + http://busybox.net/downloads. +

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

    + + +

  • 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 + http://busybox.net/downloads. +

    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. + + +
+ + + + diff --git a/busybox/docs/busybox.net/products.html b/busybox/docs/busybox.net/products.html new file mode 100644 index 000000000..6ca0e3c92 --- /dev/null +++ b/busybox/docs/busybox.net/products.html @@ -0,0 +1,166 @@ + + + +

Products/Projects Using BusyBox

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

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

+ + + + diff --git a/busybox/docs/busybox.net/screenshot.html b/busybox/docs/busybox.net/screenshot.html new file mode 100644 index 000000000..9c05791db --- /dev/null +++ b/busybox/docs/busybox.net/screenshot.html @@ -0,0 +1,57 @@ + + + + + +

Busybox Screenshot!

+ + +Everybody loves to look at screenshots, so here is a live action screenshot of BusyBox. + +
+
+
+$ ./busybox
+BusyBox v1.00 (2004.10.13-04:49+0000) multi-call binary
+
+Usage: busybox [function] [arguments]...
+   or: [function] [arguments]...
+
+        BusyBox is a multi-call binary that combines many common Unix
+        utilities into a single executable.  Most people will create a
+        link to busybox for each function they wish to use, and BusyBox
+        will act like whatever it was invoked as.
+
+Currently defined functions:
+
+	[, addgroup, adduser, adjtimex, ar, arping, ash, awk, basename, bunzip2,
+	busybox, bzcat, cal, cat, chgrp, chmod, chown, chroot, chvt, clear, cmp,
+	cp, cpio, crond, crontab, cut, date, dc, dd, deallocvt, delgroup, deluser,
+	devfsd, df, dirname, dmesg, dos2unix, dpkg, dpkg-deb, du, dumpkmap,
+	dumpleases, echo, egrep, env, expr, false, fbset, fdflush, fdformat, fdisk,
+	fgrep, find, fold, free, freeramdisk, fsck.minix, ftpget, ftpput, getopt,
+	getty, grep, gunzip, gzip, halt, hdparm, head, hexdump, hostid, hostname,
+	httpd, hush, hwclock, id, ifconfig, ifdown, ifup, inetd, init, insmod,
+	install, ip, ipaddr, ipcalc, iplink, iproute, iptunnel, kill, killall,
+	klogd, lash, last, length, linuxrc, ln, loadfont, loadkmap, logger, login,
+	logname, logread, losetup, ls, lsmod, makedevs, md5sum, mesg, mkdir,
+	mkfifo, mkfs.minix, mknod, mkswap, mktemp, modprobe, more, mount, msh, mt,
+	mv, nameif, nc, netstat, nslookup, od, openvt, passwd, patch, pidof, ping,
+	ping6, pipe_progress, pivot_root, poweroff, printf, ps, pwd, rdate,
+	readlink, realpath, reboot, renice, reset, rm, rmdir, rmmod, route, rpm,
+	rpm2cpio, run-parts, rx, sed, seq, setkeycodes, sha1sum, sleep, sort,
+	start-stop-daemon, strings, stty, su, sulogin, swapoff, swapon, sync,
+	sysctl, syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top,
+	touch, tr, traceroute, true, tty, udhcpc, udhcpd, umount, uname,
+	uncompress, uniq, unix2dos, unzip, uptime, usleep, uudecode, uuencode,
+	vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami, xargs,
+	yes, zcat
+
+
+$ _
+
+
+ + + diff --git a/busybox/docs/busybox.net/shame.html b/busybox/docs/busybox.net/shame.html new file mode 100644 index 000000000..99807c17a --- /dev/null +++ b/busybox/docs/busybox.net/shame.html @@ -0,0 +1,77 @@ + + + +

Hall of Shame!!!

+ +The following products and/or projects appear to use BusyBox, but do not +appear to release source code as required by the BusyBox license. This is a violation of the law! +The distributors of these products are invited to contact Erik Andersen if they have any confusion +as to what is needed to bring their products into compliance, or if they have +already brought their product into compliance and wish to be removed from the +Hall of Shame. + +

+ +Here are the details of exactly how to comply +with the BusyBox license, so there should be no question as to +exactly what is expected. +Complying with the Busybox license is easy and completely free, so the +companies listed below should be ashamed of themselves. Furthermore, each +product listed here is subject to being legally ordered to cease and desist +distribution for violation of copyright law, and the distributor of each +product is subject to being sued for statutory copyright infringement damages +of up to $150,000 per work plus legal fees. Nobody wants to be sued, and Erik certainly would prefer to spend +his time doing better things than sue people. But he will sue if forced to +do so to maintain compliance. + +

+ +Do everyone a favor and don't break the law -- if you use busybox, comply with +the busybox license by releasing the source code with your product. + +

+ +

+ + + + diff --git a/busybox/docs/busybox_footer.pod b/busybox/docs/busybox_footer.pod new file mode 100644 index 000000000..f965711b9 --- /dev/null +++ b/busybox/docs/busybox_footer.pod @@ -0,0 +1,258 @@ +=back + +=head1 LIBC NSS + +GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior +of the C library for the local environment, and to configure how it reads +system data, such as passwords and group information. This is implemented +using an /etc/nsswitch.conf configuration file, and using one or more of the +/lib/libnss_* libraries. BusyBox tries to avoid using any libc calls that make +use of NSS. Some applets however, such as login and su, will use libc functions +that require NSS. + +If you enable CONFIG_USE_BB_PWD_GRP, BusyBox will use internal functions to +directly access the /etc/passwd, /etc/group, and /etc/shadow files without +using NSS. This may allow you to run your system without the need for +installing any of the NSS configuration files and libraries. + +When used with glibc, the BusyBox 'networking' applets will similarly require +that you install at least some of the glibc NSS stuff (in particular, +/etc/nsswitch.conf, /lib/libnss_dns*, /lib/libnss_files*, and /lib/libresolv*). + +Shameless Plug: As an alternative, one could use a C library such as uClibc. In +addition to making your system significantly smaller, uClibc does not require the +use of any NSS support files or libraries. + +=head1 MAINTAINER + +Erik Andersen + +=head1 AUTHORS + +The following people have contributed code to BusyBox whether they know it or +not. If you have written code included in BusyBox, you should probably be +listed here so you can obtain your bit of eternal glory. If you should be +listed here, or the description of what you have done needs more detail, or is +incorect, please send in an update. + + +=for html
+ +Emanuele Aina + run-parts + +=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. + Lots of tedious effort writing these boring docs that + nobody is going to actually read. + +=for html
+ +Laurence Anderson + + rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm + +=for html
+ +Jeff Angielski + + ftpput, ftpget + +=for html
+ +Edward Betts + + expr, hostid, logname, whoami + +=for html
+ +John Beppu + + du, nslookup, sort + +=for html
+ +Brian Candler + + tiny-ls(ls) + +=for html
+ +Randolph Chung + + fbset, ping, hostname + +=for html
+ +Dave Cinege + + more(v2), makedevs, dutmp, modularization, auto links file, + various fixes, Linux Router Project maintenance + +=for html
+ +Jordan Crouse + + ipcalc + +=for html
+ +Magnus Damm + + tftp client insmod powerpc support + +=for html
+ +Larry Doolittle + + pristine source directory compilation, lots of patches and fixes. + +=for html
+ +Glenn Engel + + httpd + +=for html
+ +Gennady Feldman + + Sysklogd (single threaded syslogd, IPC Circular buffer support, + logread), various fixes. + +=for html
+ +Karl M. Hegbloom + + cp_mv.c, the test suite, various fixes to utility.c, &c. + +=for html
+ +Daniel Jacobowitz + + mktemp.c + +=for html
+ +Matt Kraai + + documentation, bugfixes, test suite + +=for html
+ +Stephan Linz + + ipcalc, Red Hat equivalence + +=for html
+ +John Lombardo + + tr + +=for html
+ +Glenn McGrath + + Common unarchving code and unarchiving applets, ifupdown, ftpgetput, + nameif, sed, patch, fold, install, uudecode. + Various bugfixes, review and apply numerous patches. + +=for html
+ +Manuel Novoa III + + cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes, + mesg, vconfig, make_directory, parse_mode, dirname, mode_string, + get_last_path_component, simplify_path, and a number trivial libbb routines + + also bug fixes, partial rewrites, and size optimizations in + ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir, + mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable, + interface, dutmp, ifconfig, route + +=for html
+ +Vladimir Oleynik + + cmdedit; xargs(current), httpd(current); + ports: ash, crond, fdisk, inetd, stty, traceroute, top; + locale, various fixes + and irreconcilable critic of everything not perfect. + +=for html
+ +Bruce Perens + + Original author of BusyBox in 1995, 1996. Some of his code can + still be found hiding here and there... + +=for html
+ +Tim Riker + + bug fixes, member of fan club + +=for html
+ +Kent Robotti + + reset, tons and tons of bug reports and patches. + +=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 + + grep, sed, cut, xargs(previous), + style-guide, new-applet-HOWTO, bug fixes, etc. + +=for html
+ +Charles P. Wright + + gzip, mini-netcat(nc) + +=for html
+ +Enrique Zanardi + + tarcat (since removed), loadkmap, various fixes, Debian maintenance + +=for html
+ +Tito Ragusa + + devfsd and size optimizations in strings, openvt and deallocvt. + +=cut + +# $Id: busybox_footer.pod,v 1.18 2004/04/25 06:05:14 bug1 Exp $ + diff --git a/busybox/docs/busybox_header.pod b/busybox/docs/busybox_header.pod new file mode 100644 index 000000000..35631b84e --- /dev/null +++ b/busybox/docs/busybox_header.pod @@ -0,0 +1,111 @@ +# 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 GNU coreutils, util-linux, etc. The utilities in BusyBox +generally have fewer options than their full-featured GNU cousins; however, the +options that are included provide the expected functionality and behave very +much like their GNU counterparts. + +BusyBox has been written with size-optimization and limited resources in mind. +It is also extremely modular so you can easily include or exclude commands (or +features) at compile time. This makes it easy to customize your embedded +systems. To create a working system, just add /dev, /etc, and a Linux kernel. +BusyBox provides a fairly complete POSIX environment for any small or embedded +system. + +BusyBox is extremely configurable. This allows you to include only the +components you need, thereby reducing binary size. Run 'make config' or 'make +menuconfig' to select the functionality that you wish to enable. The run +'make' to compile BusyBox using your configuration. + +After the compile has finished, you should use 'make install' to install +BusyBox. This will install the '/bin/busybox' binary, and will also create +symlinks pointing to the '/bin/busybox' binary for each utility that you +compile into BusyBox. By default, 'make install' will place these symlinks +into the './_install' directory, unless you have defined 'PREFIX', thereby +specifying some alternative location (i.e., 'make PREFIX=/tmp/foo install'). +If you wish to install using hardlinks, rather than the default of using +symlinks, you can use 'make PREFIX=/tmp/foo install-hardlinks' instead. + +=head1 USAGE + +BusyBox is a multi-call binary. A multi-call binary is an executable program +that performs the same job as more than one utility program. That means there +is just a single BusyBox binary, but that single binary acts like a large +number of utilities. This allows BusyBox to be smaller since all the built-in +utility programs (we call them applets) can share code for many common operations. + +You can also invoke BusyBox by issuing a command as an argument on the +command line. For example, entering + + /bin/busybox ls + +will also cause BusyBox to behave as 'ls'. + +Of course, adding '/bin/busybox' into every command would be painful. So most +people will invoke BusyBox using links to the BusyBox binary. + +For example, entering + + ln -s /bin/busybox ls + ./ls + +will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled +into BusyBox). Generally speaking, you should never need to make all these +links yourself, as the BusyBox build system will do this for you when you run +the 'make install' command. + +If you invoke BusyBox with no arguments, it will provide you with a list of the +applets that have been compiled into your BusyBox binary. + +=head1 COMMON OPTIONS + +Most BusyBox commands support the B<--help> argument to provide a terse runtime +description of their behavior. If the CONFIG_FEATURE_VERBOSE_USAGE option has +been enabled, more detailed usage information will also be available. + +=head1 COMMANDS + +Currently defined functions include: + + addgroup, adduser, adjtimex, ar, arping, ash, awk, basename, bunzip2, + busybox, bzcat, cal, cat, chgrp, chmod, chown, chroot, chvt, clear, cmp, + cp, cpio, crond, crontab, cut, date, dc, dd, deallocvt, delgroup, deluser, + devfsd, df, dirname, dmesg, dos2unix, dpkg, dpkg-deb, du, dumpkmap, + dumpleases, echo, egrep, env, expr, false, fbset, fdflush, fdformat, fdisk, + fgrep, find, fold, free, freeramdisk, fsck.minix, ftpget, ftpput, getopt, + getty, grep, gunzip, gzip, halt, hdparm, head, hexdump, hostid, hostname, + httpd, hush, hwclock, id, ifconfig, ifdown, ifup, inetd, init, insmod, + install, ip, ipaddr, ipcalc, iplink, iproute, iptunnel, kill, killall, + klogd, lash, last, length, linuxrc, ln, loadfont, loadkmap, logger, login, + logname, logread, losetup, ls, lsmod, makedevs, md5sum, mesg, mkdir, + mkfifo, mkfs.minix, mknod, mkswap, mktemp, modprobe, more, mount, msh, mt, + mv, nameif, nc, netstat, nslookup, od, openvt, passwd, patch, pidof, ping, + ping6, pipe_progress, pivot_root, poweroff, printf, ps, pwd, rdate, + readlink, realpath, reboot, renice, reset, rm, rmdir, rmmod, route, rpm, + rpm2cpio, run-parts, rx, sed, seq, setkeycodes, sha1sum, sleep, sort, + start-stop-daemon, strings, stty, su, sulogin, swapoff, swapon, sync, + sysctl, syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top, + touch, tr, traceroute, true, tty, udhcpc, udhcpd, umount, uname, + uncompress, uniq, unix2dos, unzip, uptime, usleep, uudecode, uuencode, + vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami, xargs, + yes, zcat + +=head1 COMMAND DESCRIPTIONS + +=over 4 + + diff --git a/busybox/docs/contributing.txt b/busybox/docs/contributing.txt new file mode 100644 index 000000000..e80fc135c --- /dev/null +++ b/busybox/docs/contributing.txt @@ -0,0 +1,449 @@ +Contributing To Busybox +======================= + +This document describes what you need to do to contribute to Busybox, where +you can help, guidelines on testing, and how to submit a well-formed patch +that is more likely to be accepted. + +The Busybox home page is at: http://busybox.net/ + + + +Pre-Contribution Checklist +-------------------------- + +So you want to contribute to Busybox, eh? Great, wonderful, glad you want to +help. However, before you dive in, headlong and hotfoot, there are some things +you need to do: + + +Checkout the Latest Code from CVS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is a necessary first step. Please do not try to work with the last +released version, as there is a good chance that somebody has already fixed +the bug you found. Somebody might have even added the feature you had in mind. +Don't make your work obsolete before you start! + +For information on how to check out Busybox from CVS, please look at the +following links: + + http://busybox.net/cvs_anon.html + http://busybox.net/cvs_howto.html + + +Read the Mailing List +~~~~~~~~~~~~~~~~~~~~~ + +No one is required to read the entire archives of the mailing list, but you +should at least read up on what people have been talking about lately. If +you've recently discovered a problem, chances are somebody else has too. If +you're the first to discover a problem, post a message and let the rest of us +know. + +Archives can be found here: + + http://busybox.net/lists/busybox/ + +If you have a serious interest in Busybox, i.e., you are using it day-to-day or +as part of an embedded project, it would be a good idea to join the mailing +list. + +A web-based sign-up form can be found here: + + http://busybox.net/mailman/listinfo/busybox + + +Coordinate with the Applet Maintainer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some (not all) of the applets in Busybox are "owned" by a maintainer who has +put significant effort into it and is probably more familiar with it than +others. To find the maintainer of an applet, look at the top of the .c file +for a name following the word 'Copyright' or 'Written by' or 'Maintainer'. + +Before plunging ahead, it's a good idea to send a message to the mailing list +that says: "Hey, I was thinking about adding the 'transmogrify' feature to the +'foo' applet. Would this be useful? Is anyone else working on it?" You might +want to CC the maintainer (if any) with your question. + + + +Areas Where You Can Help +------------------------ + +Busybox can always use improvement! If you're looking for ways to help, there +are a variety of areas where you could help. + + +What Busybox Doesn't Need +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before listing the areas where you _can_ help, it's worthwhile to mention the +areas where you shouldn't bother. While Busybox strives to be the "Swiss Army +Knife" of embedded Linux, there are some applets that will not be accepted: + + - Any filesystem manipulation tools: Busybox is filesystem independent and + we do not want to start adding mkfs/fsck tools for every (or any) + filesystem under the sun. (fsck_minix.c and mkfs_minix.c are living on + borrowed time.) There are far too many of these tools out there. Use + the upstream version. Not everything has to be part of Busybox. + + - Any partitioning tools: Partitioning a device is typically done once and + only once, and tools which do this generally do not need to reside on the + target device (esp a flash device). If you need a partitioning tool, grab + one (such as fdisk, sfdisk, or cfdisk from util-linux) and use that, but + don't try to merge it into busybox. These are nasty and complex and we + don't want to maintain them. + + - Any disk, device, or media-specific tools: Use the -utils or -tools package + that was designed for your device; don't try to shoehorn them into Busybox. + + - Any architecture specific tools: Busybox is (or should be) architecture + independent. Do not send us tools that cannot be used across multiple + platforms / arches. + + - Any daemons that are not essential to basic system operation. To date, only + syslogd and klogd meet this requirement. We do not need a web server, an + ftp daemon, a dhcp server, a mail transport agent or a dns resolver. If you + need one of those, you are welcome to ask the folks on the mailing list for + recommendations, but please don't bloat up Busybox with any of these. + + +Bug Reporting +~~~~~~~~~~~~~ + +If you find bugs, please submit a detailed bug report to the busybox mailing +list at busybox@busybox.net. A well-written bug report should include a +transcript of a shell session that demonstrates the bad behavior and enables +anyone else to duplicate the bug on their own machine. The following is such +an example: + + To: busybox@busybox.net + From: diligent@testing.linux.org + Subject: /bin/date doesn't work + + Package: busybox + Version: 1.00 + + When I execute Busybox 'date' it produces unexpected results. + With GNU date I get the following output: + + $ date + Wed Mar 21 14:19:41 MST 2001 + + But when I use BusyBox date I get this instead: + + $ date + llegal instruction + + I am using Debian unstable, kernel version 2.4.19-rmk1 on an Netwinder, + and the latest uClibc from CVS. Thanks for the wonderful program! + + -Diligent + +Note the careful description and use of examples showing not only what BusyBox +does, but also a counter example showing what an equivalent GNU app does. Bug +reports lacking such detail may never be fixed... Thanks for understanding. + + + +Write Documentation +~~~~~~~~~~~~~~~~~~~ + +Chances are, documentation in Busybox is either missing or needs improvement. +Either way, help is welcome. + +Work is being done to automatically generate documentation from sources, +especially from the usage.h file. If you want to correct the documentation, +please make changes to the pre-generation parts, rather than the generated +documentation. [More to come on this later...] + +It is preferred that modifications to documentation be submitted in patch +format (more on this below), but we're a little more lenient when it comes to +docs. You could, for example, just say "after the listing of the mount +options, the following example would be helpful..." + + +Consult Existing Sources +~~~~~~~~~~~~~~~~~~~~~~~~ + +For a quick listing of "needs work" spots in the sources, cd into the Busybox +directory and run the following: + + for i in TODO FIXME XXX; do find -name '*.[ch]'|xargs grep $i; done + +This will show all of the trouble spots or 'questionable' code. Pick a spot, +any spot, these are all invitations for you to contribute. + + +Add a New Applet +~~~~~~~~~~~~~~~~ + +If you want to add a new applet to Busybox, we'd love to see it. However, +before you write any code, please ask beforehand on the mailing list something +like "Do you think applet 'foo' would be useful in Busybox?" or "Would you +guys accept applet 'foo' into Busybox if I were to write it?" If the answer is +"no" by the folks on the mailing list, then you've saved yourself some time. +Conversely, you could get some positive responses from folks who might be +interested in helping you implement it, or can recommend the best approach. +Perhaps most importantly, this is your way of calling "dibs" on something and +avoiding duplication of effort. + +Also, before you write a line of code, please read the 'new-applet-HOWTO.txt' +file in the docs/ directory. + + +Janitorial Work +~~~~~~~~~~~~~~~ + +These are dirty jobs, but somebody's gotta do 'em. + + - Converting applets to use getopt() for option processing. Type 'find -name + '*.c'|grep -L getopt' to get a listing of the applets that currently don't + use getopt. If a .c file processes no options, it should have a line that + reads: /* no options, no getopt */ somewhere in the file. + + - Replace any "naked" calls to malloc, calloc, realloc, str[n]dup, fopen with + the x* equivalents found in libbb/xfuncs.c. + + - Security audits: + http://www.securityfocus.com/popups/forums/secprog/intro.shtml + + - Synthetic code removal: http://www.perl.com/pub/2000/06/commify.html - This + is very Perl-specific, but the advice given in here applies equally well to + C. + + - C library function use audits: Verifying that functions are being used + properly (called with the right args), replacing unsafe library functions + with safer versions, making sure return codes are being checked, etc. + + - Where appropriate, replace preprocessor defined macros and values with + compile-time equivalents. + + - Style guide compliance. See: docs/style-guide.txt + + - Add testcases to tests/testcases. + + - Makefile improvements: + http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html + (I think the recursive problems are pretty much taken care of at this point, non?) + + - "Ten Commandments" compliance: (this is a "maybe", certainly not as + important as any of the previous items.) + http://www.lysator.liu.se/c/ten-commandments.html + +Other useful links: + + - the comp.lang.c FAQ: http://web.onetelnet.ch/~twolf/tw/c/index.html#Sources + + + +Submitting Patches To Busybox +----------------------------- + +Here are some guidelines on how to submit a patch to Busybox. + + +Making A Patch +~~~~~~~~~~~~~~ + +If you've got anonymous CVS access set up, making a patch is simple. Just make +sure you're in the busybox/ directory and type 'cvs diff -bwu > mychanges.patch'. +You can send the resulting .patch file to the mailing list with a description +of what it does. (But not before you test it! See the next section for some +guidelines.) It is preferred that patches be sent as attachments, but it is +not required. + +Also, feel free to help test other people's patches and reply to them with +comments. You can apply a patch by saving it into your busybox/ directory and +typing 'patch < mychanges.patch'. Then you can recompile, see if it runs, test +if it works as advertised, and post your findings to the mailing list. + +NOTE: Please do not include extraneous or irrelevant changes in your patches. +Please do not try to "bundle" two patches together into one. Make single, +discreet changes on a per-patch basis. Sometimes you need to make a patch that +touches code in many places, but these kind of patches are rare and should be +coordinated with a maintainer. + + +Testing Guidelines +~~~~~~~~~~~~~~~~~~ + +It's considered good form to test your new feature before you submit a patch +to the mailing list, and especially before you commit a change to CVS. Here +are some guidelines on how to test your changes. + + - Always test Busybox applets against GNU counterparts and make sure the + behavior / output is identical between the two. + + - Try several different permutations and combinations of the features you're + adding (i.e., different combinations of command-line switches) and make sure + they all work; make sure one feature does not interfere with another. + + - Make sure you test compiling against the source both with the feature + turned on and turned off in Config.h and make sure Busybox compiles cleanly + both ways. + + - Run the multibuild.pl script in the tests directory and make sure + everything checks out OK. (Do this from within the busybox/ directory by + typing: 'tests/multibuild.pl'.) + + +Making Sure Your Patch Doesn't Get Lost +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you don't want your patch to be lost or forgotten, send it to the busybox +mailing list with a subject line something like this: + + [PATCH] - Adds "transmogrify" feature to "foo" + +In the body, you should have a pseudo-header that looks like the following: + + Package: busybox + Version: v1.01pre (or whatever the current version is) + Severity: wishlist + +The remainder of the body should read along these lines: + + This patch adds the "transmogrify" feature to the "foo" applet. I have + tested this on [arch] system(s) and it works. I have tested it against the + GNU counterparts and the outputs are identical. I have run the scripts in + the 'tests' directory and nothing breaks. + + + +Improving Your Chances of Patch Acceptance +------------------------------------------ + +Even after you send a brilliant patch to the mailing list, sometimes it can go +unnoticed, un-replied-to, and sometimes (sigh) even lost. This is an +unfortunate fact of life, but there are steps you can take to help your patch +get noticed and convince a maintainer that it should be added: + + +Be Succinct +~~~~~~~~~~~ + +A patch that includes small, isolated, obvious changes is more likely to be +accepted than a patch that touches code in lots of different places or makes +sweeping, dubious changes. + + +Back It Up +~~~~~~~~~~ + +Hard facts on why your patch is better than the existing code will go a long +way toward convincing maintainers that your patch should be included. +Specifically, patches are more likely to be accepted if they are provably more +correct, smaller, faster, simpler, or more maintainable than the existing +code. + +Conversely, any patch that is supported with nothing more than "I think this +would be cool" or "this patch is good because I say it is and I've got a Phd +in Computer Science" will likely be ignored. + + +Follow The Style Guide +~~~~~~~~~~~~~~~~~~~~~~ + +It's considered good form to abide by the established coding style used in a +project; Busybox is no exception. We have gone so far as to delineate the +"elements of Busybox style" in the file docs/style-guide.txt. Please follow +them. + + +Work With Someone Else +~~~~~~~~~~~~~~~~~~~~~~ + +Working on a patch in isolation is less effective than working with someone +else for a variety of reasons. If another Busybox user is interested in what +you're doing, then it's two (or more) voices instead of one that can petition +for inclusion of the patch. You'll also have more people that can test your +changes, or even offer suggestions on better approaches you could take. + +Getting other folks interested follows as a natural course if you've received +responses from queries to applet maintainer or positive responses from folks +on the mailing list. + +We've made strident efforts to put a useful "collaboration" infrastructure in +place in the form of mailing lists, the bug tracking system, and CVS. Please +use these resources. + + +Send Patches to the Bug Tracking System +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This was mentioned above in the "Making Sure Your Patch Doesn't Get Lost" +section, but it is worth mentioning again. A patch sent to the mailing list +might be unnoticed and forgotten. A patch sent to the bug tracking system will +be stored and closely connected to the bug it fixes. + + +Be Polite +~~~~~~~~~ + +The old saying "You'll catch more flies with honey than you will with vinegar" +applies when submitting patches to the mailing list for approval. The way you +present your patch is sometimes just as important as the actual patch itself +(if not more so). Being rude to the maintainers is not an effective way to +convince them that your patch should be included; it will likely have the +opposite effect. + + + +Committing Changes to CVS +------------------------- + +If you submit several patches that demonstrate that you are a skilled and wise +coder, you may be invited to become a committer, thus enabling you to commit +changes directly to CVS. This is nice because you don't have to wait for +someone else to commit your change for you, you can just do it yourself. + +But note that this is a priviledge that comes with some responsibilities. You +should test your changes before you commit them. You should also talk to an +applet maintainer before you make any kind of sweeping changes to somebody +else's code. Big changes should still go to the mailing list first. Remember, +being wise, polite, and discreet is more important than being clever. + + +When To Commit +~~~~~~~~~~~~~~ + +Generally, you should feel free to commit a change if: + + - Your changes are small and don't touch many files + - You are fixing a bug + - Somebody has told you that it's okay + - It's obviously the Right Thing + +The more of the above are true, the better it is to just commit a change +directly to CVS. + + +When Not To Commit +~~~~~~~~~~~~~~~~~~ + +Even if you have commit rights, you should probably still post a patch to the +mailing list if: + + - Your changes are broad and touch many different files + - You are adding a feature + - Your changes are speculative or experimental (i.e., trying a new algorithm) + - You are not the maintainer and your changes make the maintainer cringe + +The more of the above are true, the better it is to post a patch to the +mailing list instead of committing. + + + +Final Words +----------- + +If all of this seems complicated, don't panic, it's really not that tough. If +you're having difficulty following some of the steps outlined in this +document don't worry, the folks on the Busybox mailing list are a fairly +good-natured bunch and will work with you to help get your patches into shape +or help you make contributions. + + diff --git a/busybox/docs/new-applet-HOWTO.txt b/busybox/docs/new-applet-HOWTO.txt new file mode 100644 index 000000000..2fc95d36d --- /dev/null +++ b/busybox/docs/new-applet-HOWTO.txt @@ -0,0 +1,163 @@ +How to Add a New Applet to BusyBox +================================== + +This document details the steps you must take to add a new applet to BusyBox. + +Credits: +Matt Kraai - initial writeup +Mark Whitley - the remix +Thomas Lundquist - Added stuff for the new directory layout. + +Initial Write +------------- + +First, write your applet. Be sure to include copyright information at the +top, such as who you stole the code from and so forth. Also include the +mini-GPL boilerplate. Be sure to name the main function _main instead +of main. And be sure to put it in .c. Usage do not have to be taken care of by your applet. + +For a new applet mu, here is the code that would go in mu.c: + +----begin example code------ + +/* vi: set sw=4 ts=4: */ +/* + * Mini mu implementation for busybox + * + * + * Copyright (C) [YEAR] by [YOUR NAME] + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + * + */ + +#include "busybox.h" + +int mu_main(int argc, char **argv) +{ + int fd; + char mu; + + if ((fd = open("/dev/random", O_RDONLY)) < 0) + perror_msg_and_die("/dev/random"); + + if ((n = safe_read(fd, &mu, 1)) < 1) + perror_msg_and_die("/dev/random"); + + return mu; +} + +----end example code------ + + +Coding Style +------------ + +Before you submit your applet for inclusion in BusyBox, (or better yet, before +you _write_ your applet) please read through the style guide in the docs +directory and make your program compliant. + + +Some Words on libbb +------------------- + +As you are writing your applet, please be aware of the body of pre-existing +useful functions in libbb. Use these instead of reinventing the wheel. + +Additionally, if you have any useful, general-purpose functions in your +program that could be useful in another program, consider putting them in +libbb. + + +Placement / Directory +--------------------- + +Find the appropriate directory for your new applet. + +Make sure you find the appropriate places in the files, the applets are +sorted alphabetically. + +Add the applet to Makefile.in in the chosen applet directory: + +obj-$(CONFIG_MU) += mu.o + +Add the applet to Config.in in the chosen applet directory: + +config CONFIG_MU + bool "MU" + default n + help + Returns an indeterminate value. + + +Usage String(s) +--------------- + +Next, add usage information for you applet to include/usage.h. +This should look like the following: + + #define mu_trivial_usage \ + "-[abcde] FILES" + #define mu_full_usage \ + "Returns an indeterminate value.\n\n" \ + "Options:\n" \ + "\t-a\t\tfirst function\n" \ + "\t-b\t\tsecond function\n" \ + +If your program supports flags, the flags should be mentioned on the first +line (-[abcde]) and a detailed description of each flag should go in the +mu_full_usage section, one flag per line. (Numerous examples of this +currently exist in usage.h.) + + +Header Files +------------ + +Next, add an entry to include/applets.h. Be *sure* to keep the list +in alphabetical order, or else it will break the binary-search lookup +algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily: + + /* all programs above here are alphabetically "less than" 'mu' */ + #ifdef CONFIG_MU + APPLET("mu", mu_main, _BB_DIR_USR_BIN, mu_usage) + #endif + /* all programs below here are alphabetically "greater than" 'mu' */ + + +Finally, add a define for your applet to include/config.h + + #undef CONFIG_MU + + +Documentation +------------- + +If you're feeling especially nice, you should also document your applet in the +docs directory (but nobody ever does that). + +Adding some text to docs/Configure.help is a nice start. + + +The Grand Announcement +---------------------- + +Then create a diff -urN of the files you added (.c, +include/usage.c, include/applets.h, include/config.h, /Makefile.in, /config.in) +and send it to the mailing list: +busybox@busybox.net. + +Sending patches as attachments is preferred, but not required. + diff --git a/busybox/docs/style-guide.txt b/busybox/docs/style-guide.txt new file mode 100644 index 000000000..915d9b27d --- /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 'examples/mk2knr.pl files-to-convert'. This + does not do the actual conversion, rather, it generates a script called + 'convertme.pl' that shows what will be converted, giving you a chance to + review the changes beforehand. + + - Review the 'convertme.pl' script that gets generated in the busybox + directory and remove / edit any of the substitutions in there. Please + especially check for false positives (strings that should not be + converted). + + - Type './convertme.pl same-files-as-before' to perform the actual + conversion. + + - Compile and see if everything still works. + +Please be aware of changes that have cascading effects into other files. For +example, if you're changing the name of something in, say utility.c, you +should probably run 'examples/mk2knr.pl utility.c' at first, but when you run +the 'convertme.pl' script you should run it on _all_ files like so: +'./convertme.pl *.[ch]'. + + + +Avoid The Preprocessor +---------------------- + +At best, the preprocessor is a necessary evil, helping us account for platform +and architecture differences. Using the preprocessor unnecessarily is just +plain evil. + + +The Folly of #define +~~~~~~~~~~~~~~~~~~~~ + +Use 'const var' for declaring constants. + + Don't do this: + + #define var 80 + + Do this instead, when the variable is in a header file and will be used in + several source files: + + const int var = 80; + + Or do this when the variable is used only in a single source file: + + static const int var = 80; + +Declaring variables as '[static] const' gives variables an actual type and +makes the compiler do type checking for you; the preprocessor does _no_ type +checking whatsoever, making it much more error prone. Declaring variables with +'[static] const' also makes debugging programs much easier since the value of +the variable can be easily queried and displayed. + + +The Folly of Macros +~~~~~~~~~~~~~~~~~~~ + +Use 'static inline' instead of a macro. + + Don't do this: + + #define mini_func(param1, param2) (param1 << param2) + + Do this instead: + + static inline int mini_func(int param1, param2) + { + return (param1 << param2); + } + +Static inline functions are greatly preferred over macros. They provide type +safety, have no length limitations, no formatting limitations, have an actual +return value, and under gcc they are as cheap as macros. Besides, really long +macros with backslashes at the end of each line are ugly as sin. + + +The Folly of #ifdef +~~~~~~~~~~~~~~~~~~~ + +Code cluttered with ifdefs is difficult to read and maintain. Don't do it. +Instead, put your ifdefs at the top of your .c file (or in a header), and +conditionally define 'static inline' functions, (or *maybe* macros), which are +used in the code. + + Don't do this: + + ret = my_func(bar, baz); + if (!ret) + return -1; + #ifdef CONFIG_FEATURE_FUNKY + maybe_do_funky_stuff(bar, baz); + #endif + + Do this instead: + + (in .h header file) + + #ifdef CONFIG_FEATURE_FUNKY + static inline void maybe_do_funky_stuff (int bar, int baz) + { + /* lotsa code in here */ + } + #else + static inline void maybe_do_funky_stuff (int bar, int baz) {} + #endif + + (in the .c source file) + + ret = my_func(bar, baz); + if (!ret) + return -1; + maybe_do_funky_stuff(bar, baz); + +The great thing about this approach is that the compiler will optimize away +the "no-op" case (the empty function) when the feature is turned off. + +Note also the use of the word 'maybe' in the function name to indicate +conditional execution. + + + +Notes on Strings +---------------- + +Strings in C can get a little thorny. Here's some guidelines for dealing with +strings in Busybox. (There is surely more that could be added to this +section.) + + +String Files +~~~~~~~~~~~~ + +Put all help/usage messages in usage.c. Put other strings in messages.c. +Putting these strings into their own file is a calculated decision designed to +confine spelling errors to a single place and aid internationalization +efforts, if needed. (Side Note: we might want to use a single file - maybe +called 'strings.c' - instead of two, food for thought). + + +Testing String Equivalence +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There's a right way and a wrong way to test for sting equivalence with +strcmp(): + + The wrong way: + + if (!strcmp(string, "foo")) { + ... + + The right way: + + if (strcmp(string, "foo") == 0){ + ... + +The use of the "equals" (==) operator in the latter example makes it much more +obvious that you are testing for equivalence. The former example with the +"not" (!) operator makes it look like you are testing for an error. In a more +perfect world, we would have a streq() function in the string library, but +that ain't the world we're living in. + + +Avoid Dangerous String Functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unfortunately, the way C handles strings makes them prone to overruns when +certain library functions are (mis)used. The following table offers a summary +of some of the more notorious troublemakers: + +function overflows preferred +---------------------------------------- +strcpy dest string strncpy +strcat dest string strncat +gets string it gets fgets +getwd buf string getcwd +[v]sprintf str buffer [v]snprintf +realpath path buffer use with pathconf +[vf]scanf its arguments just avoid it + + +The above is by no means a complete list. Be careful out there. + + + +Avoid Big Static Buffers +------------------------ + +First, some background to put this discussion in context: Static buffers look +like this in code: + + /* in a .c file outside any functions */ + static char *buffer[BUFSIZ]; /* happily used by any function in this file, + but ick! big! */ + +The problem with these is that any time any busybox app is run, you pay a +memory penalty for this buffer, even if the applet that uses said buffer is +not run. This can be fixed, thusly: + + static char *buffer; + ... + other_func() + { + strcpy(buffer, lotsa_chars); /* happily uses global *buffer */ + ... + foo_main() + { + buffer = xmalloc(sizeof(char)*BUFSIZ); + ... + +However, this approach trades bss segment for text segment. Rather than +mallocing the buffers (and thus growing the text size), buffers can be +declared on the stack in the *_main() function and made available globally by +assigning them to a global pointer thusly: + + static char *pbuffer; + ... + other_func() + { + strcpy(pbuffer, lotsa_chars); /* happily uses global *pbuffer */ + ... + foo_main() + { + char *buffer[BUFSIZ]; /* declared locally, on stack */ + pbuffer = buffer; /* but available globally */ + ... + +This last approach has some advantages (low code size, space not used until +it's needed), but can be a problem in some low resource machines that have +very limited stack space (e.g., uCLinux). + +A macro is declared in busybox.h that implements compile-time selection +between xmalloc() and stack creation, so you can code the line in question as + + RESERVE_CONFIG_BUFFER(buffer, BUFSIZ); + +and the right thing will happen, based on your configuration. + + + +Miscellaneous Coding Guidelines +------------------------------- + +The following are important items that don't fit into any of the above +sections. + + +Model Busybox Applets After GNU Counterparts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When in doubt about the proper behavior of a Busybox program (output, +formatting, options, etc.), model it after the equivalent GNU program. +Doesn't matter how that program behaves on some other flavor of *NIX; doesn't +matter what the POSIX standard says or doesn't say, just model Busybox +programs after their GNU counterparts and it will make life easier on (nearly) +everyone. + +The only time we deviate from emulating the GNU behavior is when: + + - We are deliberately not supporting a feature (such as a command line + switch) + - Emulating the GNU behavior is prohibitively expensive (lots more code + would be required, lots more memory would be used, etc.) + - The difference is minor or cosmetic + +A note on the 'cosmetic' case: Output differences might be considered +cosmetic, but if the output is significant enough to break other scripts that +use the output, it should really be fixed. + + +Scope +~~~~~ + +If a const variable is used only in a single source file, put it in the source +file and not in a header file. Likewise, if a const variable is used in only +one function, do not make it global to the file. Instead, declare it inside +the function body. Bottom line: Make a conscious effort to limit declarations +to the smallest scope possible. + +Inside applet files, all functions should be declared static so as to keep the +global name space clean. The only exception to this rule is the "applet_main" +function which must be declared extern. + +If you write a function that performs a task that could be useful outside the +immediate file, turn it into a general-purpose function with no ties to any +applet and put it in the utility.c file instead. + + +Brackets Are Your Friends +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please use brackets on all if and else statements, even if it is only one +line. Example: + + Don't do this: + + if (foo) + stmt1; + stmt2 + stmt3; + + Do this instead: + + if (foo) { + stmt1; + } + stmt2 + stmt3; + +The "bracketless" approach is error prone because someday you might add a line +like this: + + if (foo) + stmt1; + new_line(); + stmt2 + stmt3; + +And the resulting behavior of your program would totally bewilder you. (Don't +laugh, it happens to us all.) Remember folks, this is C, not Python. + + +Function Declarations +~~~~~~~~~~~~~~~~~~~~~ + +Do not use old-style function declarations that declare variable types between +the parameter list and opening bracket. Example: + + Don't do this: + + int foo(parm1, parm2) + char parm1; + float parm2; + { + .... + + Do this instead: + + int foo(char parm1, float parm2) + { + .... + +The only time you would ever need to use the old declaration syntax is to +support ancient, antediluvian compilers. To our good fortune, we have access +to more modern compilers and the old declaration syntax is neither necessary +nor desired. + + +Emphasizing Logical Blocks +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Organization and readability are improved by putting extra newlines around +blocks of code that perform a single task. These are typically blocks that +begin with a C keyword, but not always. + +Furthermore, you should put a single comment (not necessarily one line, just +one comment) before the block, rather than commenting each and every line. +There is an optimal ammount of commenting that a program can have; you can +comment too much as well as too little. + +A picture is really worth a thousand words here, the following example +illustrates how to emphasize logical blocks: + + while (line = get_line_from_file(fp)) { + + /* eat the newline, if any */ + chomp(line); + + /* ignore blank lines */ + if (strlen(file_to_act_on) == 0) { + continue; + } + + /* if the search string is in this line, print it, + * unless we were told to be quiet */ + if (strstr(line, search) && !be_quiet) { + puts(line); + } + + /* clean up */ + free(line); + } + + +Processing Options with getopt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your applet needs to process command-line switches, please use getopt() to +do so. Numerous examples can be seen in many of the existing applets, but +basically it boils down to two things: at the top of the .c file, have this +line in the midst of your #includes: + + #include + +And a code block similar to the following near the top of your applet_main() +routine: + + while ((opt = getopt(argc, argv, "abc")) > 0) { + switch (opt) { + case 'a': + do_a_opt = 1; + break; + case 'b': + do_b_opt = 1; + break; + case 'c': + do_c_opt = 1; + break; + default: + show_usage(); /* in utility.c */ + } + } + +If your applet takes no options (such as 'init'), there should be a line +somewhere in the file reads: + + /* no options, no getopt */ + +That way, when people go grepping to see which applets need to be converted to +use getopt, they won't get false positives. + +Additional Note: Do not use the getopt_long library function and do not try to +hand-roll your own long option parsing. Busybox applets should only support +short options. Explanations and examples of the short options should be +documented in usage.h. diff --git a/busybox/editors/Config.in b/busybox/editors/Config.in new file mode 100644 index 000000000..bb0285976 --- /dev/null +++ b/busybox/editors/Config.in @@ -0,0 +1,123 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Editors" + +config CONFIG_AWK + bool "awk" + default n + help + Awk is used as a pattern scanning and processing language. This is + the BusyBox implementation of that programming language. + +config CONFIG_FEATURE_AWK_MATH + bool " Enable math functions (requires libm)" + default y + depends on CONFIG_AWK + help + Enable math functions of the Awk programming language. + NOTE: This will require libm to be present for linking. + +config CONFIG_PATCH + bool "patch" + default n + help + Apply a unified diff formatted patch. + +config CONFIG_SED + bool "sed" + default n + help + sed is used to perform text transformations on a file + or input from a pipeline. + +config CONFIG_VI + bool "vi" + default n + help + 'vi' is a text editor. More specifically, it is the One True + text editor . It does, however, have a rather steep + learning curve. If you are not already comfortable with 'vi' + you may wish to use something else. + +config CONFIG_FEATURE_VI_COLON + bool " Enable \":\" colon commands (no \"ex\" mode)" + default y + depends on CONFIG_VI + help + Enable a limited set of colon commands for vi. This does not + provide an "ex" mode. + +config CONFIG_FEATURE_VI_YANKMARK + bool " Enable yank/put commands and mark cmds" + default y + depends on CONFIG_VI + help + This will enable you to use yank and put, as well as mark in + busybox vi. + +config CONFIG_FEATURE_VI_SEARCH + bool " Enable search and replace cmds" + default y + depends on CONFIG_VI + help + Select this if you wish to be able to do search and replace in + busybox vi. + +config CONFIG_FEATURE_VI_USE_SIGNALS + bool " Catch signals" + default y + depends on CONFIG_VI + help + Selecting this option will make busybox vi signal aware. This will + make busybox vi support SIGWINCH to deal with Window Changes, catch + Ctrl-Z and Ctrl-C and alarms. + +config CONFIG_FEATURE_VI_DOT_CMD + bool " Remember previous cmd and \".\" cmd" + default y + depends on CONFIG_VI + help + Make busybox vi remember the last command and be able to repeat it. + +config CONFIG_FEATURE_VI_READONLY + bool " Enable -R option and \"view\" mode" + default y + depends on CONFIG_VI + help + Enable the read-only command line option, which allows the user to + open a file in read-only mode. + +config CONFIG_FEATURE_VI_SETOPTS + bool " Enable set-able options, ai ic showmatch" + default y + depends on CONFIG_VI + help + Enable the editor to set some (ai, ic, showmatch) options. + +config CONFIG_FEATURE_VI_SET + bool " Support for :set" + default y + depends on CONFIG_VI + help + Support for ":set". + +config CONFIG_FEATURE_VI_WIN_RESIZE + bool " Handle window resize" + default y + depends on CONFIG_VI + help + Make busybox vi behave nicely with terminals that get resized. + +config CONFIG_FEATURE_VI_OPTIMIZE_CURSOR + bool " Optimize cursor movement" + default y + depends on CONFIG_VI + help + This will make the cursor movement faster, but requires more memory + and it makes the applet a tiny bit larger. + +endmenu + diff --git a/busybox/editors/Makefile b/busybox/editors/Makefile new file mode 100644 index 000000000..e6c114781 --- /dev/null +++ b/busybox/editors/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/editors +EDITOR_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/editors/Makefile.in b/busybox/editors/Makefile.in new file mode 100644 index 000000000..571e05568 --- /dev/null +++ b/busybox/editors/Makefile.in @@ -0,0 +1,48 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +EDITOR_AR:=editors.a +ifndef $(EDITOR_DIR) +EDITOR_DIR:=$(top_builddir)/editors/ +endif +srcdir=$(top_srcdir)/editors + +EDITOR-y:= +EDITOR-$(CONFIG_AWK) += awk.o +EDITOR-$(CONFIG_PATCH) += patch.o +EDITOR-$(CONFIG_SED) += sed.o +EDITOR-$(CONFIG_VI) += vi.o +EDITOR_SRC:= $(EDITOR-y) +EDITOR_OBJ:= $(patsubst %.c,$(EDITOR_DIR)%.o, $(EDITOR_SRC)) + +libraries-y+=$(EDITOR_DIR)$(EDITOR_AR) + +needlibm-y:= +needlibm-$(CONFIG_FEATURE_AWK_MATH) := y + +ifeq ($(needlibm-y),y) + LIBRARIES += -lm +endif + +$(EDITOR_DIR)$(EDITOR_AR): $(patsubst %,$(EDITOR_DIR)%, $(EDITOR-y)) + $(AR) -ro $@ $(patsubst %,$(EDITOR_DIR)%, $(EDITOR-y)) + +$(EDITOR_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/editors/awk.c b/busybox/editors/awk.c new file mode 100644 index 000000000..c1cb2a2e2 --- /dev/null +++ b/busybox/editors/awk.c @@ -0,0 +1,2764 @@ +/* vi: set sw=4 ts=4: */ +/* + * awk implementation for busybox + * + * Copyright (C) 2002 by Dmitry Zakharov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + + +#define MAXVARFMT 240 +#define MINNVBLOCK 64 + +/* variable flags */ +#define VF_NUMBER 0x0001 /* 1 = primary type is number */ +#define VF_ARRAY 0x0002 /* 1 = it's an array */ + +#define VF_CACHED 0x0100 /* 1 = num/str value has cached str/num eq */ +#define VF_USER 0x0200 /* 1 = user input (may be numeric string) */ +#define VF_SPECIAL 0x0400 /* 1 = requires extra handling when changed */ +#define VF_WALK 0x0800 /* 1 = variable has alloc'd x.walker list */ +#define VF_FSTR 0x1000 /* 1 = string points to fstring buffer */ +#define VF_CHILD 0x2000 /* 1 = function arg; x.parent points to source */ +#define VF_DIRTY 0x4000 /* 1 = variable was set explicitly */ + +/* these flags are static, don't change them when value is changed */ +#define VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY) + +/* Variable */ +typedef struct var_s { + unsigned short type; /* flags */ + double number; + char *string; + union { + int aidx; /* func arg index (on compilation stage) */ + struct xhash_s *array; /* array ptr */ + struct var_s *parent; /* for func args, ptr to actual parameter */ + char **walker; /* list of array elements (for..in) */ + } x; +} var; + +/* Node chain (pattern-action chain, BEGIN, END, function bodies) */ +typedef struct chain_s { + struct node_s *first; + struct node_s *last; + char *programname; +} chain; + +/* Function */ +typedef struct func_s { + unsigned short nargs; + struct chain_s body; +} func; + +/* I/O stream */ +typedef struct rstream_s { + FILE *F; + char *buffer; + int adv; + int size; + int pos; + unsigned short is_pipe; +} rstream; + +typedef struct hash_item_s { + union { + struct var_s v; /* variable/array hash */ + struct rstream_s rs; /* redirect streams hash */ + struct func_s f; /* functions hash */ + } data; + struct hash_item_s *next; /* next in chain */ + char name[1]; /* really it's longer */ +} hash_item; + +typedef struct xhash_s { + unsigned int nel; /* num of elements */ + unsigned int csize; /* current hash size */ + unsigned int nprime; /* next hash size in PRIMES[] */ + unsigned int glen; /* summary length of item names */ + struct hash_item_s **items; +} xhash; + +/* Tree node */ +typedef struct node_s { + unsigned long info; + unsigned short lineno; + union { + struct node_s *n; + var *v; + int i; + char *s; + regex_t *re; + } l; + union { + struct node_s *n; + regex_t *ire; + func *f; + int argno; + } r; + union { + struct node_s *n; + } a; +} node; + +/* Block of temporary variables */ +typedef struct nvblock_s { + int size; + var *pos; + struct nvblock_s *prev; + struct nvblock_s *next; + var nv[0]; +} nvblock; + +typedef struct tsplitter_s { + node n; + regex_t re[2]; +} tsplitter; + +/* simple token classes */ +/* Order and hex values are very important!!! See next_token() */ +#define TC_SEQSTART 1 /* ( */ +#define TC_SEQTERM (1 << 1) /* ) */ +#define TC_REGEXP (1 << 2) /* /.../ */ +#define TC_OUTRDR (1 << 3) /* | > >> */ +#define TC_UOPPOST (1 << 4) /* unary postfix operator */ +#define TC_UOPPRE1 (1 << 5) /* unary prefix operator */ +#define TC_BINOPX (1 << 6) /* two-opnd operator */ +#define TC_IN (1 << 7) +#define TC_COMMA (1 << 8) +#define TC_PIPE (1 << 9) /* input redirection pipe */ +#define TC_UOPPRE2 (1 << 10) /* unary prefix operator */ +#define TC_ARRTERM (1 << 11) /* ] */ +#define TC_GRPSTART (1 << 12) /* { */ +#define TC_GRPTERM (1 << 13) /* } */ +#define TC_SEMICOL (1 << 14) +#define TC_NEWLINE (1 << 15) +#define TC_STATX (1 << 16) /* ctl statement (for, next...) */ +#define TC_WHILE (1 << 17) +#define TC_ELSE (1 << 18) +#define TC_BUILTIN (1 << 19) +#define TC_GETLINE (1 << 20) +#define TC_FUNCDECL (1 << 21) /* `function' `func' */ +#define TC_BEGIN (1 << 22) +#define TC_END (1 << 23) +#define TC_EOF (1 << 24) +#define TC_VARIABLE (1 << 25) +#define TC_ARRAY (1 << 26) +#define TC_FUNCTION (1 << 27) +#define TC_STRING (1 << 28) +#define TC_NUMBER (1 << 29) + +#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2) + +/* combined token classes */ +#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN) +#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST) +#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION | \ + TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER) + +#define TC_STATEMNT (TC_STATX | TC_WHILE) +#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE) + +/* word tokens, cannot mean something else if not expected */ +#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN | \ + TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END) + +/* discard newlines after these */ +#define TC_NOTERM (TC_COMMA | TC_GRPSTART | TC_GRPTERM | \ + TC_BINOP | TC_OPTERM) + +/* what can expression begin with */ +#define TC_OPSEQ (TC_OPERAND | TC_UOPPRE | TC_REGEXP) +/* what can group begin with */ +#define TC_GRPSEQ (TC_OPSEQ | TC_OPTERM | TC_STATEMNT | TC_GRPSTART) + +/* if previous token class is CONCAT1 and next is CONCAT2, concatenation */ +/* operator is inserted between them */ +#define TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM | \ + TC_STRING | TC_NUMBER | TC_UOPPOST) +#define TC_CONCAT2 (TC_OPERAND | TC_UOPPRE) + +#define OF_RES1 0x010000 +#define OF_RES2 0x020000 +#define OF_STR1 0x040000 +#define OF_STR2 0x080000 +#define OF_NUM1 0x100000 +#define OF_CHECKED 0x200000 + +/* combined operator flags */ +#define xx 0 +#define xV OF_RES2 +#define xS (OF_RES2 | OF_STR2) +#define Vx OF_RES1 +#define VV (OF_RES1 | OF_RES2) +#define Nx (OF_RES1 | OF_NUM1) +#define NV (OF_RES1 | OF_NUM1 | OF_RES2) +#define Sx (OF_RES1 | OF_STR1) +#define SV (OF_RES1 | OF_STR1 | OF_RES2) +#define SS (OF_RES1 | OF_STR1 | OF_RES2 | OF_STR2) + +#define OPCLSMASK 0xFF00 +#define OPNMASK 0x007F + +/* operator priority is a highest byte (even: r->l, odd: l->r grouping) + * For builtins it has different meaning: n n s3 s2 s1 v3 v2 v1, + * n - min. number of args, vN - resolve Nth arg to var, sN - resolve to string + */ +#define P(x) (x << 24) +#define PRIMASK 0x7F000000 +#define PRIMASK2 0x7E000000 + +/* Operation classes */ + +#define SHIFT_TIL_THIS 0x0600 +#define RECUR_FROM_THIS 0x1000 + +enum { + OC_DELETE=0x0100, OC_EXEC=0x0200, OC_NEWSOURCE=0x0300, + OC_PRINT=0x0400, OC_PRINTF=0x0500, OC_WALKINIT=0x0600, + + OC_BR=0x0700, OC_BREAK=0x0800, OC_CONTINUE=0x0900, + OC_EXIT=0x0a00, OC_NEXT=0x0b00, OC_NEXTFILE=0x0c00, + OC_TEST=0x0d00, OC_WALKNEXT=0x0e00, + + OC_BINARY=0x1000, OC_BUILTIN=0x1100, OC_COLON=0x1200, + OC_COMMA=0x1300, OC_COMPARE=0x1400, OC_CONCAT=0x1500, + OC_FBLTIN=0x1600, OC_FIELD=0x1700, OC_FNARG=0x1800, + OC_FUNC=0x1900, OC_GETLINE=0x1a00, OC_IN=0x1b00, + OC_LAND=0x1c00, OC_LOR=0x1d00, OC_MATCH=0x1e00, + OC_MOVE=0x1f00, OC_PGETLINE=0x2000, OC_REGEXP=0x2100, + OC_REPLACE=0x2200, OC_RETURN=0x2300, OC_SPRINTF=0x2400, + OC_TERNARY=0x2500, OC_UNARY=0x2600, OC_VAR=0x2700, + OC_DONE=0x2800, + + ST_IF=0x3000, ST_DO=0x3100, ST_FOR=0x3200, + ST_WHILE=0x3300 +}; + +/* simple builtins */ +enum { + F_in=0, F_rn, F_co, F_ex, F_lg, F_si, F_sq, F_sr, + F_ti, F_le, F_sy, F_ff, F_cl +}; + +/* builtins */ +enum { + B_a2=0, B_ix, B_ma, B_sp, B_ss, B_ti, B_lo, B_up, + B_ge, B_gs, B_su +}; + +/* tokens and their corresponding info values */ + +#define NTC "\377" /* switch to next token class (tc<<1) */ +#define NTCC '\377' + +#define OC_B OC_BUILTIN + +static char * const tokenlist = + "\1(" NTC + "\1)" NTC + "\1/" NTC /* REGEXP */ + "\2>>" "\1>" "\1|" NTC /* OUTRDR */ + "\2++" "\2--" NTC /* UOPPOST */ + "\2++" "\2--" "\1$" NTC /* UOPPRE1 */ + "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */ + "\2*=" "\2/=" "\2%=" "\2^=" + "\1+" "\1-" "\3**=" "\2**" + "\1/" "\1%" "\1^" "\1*" + "\2!=" "\2>=" "\2<=" "\1>" + "\1<" "\2!~" "\1~" "\2&&" + "\2||" "\1?" "\1:" NTC + "\2in" NTC + "\1," NTC + "\1|" NTC + "\1+" "\1-" "\1!" NTC /* UOPPRE2 */ + "\1]" NTC + "\1{" NTC + "\1}" NTC + "\1;" NTC + "\1\n" NTC + "\2if" "\2do" "\3for" "\5break" /* STATX */ + "\10continue" "\6delete" "\5print" + "\6printf" "\4next" "\10nextfile" + "\6return" "\4exit" NTC + "\5while" NTC + "\4else" NTC + + "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */ + "\3cos" "\3exp" "\3int" "\3log" + "\4rand" "\3sin" "\4sqrt" "\5srand" + "\6gensub" "\4gsub" "\5index" "\6length" + "\5match" "\5split" "\7sprintf" "\3sub" + "\6substr" "\7systime" "\10strftime" + "\7tolower" "\7toupper" NTC + "\7getline" NTC + "\4func" "\10function" NTC + "\5BEGIN" NTC + "\3END" "\0" + ; + +static unsigned long tokeninfo[] = { + + 0, + 0, + OC_REGEXP, + xS|'a', xS|'w', xS|'|', + OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m', + OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M', + OC_FIELD|xV|P(5), + OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), + OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', + OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', + OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', + OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', + OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', + OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', + OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', + OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, + OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, + OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', + OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55), + OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', + OC_COLON|xx|P(67)|':', + OC_IN|SV|P(49), + OC_COMMA|SS|P(80), + OC_PGETLINE|SV|P(37), + OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-', + OC_UNARY|xV|P(19)|'!', + 0, + 0, + 0, + 0, + 0, + ST_IF, ST_DO, ST_FOR, OC_BREAK, + OC_CONTINUE, OC_DELETE|Vx, OC_PRINT, + OC_PRINTF, OC_NEXT, OC_NEXTFILE, + OC_RETURN|Vx, OC_EXIT|Nx, + ST_WHILE, + 0, + + OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83), + OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg, + OC_FBLTIN|F_rn, OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr, + OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le, + OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6), + OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b), + OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49), + OC_GETLINE|SV|P(0), + 0, 0, + 0, + 0 +}; + +/* internal variable names and their initial values */ +/* asterisk marks SPECIAL vars; $ is just no-named Field0 */ +enum { + CONVFMT=0, OFMT, FS, OFS, + ORS, RS, RT, FILENAME, + SUBSEP, ARGIND, ARGC, ARGV, + ERRNO, FNR, + NR, NF, IGNORECASE, + ENVIRON, F0, _intvarcount_ +}; + +static char * vNames = + "CONVFMT\0" "OFMT\0" "FS\0*" "OFS\0" + "ORS\0" "RS\0*" "RT\0" "FILENAME\0" + "SUBSEP\0" "ARGIND\0" "ARGC\0" "ARGV\0" + "ERRNO\0" "FNR\0" + "NR\0" "NF\0*" "IGNORECASE\0*" + "ENVIRON\0" "$\0*" "\0"; + +static char * vValues = + "%.6g\0" "%.6g\0" " \0" " \0" + "\n\0" "\n\0" "\0" "\0" + "\034\0" + "\377"; + +/* hash size may grow to these values */ +#define FIRST_PRIME 61; +static const unsigned int PRIMES[] = { 251, 1021, 4093, 16381, 65521 }; +static const unsigned int NPRIMES = sizeof(PRIMES) / sizeof(unsigned int); + +/* globals */ + +extern char **environ; + +static var * V[_intvarcount_]; +static chain beginseq, mainseq, endseq, *seq; +static int nextrec, nextfile; +static node *break_ptr, *continue_ptr; +static rstream *iF; +static xhash *vhash, *ahash, *fdhash, *fnhash; +static char *programname; +static short lineno; +static int is_f0_split; +static int nfields = 0; +static var *Fields = NULL; +static tsplitter fsplitter, rsplitter; +static nvblock *cb = NULL; +static char *pos; +static char *buf; +static int icase = FALSE; +static int exiting = FALSE; + +static struct { + unsigned long tclass; + unsigned long info; + char *string; + double number; + short lineno; + int rollback; +} t; + +/* function prototypes */ +extern void xregcomp(regex_t *preg, const char *regex, int cflags); +static void handle_special(var *); +static node *parse_expr(unsigned long); +static void chain_group(void); +static var *evaluate(node *, var *); +static rstream *next_input_file(void); +static int fmt_num(char *, int, char *, double, int); +static int awk_exit(int); + +/* ---- error handling ---- */ + +static const char EMSG_INTERNAL_ERROR[] = "Internal error"; +static const char EMSG_UNEXP_EOS[] = "Unexpected end of string"; +static const char EMSG_UNEXP_TOKEN[] = "Unexpected token"; +static const char EMSG_DIV_BY_ZERO[] = "Division by zero"; +static const char EMSG_INV_FMT[] = "Invalid format specifier"; +static const char EMSG_TOO_FEW_ARGS[] = "Too few arguments for builtin"; +static const char EMSG_NOT_ARRAY[] = "Not an array"; +static const char EMSG_POSSIBLE_ERROR[] = "Possible syntax error"; +static const char EMSG_UNDEF_FUNC[] = "Call to undefined function"; +#ifndef CONFIG_FEATURE_AWK_MATH +static const char EMSG_NO_MATH[] = "Math support is not compiled in"; +#endif + +static void syntax_error(const char * const message) +{ + bb_error_msg("%s:%i: %s", programname, lineno, message); + exit(1); +} + +#define runtime_error(x) syntax_error(x) + + +/* ---- hash stuff ---- */ + +static unsigned int hashidx(char *name) { + + register unsigned int idx=0; + + while (*name) idx = *name++ + (idx << 6) - idx; + return idx; +} + +/* create new hash */ +static xhash *hash_init(void) { + + xhash *newhash; + + newhash = (xhash *)xcalloc(1, sizeof(xhash)); + newhash->csize = FIRST_PRIME; + newhash->items = (hash_item **)xcalloc(newhash->csize, sizeof(hash_item *)); + + return newhash; +} + +/* find item in hash, return ptr to data, NULL if not found */ +static void *hash_search(xhash *hash, char *name) { + + hash_item *hi; + + hi = hash->items [ hashidx(name) % hash->csize ]; + while (hi) { + if (strcmp(hi->name, name) == 0) + return &(hi->data); + hi = hi->next; + } + return NULL; +} + +/* grow hash if it becomes too big */ +static void hash_rebuild(xhash *hash) { + + unsigned int newsize, i, idx; + hash_item **newitems, *hi, *thi; + + if (hash->nprime == NPRIMES) + return; + + newsize = PRIMES[hash->nprime++]; + newitems = (hash_item **)xcalloc(newsize, sizeof(hash_item *)); + + for (i=0; icsize; i++) { + hi = hash->items[i]; + while (hi) { + thi = hi; + hi = thi->next; + idx = hashidx(thi->name) % newsize; + thi->next = newitems[idx]; + newitems[idx] = thi; + } + } + + free(hash->items); + hash->csize = newsize; + hash->items = newitems; +} + +/* find item in hash, add it if necessary. Return ptr to data */ +static void *hash_find(xhash *hash, char *name) { + + hash_item *hi; + unsigned int idx; + int l; + + hi = hash_search(hash, name); + if (! hi) { + if (++hash->nel / hash->csize > 10) + hash_rebuild(hash); + + l = bb_strlen(name) + 1; + hi = xcalloc(sizeof(hash_item) + l, 1); + memcpy(hi->name, name, l); + + idx = hashidx(name) % hash->csize; + hi->next = hash->items[idx]; + hash->items[idx] = hi; + hash->glen += l; + } + return &(hi->data); +} + +#define findvar(hash, name) (var *) hash_find ( (hash) , (name) ) +#define newvar(name) (var *) hash_find ( vhash , (name) ) +#define newfile(name) (rstream *) hash_find ( fdhash , (name) ) +#define newfunc(name) (func *) hash_find ( fnhash , (name) ) + +static void hash_remove(xhash *hash, char *name) { + + hash_item *hi, **phi; + + phi = &(hash->items[ hashidx(name) % hash->csize ]); + while (*phi) { + hi = *phi; + if (strcmp(hi->name, name) == 0) { + hash->glen -= (bb_strlen(name) + 1); + hash->nel--; + *phi = hi->next; + free(hi); + break; + } + phi = &(hi->next); + } +} + +/* ------ some useful functions ------ */ + +static void skip_spaces(char **s) { + + register char *p = *s; + + while(*p == ' ' || *p == '\t' || + (*p == '\\' && *(p+1) == '\n' && (++p, ++t.lineno))) { + p++; + } + *s = p; +} + +static char *nextword(char **s) { + + register char *p = *s; + + while (*(*s)++) ; + + return p; +} + +static char nextchar(char **s) { + + register char c, *pps; + + c = *((*s)++); + pps = *s; + if (c == '\\') c = bb_process_escape_sequence((const char**)s); + if (c == '\\' && *s == pps) c = *((*s)++); + return c; +} + +static inline int isalnum_(int c) { + + return (isalnum(c) || c == '_'); +} + +static FILE *afopen(const char *path, const char *mode) { + + return (*path == '-' && *(path+1) == '\0') ? stdin : bb_xfopen(path, mode); +} + +/* -------- working with variables (set/get/copy/etc) -------- */ + +static xhash *iamarray(var *v) { + + var *a = v; + + while (a->type & VF_CHILD) + a = a->x.parent; + + if (! (a->type & VF_ARRAY)) { + a->type |= VF_ARRAY; + a->x.array = hash_init(); + } + return a->x.array; +} + +static void clear_array(xhash *array) { + + unsigned int i; + hash_item *hi, *thi; + + for (i=0; icsize; i++) { + hi = array->items[i]; + while (hi) { + thi = hi; + hi = hi->next; + free(thi->data.v.string); + free(thi); + } + array->items[i] = NULL; + } + array->glen = array->nel = 0; +} + +/* clear a variable */ +static var *clrvar(var *v) { + + if (!(v->type & VF_FSTR)) + free(v->string); + + v->type &= VF_DONTTOUCH; + v->type |= VF_DIRTY; + v->string = NULL; + return v; +} + +/* assign string value to variable */ +static var *setvar_p(var *v, char *value) { + + clrvar(v); + v->string = value; + handle_special(v); + + return v; +} + +/* same as setvar_p but make a copy of string */ +static var *setvar_s(var *v, char *value) { + + return setvar_p(v, (value && *value) ? bb_xstrdup(value) : NULL); +} + +/* same as setvar_s but set USER flag */ +static var *setvar_u(var *v, char *value) { + + setvar_s(v, value); + v->type |= VF_USER; + return v; +} + +/* set array element to user string */ +static void setari_u(var *a, int idx, char *s) { + + register var *v; + static char sidx[12]; + + sprintf(sidx, "%d", idx); + v = findvar(iamarray(a), sidx); + setvar_u(v, s); +} + +/* assign numeric value to variable */ +static var *setvar_i(var *v, double value) { + + clrvar(v); + v->type |= VF_NUMBER; + v->number = value; + handle_special(v); + return v; +} + +static char *getvar_s(var *v) { + + /* if v is numeric and has no cached string, convert it to string */ + if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { + fmt_num(buf, MAXVARFMT, getvar_s(V[CONVFMT]), v->number, TRUE); + v->string = bb_xstrdup(buf); + v->type |= VF_CACHED; + } + return (v->string == NULL) ? "" : v->string; +} + +static double getvar_i(var *v) { + + char *s; + + if ((v->type & (VF_NUMBER | VF_CACHED)) == 0) { + v->number = 0; + s = v->string; + if (s && *s) { + v->number = strtod(s, &s); + if (v->type & VF_USER) { + skip_spaces(&s); + if (*s != '\0') + v->type &= ~VF_USER; + } + } else { + v->type &= ~VF_USER; + } + v->type |= VF_CACHED; + } + return v->number; +} + +static var *copyvar(var *dest, var *src) { + + if (dest != src) { + clrvar(dest); + dest->type |= (src->type & ~VF_DONTTOUCH); + dest->number = src->number; + if (src->string) + dest->string = bb_xstrdup(src->string); + } + handle_special(dest); + return dest; +} + +static var *incvar(var *v) { + + return setvar_i(v, getvar_i(v)+1.); +} + +/* return true if v is number or numeric string */ +static int is_numeric(var *v) { + + getvar_i(v); + return ((v->type ^ VF_DIRTY) & (VF_NUMBER | VF_USER | VF_DIRTY)); +} + +/* return 1 when value of v corresponds to true, 0 otherwise */ +static int istrue(var *v) { + + if (is_numeric(v)) + return (v->number == 0) ? 0 : 1; + else + return (v->string && *(v->string)) ? 1 : 0; +} + +/* temporary variables allocator. Last allocated should be first freed */ +static var *nvalloc(int n) { + + nvblock *pb = NULL; + var *v, *r; + int size; + + while (cb) { + pb = cb; + if ((cb->pos - cb->nv) + n <= cb->size) break; + cb = cb->next; + } + + if (! cb) { + size = (n <= MINNVBLOCK) ? MINNVBLOCK : n; + cb = (nvblock *)xmalloc(sizeof(nvblock) + size * sizeof(var)); + cb->size = size; + cb->pos = cb->nv; + cb->prev = pb; + cb->next = NULL; + if (pb) pb->next = cb; + } + + v = r = cb->pos; + cb->pos += n; + + while (v < cb->pos) { + v->type = 0; + v->string = NULL; + v++; + } + + return r; +} + +static void nvfree(var *v) { + + var *p; + + if (v < cb->nv || v >= cb->pos) + runtime_error(EMSG_INTERNAL_ERROR); + + for (p=v; ppos; p++) { + if ((p->type & (VF_ARRAY|VF_CHILD)) == VF_ARRAY) { + clear_array(iamarray(p)); + free(p->x.array->items); + free(p->x.array); + } + if (p->type & VF_WALK) + free(p->x.walker); + + clrvar(p); + } + + cb->pos = v; + while (cb->prev && cb->pos == cb->nv) { + cb = cb->prev; + } +} + +/* ------- awk program text parsing ------- */ + +/* Parse next token pointed by global pos, place results into global t. + * If token isn't expected, give away. Return token class + */ +static unsigned long next_token(unsigned long expected) { + + char *p, *pp, *s; + char *tl; + unsigned long tc, *ti; + int l; + static int concat_inserted = FALSE; + static unsigned long save_tclass, save_info; + static unsigned long ltclass = TC_OPTERM; + + if (t.rollback) { + + t.rollback = FALSE; + + } else if (concat_inserted) { + + concat_inserted = FALSE; + t.tclass = save_tclass; + t.info = save_info; + + } else { + + p = pos; + + readnext: + skip_spaces(&p); + lineno = t.lineno; + if (*p == '#') + while (*p != '\n' && *p != '\0') p++; + + if (*p == '\n') + t.lineno++; + + if (*p == '\0') { + tc = TC_EOF; + + } else if (*p == '\"') { + /* it's a string */ + t.string = s = ++p; + while (*p != '\"') { + if (*p == '\0' || *p == '\n') + syntax_error(EMSG_UNEXP_EOS); + *(s++) = nextchar(&p); + } + p++; + *s = '\0'; + tc = TC_STRING; + + } else if ((expected & TC_REGEXP) && *p == '/') { + /* it's regexp */ + t.string = s = ++p; + while (*p != '/') { + if (*p == '\0' || *p == '\n') + syntax_error(EMSG_UNEXP_EOS); + if ((*s++ = *p++) == '\\') { + pp = p; + *(s-1) = bb_process_escape_sequence((const char **)&p); + if (*pp == '\\') *s++ = '\\'; + if (p == pp) *s++ = *p++; + } + } + p++; + *s = '\0'; + tc = TC_REGEXP; + + } else if (*p == '.' || isdigit(*p)) { + /* it's a number */ + t.number = strtod(p, &p); + if (*p == '.') + syntax_error(EMSG_UNEXP_TOKEN); + tc = TC_NUMBER; + + } else { + /* search for something known */ + tl = tokenlist; + tc = 0x00000001; + ti = tokeninfo; + while (*tl) { + l = *(tl++); + if (l == NTCC) { + tc <<= 1; + continue; + } + /* if token class is expected, token + * matches and it's not a longer word, + * then this is what we are looking for + */ + if ((tc & (expected | TC_WORD | TC_NEWLINE)) && + *tl == *p && strncmp(p, tl, l) == 0 && + !((tc & TC_WORD) && isalnum_(*(p + l)))) { + t.info = *ti; + p += l; + break; + } + ti++; + tl += l; + } + + if (! *tl) { + /* it's a name (var/array/function), + * otherwise it's something wrong + */ + if (! isalnum_(*p)) + syntax_error(EMSG_UNEXP_TOKEN); + + t.string = --p; + while(isalnum_(*(++p))) { + *(p-1) = *p; + } + *(p-1) = '\0'; + tc = TC_VARIABLE; + if (*p == '(') { + tc = TC_FUNCTION; + } else { + skip_spaces(&p); + if (*p == '[') { + p++; + tc = TC_ARRAY; + } + } + } + } + pos = p; + + /* skipping newlines in some cases */ + if ((ltclass & TC_NOTERM) && (tc & TC_NEWLINE)) + goto readnext; + + /* insert concatenation operator when needed */ + if ((ltclass&TC_CONCAT1) && (tc&TC_CONCAT2) && (expected&TC_BINOP)) { + concat_inserted = TRUE; + save_tclass = tc; + save_info = t.info; + tc = TC_BINOP; + t.info = OC_CONCAT | SS | P(35); + } + + t.tclass = tc; + } + ltclass = t.tclass; + + /* Are we ready for this? */ + if (! (ltclass & expected)) + syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ? + EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN); + + return ltclass; +} + +static void rollback_token(void) { t.rollback = TRUE; } + +static node *new_node(unsigned long info) { + + register node *n; + + n = (node *)xcalloc(sizeof(node), 1); + n->info = info; + n->lineno = lineno; + return n; +} + +static node *mk_re_node(char *s, node *n, regex_t *re) { + + n->info = OC_REGEXP; + n->l.re = re; + n->r.ire = re + 1; + xregcomp(re, s, REG_EXTENDED); + xregcomp(re+1, s, REG_EXTENDED | REG_ICASE); + + return n; +} + +static node *condition(void) { + + next_token(TC_SEQSTART); + return parse_expr(TC_SEQTERM); +} + +/* parse expression terminated by given argument, return ptr + * to built subtree. Terminator is eaten by parse_expr */ +static node *parse_expr(unsigned long iexp) { + + node sn; + node *cn = &sn; + node *vn, *glptr; + unsigned long tc, xtc; + var *v; + + sn.info = PRIMASK; + sn.r.n = glptr = NULL; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp; + + while (! ((tc = next_token(xtc)) & iexp)) { + if (glptr && (t.info == (OC_COMPARE|VV|P(39)|2))) { + /* input redirection (<) attached to glptr node */ + cn = glptr->l.n = new_node(OC_CONCAT|SS|P(37)); + cn->a.n = glptr; + xtc = TC_OPERAND | TC_UOPPRE; + glptr = NULL; + + } else if (tc & (TC_BINOP | TC_UOPPOST)) { + /* for binary and postfix-unary operators, jump back over + * previous operators with higher priority */ + vn = cn; + while ( ((t.info & PRIMASK) > (vn->a.n->info & PRIMASK2)) || + ((t.info == vn->info) && ((t.info & OPCLSMASK) == OC_COLON)) ) + vn = vn->a.n; + if ((t.info & OPCLSMASK) == OC_TERNARY) + t.info += P(6); + cn = vn->a.n->r.n = new_node(t.info); + cn->a.n = vn->a.n; + if (tc & TC_BINOP) { + cn->l.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP; + if ((t.info & OPCLSMASK) == OC_PGETLINE) { + /* it's a pipe */ + next_token(TC_GETLINE); + /* give maximum priority to this pipe */ + cn->info &= ~PRIMASK; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + } + } else { + cn->r.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + } + vn->a.n = cn; + + } else { + /* for operands and prefix-unary operators, attach them + * to last node */ + vn = cn; + cn = vn->r.n = new_node(t.info); + cn->a.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP; + if (tc & (TC_OPERAND | TC_REGEXP)) { + xtc = TC_UOPPRE | TC_BINOP | TC_OPERAND | iexp; + /* one should be very careful with switch on tclass - + * only simple tclasses should be used! */ + switch (tc) { + case TC_VARIABLE: + case TC_ARRAY: + cn->info = OC_VAR; + if ((v = hash_search(ahash, t.string)) != NULL) { + cn->info = OC_FNARG; + cn->l.i = v->x.aidx; + } else { + cn->l.v = newvar(t.string); + } + if (tc & TC_ARRAY) { + cn->info |= xS; + cn->r.n = parse_expr(TC_ARRTERM); + } + xtc = TC_UOPPOST | TC_UOPPRE | TC_BINOP | TC_OPERAND | iexp; + break; + + case TC_NUMBER: + case TC_STRING: + cn->info = OC_VAR; + v = cn->l.v = xcalloc(sizeof(var), 1); + if (tc & TC_NUMBER) + setvar_i(v, t.number); + else + setvar_s(v, t.string); + break; + + case TC_REGEXP: + mk_re_node(t.string, cn, + (regex_t *)xcalloc(sizeof(regex_t),2)); + break; + + case TC_FUNCTION: + cn->info = OC_FUNC; + cn->r.f = newfunc(t.string); + cn->l.n = condition(); + break; + + case TC_SEQSTART: + cn = vn->r.n = parse_expr(TC_SEQTERM); + cn->a.n = vn; + break; + + case TC_GETLINE: + glptr = cn; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + break; + + case TC_BUILTIN: + cn->l.n = condition(); + break; + } + } + } + } + return sn.r.n; +} + +/* add node to chain. Return ptr to alloc'd node */ +static node *chain_node(unsigned long info) { + + register node *n; + + if (! seq->first) + seq->first = seq->last = new_node(0); + + if (seq->programname != programname) { + seq->programname = programname; + n = chain_node(OC_NEWSOURCE); + n->l.s = bb_xstrdup(programname); + } + + n = seq->last; + n->info = info; + seq->last = n->a.n = new_node(OC_DONE); + + return n; +} + +static void chain_expr(unsigned long info) { + + node *n; + + n = chain_node(info); + n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM); + if (t.tclass & TC_GRPTERM) + rollback_token(); +} + +static node *chain_loop(node *nn) { + + node *n, *n2, *save_brk, *save_cont; + + save_brk = break_ptr; + save_cont = continue_ptr; + + n = chain_node(OC_BR | Vx); + continue_ptr = new_node(OC_EXEC); + break_ptr = new_node(OC_EXEC); + chain_group(); + n2 = chain_node(OC_EXEC | Vx); + n2->l.n = nn; + n2->a.n = n; + continue_ptr->a.n = n2; + break_ptr->a.n = n->r.n = seq->last; + + continue_ptr = save_cont; + break_ptr = save_brk; + + return n; +} + +/* parse group and attach it to chain */ +static void chain_group(void) { + + unsigned long c; + node *n, *n2, *n3; + + do { + c = next_token(TC_GRPSEQ); + } while (c & TC_NEWLINE); + + if (c & TC_GRPSTART) { + while(next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) { + if (t.tclass & TC_NEWLINE) continue; + rollback_token(); + chain_group(); + } + } else if (c & (TC_OPSEQ | TC_OPTERM)) { + rollback_token(); + chain_expr(OC_EXEC | Vx); + } else { /* TC_STATEMNT */ + switch (t.info & OPCLSMASK) { + case ST_IF: + n = chain_node(OC_BR | Vx); + n->l.n = condition(); + chain_group(); + n2 = chain_node(OC_EXEC); + n->r.n = seq->last; + if (next_token(TC_GRPSEQ | TC_GRPTERM | TC_ELSE)==TC_ELSE) { + chain_group(); + n2->a.n = seq->last; + } else { + rollback_token(); + } + break; + + case ST_WHILE: + n2 = condition(); + n = chain_loop(NULL); + n->l.n = n2; + break; + + case ST_DO: + n2 = chain_node(OC_EXEC); + n = chain_loop(NULL); + n2->a.n = n->a.n; + next_token(TC_WHILE); + n->l.n = condition(); + break; + + case ST_FOR: + next_token(TC_SEQSTART); + n2 = parse_expr(TC_SEMICOL | TC_SEQTERM); + if (t.tclass & TC_SEQTERM) { /* for-in */ + if ((n2->info & OPCLSMASK) != OC_IN) + syntax_error(EMSG_UNEXP_TOKEN); + n = chain_node(OC_WALKINIT | VV); + n->l.n = n2->l.n; + n->r.n = n2->r.n; + n = chain_loop(NULL); + n->info = OC_WALKNEXT | Vx; + n->l.n = n2->l.n; + } else { /* for(;;) */ + n = chain_node(OC_EXEC | Vx); + n->l.n = n2; + n2 = parse_expr(TC_SEMICOL); + n3 = parse_expr(TC_SEQTERM); + n = chain_loop(n3); + n->l.n = n2; + if (! n2) + n->info = OC_EXEC; + } + break; + + case OC_PRINT: + case OC_PRINTF: + n = chain_node(t.info); + n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM); + if (t.tclass & TC_OUTRDR) { + n->info |= t.info; + n->r.n = parse_expr(TC_OPTERM | TC_GRPTERM); + } + if (t.tclass & TC_GRPTERM) + rollback_token(); + break; + + case OC_BREAK: + n = chain_node(OC_EXEC); + n->a.n = break_ptr; + break; + + case OC_CONTINUE: + n = chain_node(OC_EXEC); + n->a.n = continue_ptr; + break; + + /* delete, next, nextfile, return, exit */ + default: + chain_expr(t.info); + + } + } +} + +static void parse_program(char *p) { + + unsigned long tclass; + node *cn; + func *f; + var *v; + + pos = p; + t.lineno = 1; + while((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART | + TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) { + + if (tclass & TC_OPTERM) + continue; + + seq = &mainseq; + if (tclass & TC_BEGIN) { + seq = &beginseq; + chain_group(); + + } else if (tclass & TC_END) { + seq = &endseq; + chain_group(); + + } else if (tclass & TC_FUNCDECL) { + next_token(TC_FUNCTION); + pos++; + f = newfunc(t.string); + f->body.first = NULL; + f->nargs = 0; + while(next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { + v = findvar(ahash, t.string); + v->x.aidx = (f->nargs)++; + + if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) + break; + } + seq = &(f->body); + chain_group(); + clear_array(ahash); + + } else if (tclass & TC_OPSEQ) { + rollback_token(); + cn = chain_node(OC_TEST); + cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART); + if (t.tclass & TC_GRPSTART) { + rollback_token(); + chain_group(); + } else { + chain_node(OC_PRINT); + } + cn->r.n = mainseq.last; + + } else /* if (tclass & TC_GRPSTART) */ { + rollback_token(); + chain_group(); + } + } +} + + +/* -------- program execution part -------- */ + +static node *mk_splitter(char *s, tsplitter *spl) { + + register regex_t *re, *ire; + node *n; + + re = &spl->re[0]; + ire = &spl->re[1]; + n = &spl->n; + if ((n->info && OPCLSMASK) == OC_REGEXP) { + regfree(re); + regfree(ire); + } + if (bb_strlen(s) > 1) { + mk_re_node(s, n, re); + } else { + n->info = (unsigned long) *s; + } + + return n; +} + +/* use node as a regular expression. Supplied with node ptr and regex_t + * storage space. Return ptr to regex (if result points to preg, it should + * be later regfree'd manually + */ +static regex_t *as_regex(node *op, regex_t *preg) { + + var *v; + char *s; + + if ((op->info & OPCLSMASK) == OC_REGEXP) { + return icase ? op->r.ire : op->l.re; + } else { + v = nvalloc(1); + s = getvar_s(evaluate(op, v)); + xregcomp(preg, s, icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED); + nvfree(v); + return preg; + } +} + +/* gradually increasing buffer */ +static void qrealloc(char **b, int n, int *size) { + + if (! *b || n >= *size) + *b = xrealloc(*b, *size = n + (n>>1) + 80); +} + +/* resize field storage space */ +static void fsrealloc(int size) { + + static int maxfields = 0; + int i; + + if (size >= maxfields) { + i = maxfields; + maxfields = size + 16; + Fields = (var *)xrealloc(Fields, maxfields * sizeof(var)); + for (; iinfo; + c[2] = c[3] = '\0'; + if (*getvar_s(V[RS]) == '\0') c[2] = '\n'; + + if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */ + while (*s) { + l = strcspn(s, c+2); + if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0 && + pmatch[0].rm_so <= l) { + l = pmatch[0].rm_so; + if (pmatch[0].rm_eo == 0) { l++; pmatch[0].rm_eo++; } + } else { + pmatch[0].rm_eo = l; + if (*(s+l)) pmatch[0].rm_eo++; + } + + memcpy(s1, s, l); + *(s1+l) = '\0'; + nextword(&s1); + s += pmatch[0].rm_eo; + n++; + } + } else if (c[0] == '\0') { /* null split */ + while(*s) { + *(s1++) = *(s++); + *(s1++) = '\0'; + n++; + } + } else if (c[0] != ' ') { /* single-character split */ + if (icase) { + c[0] = toupper(c[0]); + c[1] = tolower(c[1]); + } + if (*s1) n++; + while ((s1 = strpbrk(s1, c))) { + *(s1++) = '\0'; + n++; + } + } else { /* space split */ + while (*s) { + while (isspace(*s)) s++; + if (! *s) break; + n++; + while (*s && !isspace(*s)) + *(s1++) = *(s++); + *(s1++) = '\0'; + } + } + return n; +} + +static void split_f0(void) { + + static char *fstrings = NULL; + int i, n; + char *s; + + if (is_f0_split) + return; + + is_f0_split = TRUE; + free(fstrings); + fsrealloc(0); + n = awk_split(getvar_s(V[F0]), &fsplitter.n, &fstrings); + fsrealloc(n); + s = fstrings; + for (i=0; itype = VF_NUMBER | VF_SPECIAL; + V[NF]->number = nfields; +} + +/* perform additional actions when some internal variables changed */ +static void handle_special(var *v) { + + int n; + char *b, *sep, *s; + int sl, l, len, i, bsize; + + if (! (v->type & VF_SPECIAL)) + return; + + if (v == V[NF]) { + n = (int)getvar_i(v); + fsrealloc(n); + + /* recalculate $0 */ + sep = getvar_s(V[OFS]); + sl = bb_strlen(sep); + b = NULL; + len = 0; + for (i=0; i v-Fields ? n : v-Fields+1); + /* right here v is invalid. Just to note... */ + } +} + +/* step through func/builtin/etc arguments */ +static node *nextarg(node **pn) { + + node *n; + + n = *pn; + if (n && (n->info & OPCLSMASK) == OC_COMMA) { + *pn = n->r.n; + n = n->l.n; + } else { + *pn = NULL; + } + return n; +} + +static void hashwalk_init(var *v, xhash *array) { + + char **w; + hash_item *hi; + int i; + + if (v->type & VF_WALK) + free(v->x.walker); + + v->type |= VF_WALK; + w = v->x.walker = (char **)xcalloc(2 + 2*sizeof(char *) + array->glen, 1); + *w = *(w+1) = (char *)(w + 2); + for (i=0; icsize; i++) { + hi = array->items[i]; + while(hi) { + strcpy(*w, hi->name); + nextword(w); + hi = hi->next; + } + } +} + +static int hashwalk_next(var *v) { + + char **w; + + w = v->x.walker; + if (*(w+1) == *w) + return FALSE; + + setvar_s(v, nextword(w+1)); + return TRUE; +} + +/* evaluate node, return 1 when result is true, 0 otherwise */ +static int ptest(node *pattern) { + static var v; + + return istrue(evaluate(pattern, &v)); +} + +/* read next record from stream rsm into a variable v */ +static int awk_getline(rstream *rsm, var *v) { + + char *b; + regmatch_t pmatch[2]; + int a, p, pp=0, size; + int fd, so, eo, r, rp; + char c, *m, *s; + + /* we're using our own buffer since we need access to accumulating + * characters + */ + fd = fileno(rsm->F); + m = rsm->buffer; + a = rsm->adv; + p = rsm->pos; + size = rsm->size; + c = (char) rsplitter.n.info; + rp = 0; + + if (! m) qrealloc(&m, 256, &size); + do { + b = m + a; + so = eo = p; + r = 1; + if (p > 0) { + if ((rsplitter.n.info & OPCLSMASK) == OC_REGEXP) { + if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re, + b, 1, pmatch, 0) == 0) { + so = pmatch[0].rm_so; + eo = pmatch[0].rm_eo; + if (b[eo] != '\0') + break; + } + } else if (c != '\0') { + s = strchr(b+pp, c); + if (s) { + so = eo = s-b; + eo++; + break; + } + } else { + while (b[rp] == '\n') + rp++; + s = strstr(b+rp, "\n\n"); + if (s) { + so = eo = s-b; + while (b[eo] == '\n') eo++; + if (b[eo] != '\0') + break; + } + } + } + + if (a > 0) { + memmove(m, (const void *)(m+a), p+1); + b = m; + a = 0; + } + + qrealloc(&m, a+p+128, &size); + b = m + a; + pp = p; + p += safe_read(fd, b+p, size-p-1); + if (p < pp) { + p = 0; + r = 0; + setvar_i(V[ERRNO], errno); + } + b[p] = '\0'; + + } while (p > pp); + + if (p == 0) { + r--; + } else { + c = b[so]; b[so] = '\0'; + setvar_s(v, b+rp); + v->type |= VF_USER; + b[so] = c; + c = b[eo]; b[eo] = '\0'; + setvar_s(V[RT], b+so); + b[eo] = c; + } + + rsm->buffer = m; + rsm->adv = a + eo; + rsm->pos = p - eo; + rsm->size = size; + + return r; +} + +static int fmt_num(char *b, int size, char *format, double n, int int_as_int) { + + int r=0; + char c, *s=format; + + if (int_as_int && n == (int)n) { + r = snprintf(b, size, "%d", (int)n); + } else { + do { c = *s; } while (*s && *++s); + if (strchr("diouxX", c)) { + r = snprintf(b, size, format, (int)n); + } else if (strchr("eEfgG", c)) { + r = snprintf(b, size, format, n); + } else { + runtime_error(EMSG_INV_FMT); + } + } + return r; +} + + +/* formatted output into an allocated buffer, return ptr to buffer */ +static char *awk_printf(node *n) { + + char *b = NULL; + char *fmt, *s, *s1, *f; + int i, j, incr, bsize; + char c, c1; + var *v, *arg; + + v = nvalloc(1); + fmt = f = bb_xstrdup(getvar_s(evaluate(nextarg(&n), v))); + + i = 0; + while (*f) { + s = f; + while (*f && (*f != '%' || *(++f) == '%')) + f++; + while (*f && !isalpha(*f)) + f++; + + incr = (f - s) + MAXVARFMT; + qrealloc(&b, incr+i, &bsize); + c = *f; if (c != '\0') f++; + c1 = *f ; *f = '\0'; + arg = evaluate(nextarg(&n), v); + + j = i; + if (c == 'c' || !c) { + i += sprintf(b+i, s, + is_numeric(arg) ? (char)getvar_i(arg) : *getvar_s(arg)); + + } else if (c == 's') { + s1 = getvar_s(arg); + qrealloc(&b, incr+i+bb_strlen(s1), &bsize); + i += sprintf(b+i, s, s1); + + } else { + i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE); + } + *f = c1; + + /* if there was an error while sprintf, return value is negative */ + if (i < j) i = j; + + } + + b = xrealloc(b, i+1); + free(fmt); + nvfree(v); + b[i] = '\0'; + return b; +} + +/* common substitution routine + * replace (nm) substring of (src) that match (n) with (repl), store + * result into (dest), return number of substitutions. If nm=0, replace + * all matches. If src or dst is NULL, use $0. If ex=TRUE, enable + * subexpression matching (\1-\9) + */ +static int awk_sub(node *rn, char *repl, int nm, var *src, var *dest, int ex) { + + char *ds = NULL; + char *sp, *s; + int c, i, j, di, rl, so, eo, nbs, n, dssize; + regmatch_t pmatch[10]; + regex_t sreg, *re; + + re = as_regex(rn, &sreg); + if (! src) src = V[F0]; + if (! dest) dest = V[F0]; + + i = di = 0; + sp = getvar_s(src); + rl = bb_strlen(repl); + while (regexec(re, sp, 10, pmatch, sp==getvar_s(src) ? 0:REG_NOTBOL) == 0) { + so = pmatch[0].rm_so; + eo = pmatch[0].rm_eo; + + qrealloc(&ds, di + eo + rl, &dssize); + memcpy(ds + di, sp, eo); + di += eo; + if (++i >= nm) { + /* replace */ + di -= (eo - so); + nbs = 0; + for (s = repl; *s; s++) { + ds[di++] = c = *s; + if (c == '\\') { + nbs++; + continue; + } + if (c == '&' || (ex && c >= '0' && c <= '9')) { + di -= ((nbs + 3) >> 1); + j = 0; + if (c != '&') { + j = c - '0'; + nbs++; + } + if (nbs % 2) { + ds[di++] = c; + } else { + n = pmatch[j].rm_eo - pmatch[j].rm_so; + qrealloc(&ds, di + rl + n, &dssize); + memcpy(ds + di, sp + pmatch[j].rm_so, n); + di += n; + } + } + nbs = 0; + } + } + + sp += eo; + if (i == nm) break; + if (eo == so) { + if (! (ds[di++] = *sp++)) break; + } + } + + qrealloc(&ds, di + strlen(sp), &dssize); + strcpy(ds + di, sp); + setvar_p(dest, ds); + if (re == &sreg) regfree(re); + return i; +} + +static var *exec_builtin(node *op, var *res) { + + int (*to_xxx)(int); + var *tv; + node *an[4]; + var *av[4]; + char *as[4]; + regmatch_t pmatch[2]; + regex_t sreg, *re; + static tsplitter tspl; + node *spl; + unsigned long isr, info; + int nargs; + time_t tt; + char *s, *s1; + int i, l, ll, n; + + tv = nvalloc(4); + isr = info = op->info; + op = op->l.n; + + av[2] = av[3] = NULL; + for (i=0 ; i<4 && op ; i++) { + an[i] = nextarg(&op); + if (isr & 0x09000000) av[i] = evaluate(an[i], &tv[i]); + if (isr & 0x08000000) as[i] = getvar_s(av[i]); + isr >>= 1; + } + + nargs = i; + if (nargs < (info >> 30)) + runtime_error(EMSG_TOO_FEW_ARGS); + + switch (info & OPNMASK) { + + case B_a2: +#ifdef CONFIG_FEATURE_AWK_MATH + setvar_i(res, atan2(getvar_i(av[i]), getvar_i(av[1]))); +#else + runtime_error(EMSG_NO_MATH); +#endif + break; + + case B_sp: + if (nargs > 2) { + spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ? + an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl); + } else { + spl = &fsplitter.n; + } + + n = awk_split(as[0], spl, &s); + s1 = s; + clear_array(iamarray(av[1])); + for (i=1; i<=n; i++) + setari_u(av[1], i, nextword(&s1)); + free(s); + setvar_i(res, n); + break; + + case B_ss: + l = bb_strlen(as[0]); + i = getvar_i(av[1]) - 1; + if (i>l) i=l; if (i<0) i=0; + n = (nargs > 2) ? getvar_i(av[2]) : l-i; + if (n<0) n=0; + s = xmalloc(n+1); + strncpy(s, as[0]+i, n); + s[n] = '\0'; + setvar_p(res, s); + break; + + case B_lo: + to_xxx = tolower; + goto lo_cont; + + case B_up: + to_xxx = toupper; +lo_cont: + s1 = s = bb_xstrdup(as[0]); + while (*s1) { + *s1 = (*to_xxx)(*s1); + s1++; + } + setvar_p(res, s); + break; + + case B_ix: + n = 0; + ll = bb_strlen(as[1]); + l = bb_strlen(as[0]) - ll; + if (ll > 0 && l >= 0) { + if (! icase) { + s = strstr(as[0], as[1]); + if (s) n = (s - as[0]) + 1; + } else { + /* this piece of code is terribly slow and + * really should be rewritten + */ + for (i=0; i<=l; i++) { + if (strncasecmp(as[0]+i, as[1], ll) == 0) { + n = i+1; + break; + } + } + } + } + setvar_i(res, n); + break; + + case B_ti: + if (nargs > 1) + tt = getvar_i(av[1]); + else + time(&tt); + s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"; + i = strftime(buf, MAXVARFMT, s, localtime(&tt)); + buf[i] = '\0'; + setvar_s(res, buf); + break; + + case B_ma: + re = as_regex(an[1], &sreg); + n = regexec(re, as[0], 1, pmatch, 0); + if (n == 0) { + pmatch[0].rm_so++; + pmatch[0].rm_eo++; + } else { + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = -1; + } + setvar_i(newvar("RSTART"), pmatch[0].rm_so); + setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so); + setvar_i(res, pmatch[0].rm_so); + if (re == &sreg) regfree(re); + break; + + case B_ge: + awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE); + break; + + case B_gs: + setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE)); + break; + + case B_su: + setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE)); + break; + } + + nvfree(tv); + return res; +} + +/* + * Evaluate node - the heart of the program. Supplied with subtree + * and place where to store result. returns ptr to result. + */ +#define XC(n) ((n) >> 8) + +static var *evaluate(node *op, var *res) { + + /* This procedure is recursive so we should count every byte */ + static var *fnargs = NULL; + static unsigned int seed = 1; + static regex_t sreg; + node *op1; + var *v1; + union { + var *v; + char *s; + double d; + int i; + } L, R; + unsigned long opinfo; + short opn; + union { + char *s; + rstream *rsm; + FILE *F; + var *v; + regex_t *re; + unsigned long info; + } X; + + if (! op) + return setvar_s(res, NULL); + + v1 = nvalloc(2); + + while (op) { + + opinfo = op->info; + opn = (short)(opinfo & OPNMASK); + lineno = op->lineno; + + /* execute inevitable things */ + op1 = op->l.n; + if (opinfo & OF_RES1) X.v = L.v = evaluate(op1, v1); + if (opinfo & OF_RES2) R.v = evaluate(op->r.n, v1+1); + if (opinfo & OF_STR1) L.s = getvar_s(L.v); + if (opinfo & OF_STR2) R.s = getvar_s(R.v); + if (opinfo & OF_NUM1) L.d = getvar_i(L.v); + + switch (XC(opinfo & OPCLSMASK)) { + + /* -- iterative node type -- */ + + /* test pattern */ + case XC( OC_TEST ): + if ((op1->info & OPCLSMASK) == OC_COMMA) { + /* it's range pattern */ + if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) { + op->info |= OF_CHECKED; + if (ptest(op1->r.n)) + op->info &= ~OF_CHECKED; + + op = op->a.n; + } else { + op = op->r.n; + } + } else { + op = (ptest(op1)) ? op->a.n : op->r.n; + } + break; + + /* just evaluate an expression, also used as unconditional jump */ + case XC( OC_EXEC ): + break; + + /* branch, used in if-else and various loops */ + case XC( OC_BR ): + op = istrue(L.v) ? op->a.n : op->r.n; + break; + + /* initialize for-in loop */ + case XC( OC_WALKINIT ): + hashwalk_init(L.v, iamarray(R.v)); + break; + + /* get next array item */ + case XC( OC_WALKNEXT ): + op = hashwalk_next(L.v) ? op->a.n : op->r.n; + break; + + case XC( OC_PRINT ): + case XC( OC_PRINTF ): + X.F = stdout; + if (op->r.n) { + X.rsm = newfile(R.s); + if (! X.rsm->F) { + if (opn == '|') { + if((X.rsm->F = popen(R.s, "w")) == NULL) + bb_perror_msg_and_die("popen"); + X.rsm->is_pipe = 1; + } else { + X.rsm->F = bb_xfopen(R.s, opn=='w' ? "w" : "a"); + } + } + X.F = X.rsm->F; + } + + if ((opinfo & OPCLSMASK) == OC_PRINT) { + if (! op1) { + fputs(getvar_s(V[F0]), X.F); + } else { + while (op1) { + L.v = evaluate(nextarg(&op1), v1); + if (L.v->type & VF_NUMBER) { + fmt_num(buf, MAXVARFMT, getvar_s(V[OFMT]), + getvar_i(L.v), TRUE); + fputs(buf, X.F); + } else { + fputs(getvar_s(L.v), X.F); + } + + if (op1) fputs(getvar_s(V[OFS]), X.F); + } + } + fputs(getvar_s(V[ORS]), X.F); + + } else { /* OC_PRINTF */ + L.s = awk_printf(op1); + fputs(L.s, X.F); + free(L.s); + } + fflush(X.F); + break; + + case XC( OC_DELETE ): + X.info = op1->info & OPCLSMASK; + if (X.info == OC_VAR) { + R.v = op1->l.v; + } else if (X.info == OC_FNARG) { + R.v = &fnargs[op1->l.i]; + } else { + runtime_error(EMSG_NOT_ARRAY); + } + + if (op1->r.n) { + clrvar(L.v); + L.s = getvar_s(evaluate(op1->r.n, v1)); + hash_remove(iamarray(R.v), L.s); + } else { + clear_array(iamarray(R.v)); + } + break; + + case XC( OC_NEWSOURCE ): + programname = op->l.s; + break; + + case XC( OC_RETURN ): + copyvar(res, L.v); + break; + + case XC( OC_NEXTFILE ): + nextfile = TRUE; + case XC( OC_NEXT ): + nextrec = TRUE; + case XC( OC_DONE ): + clrvar(res); + break; + + case XC( OC_EXIT ): + awk_exit(L.d); + + /* -- recursive node type -- */ + + case XC( OC_VAR ): + L.v = op->l.v; + if (L.v == V[NF]) + split_f0(); + goto v_cont; + + case XC( OC_FNARG ): + L.v = &fnargs[op->l.i]; + +v_cont: + res = (op->r.n) ? findvar(iamarray(L.v), R.s) : L.v; + break; + + case XC( OC_IN ): + setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0); + break; + + case XC( OC_REGEXP ): + op1 = op; + L.s = getvar_s(V[F0]); + goto re_cont; + + case XC( OC_MATCH ): + op1 = op->r.n; +re_cont: + X.re = as_regex(op1, &sreg); + R.i = regexec(X.re, L.s, 0, NULL, 0); + if (X.re == &sreg) regfree(X.re); + setvar_i(res, (R.i == 0 ? 1 : 0) ^ (opn == '!' ? 1 : 0)); + break; + + case XC( OC_MOVE ): + /* if source is a temporary string, jusk relink it to dest */ + if (R.v == v1+1 && R.v->string) { + res = setvar_p(L.v, R.v->string); + R.v->string = NULL; + } else { + res = copyvar(L.v, R.v); + } + break; + + case XC( OC_TERNARY ): + if ((op->r.n->info & OPCLSMASK) != OC_COLON) + runtime_error(EMSG_POSSIBLE_ERROR); + res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res); + break; + + case XC( OC_FUNC ): + if (! op->r.f->body.first) + runtime_error(EMSG_UNDEF_FUNC); + + X.v = R.v = nvalloc(op->r.f->nargs+1); + while (op1) { + L.v = evaluate(nextarg(&op1), v1); + copyvar(R.v, L.v); + R.v->type |= VF_CHILD; + R.v->x.parent = L.v; + if (++R.v - X.v >= op->r.f->nargs) + break; + } + + R.v = fnargs; + fnargs = X.v; + + L.s = programname; + res = evaluate(op->r.f->body.first, res); + programname = L.s; + + nvfree(fnargs); + fnargs = R.v; + break; + + case XC( OC_GETLINE ): + case XC( OC_PGETLINE ): + if (op1) { + X.rsm = newfile(L.s); + if (! X.rsm->F) { + if ((opinfo & OPCLSMASK) == OC_PGETLINE) { + X.rsm->F = popen(L.s, "r"); + X.rsm->is_pipe = TRUE; + } else { + X.rsm->F = fopen(L.s, "r"); /* not bb_xfopen! */ + } + } + } else { + if (! iF) iF = next_input_file(); + X.rsm = iF; + } + + if (! X.rsm->F) { + setvar_i(V[ERRNO], errno); + setvar_i(res, -1); + break; + } + + if (! op->r.n) + R.v = V[F0]; + + L.i = awk_getline(X.rsm, R.v); + if (L.i > 0) { + if (! op1) { + incvar(V[FNR]); + incvar(V[NR]); + } + } + setvar_i(res, L.i); + break; + + /* simple builtins */ + case XC( OC_FBLTIN ): + switch (opn) { + + case F_in: + R.d = (int)L.d; + break; + + case F_rn: + R.d = (double)rand() / (double)RAND_MAX; + break; + +#ifdef CONFIG_FEATURE_AWK_MATH + case F_co: + R.d = cos(L.d); + break; + + case F_ex: + R.d = exp(L.d); + break; + + case F_lg: + R.d = log(L.d); + break; + + case F_si: + R.d = sin(L.d); + break; + + case F_sq: + R.d = sqrt(L.d); + break; +#else + case F_co: + case F_ex: + case F_lg: + case F_si: + case F_sq: + runtime_error(EMSG_NO_MATH); + break; +#endif + + case F_sr: + R.d = (double)seed; + seed = op1 ? (unsigned int)L.d : (unsigned int)time(NULL); + srand(seed); + break; + + case F_ti: + R.d = time(NULL); + break; + + case F_le: + if (! op1) + L.s = getvar_s(V[F0]); + R.d = bb_strlen(L.s); + break; + + case F_sy: + fflush(NULL); + R.d = (L.s && *L.s) ? system(L.s) : 0; + break; + + case F_ff: + if (! op1) + fflush(stdout); + else { + if (L.s && *L.s) { + X.rsm = newfile(L.s); + fflush(X.rsm->F); + } else { + fflush(NULL); + } + } + break; + + case F_cl: + X.rsm = (rstream *)hash_search(fdhash, L.s); + if (X.rsm) { + R.i = X.rsm->is_pipe ? pclose(X.rsm->F) : fclose(X.rsm->F); + free(X.rsm->buffer); + hash_remove(fdhash, L.s); + } + if (R.i != 0) + setvar_i(V[ERRNO], errno); + R.d = (double)R.i; + break; + } + setvar_i(res, R.d); + break; + + case XC( OC_BUILTIN ): + res = exec_builtin(op, res); + break; + + case XC( OC_SPRINTF ): + setvar_p(res, awk_printf(op1)); + break; + + case XC( OC_UNARY ): + X.v = R.v; + L.d = R.d = getvar_i(R.v); + switch (opn) { + case 'P': + L.d = ++R.d; + goto r_op_change; + case 'p': + R.d++; + goto r_op_change; + case 'M': + L.d = --R.d; + goto r_op_change; + case 'm': + R.d--; + goto r_op_change; + case '!': + L.d = istrue(X.v) ? 0 : 1; + break; + case '-': + L.d = -R.d; + break; + r_op_change: + setvar_i(X.v, R.d); + } + setvar_i(res, L.d); + break; + + case XC( OC_FIELD ): + R.i = (int)getvar_i(R.v); + if (R.i == 0) { + res = V[F0]; + } else { + split_f0(); + if (R.i > nfields) + fsrealloc(R.i); + + res = &Fields[R.i-1]; + } + break; + + /* concatenation (" ") and index joining (",") */ + case XC( OC_CONCAT ): + case XC( OC_COMMA ): + opn = bb_strlen(L.s) + bb_strlen(R.s) + 2; + X.s = (char *)xmalloc(opn); + strcpy(X.s, L.s); + if ((opinfo & OPCLSMASK) == OC_COMMA) { + L.s = getvar_s(V[SUBSEP]); + X.s = (char *)xrealloc(X.s, opn + bb_strlen(L.s)); + strcat(X.s, L.s); + } + strcat(X.s, R.s); + setvar_p(res, X.s); + break; + + case XC( OC_LAND ): + setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0); + break; + + case XC( OC_LOR ): + setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n)); + break; + + case XC( OC_BINARY ): + case XC( OC_REPLACE ): + R.d = getvar_i(R.v); + switch (opn) { + case '+': + L.d += R.d; + break; + case '-': + L.d -= R.d; + break; + case '*': + L.d *= R.d; + break; + case '/': + if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO); + L.d /= R.d; + break; + case '&': +#ifdef CONFIG_FEATURE_AWK_MATH + L.d = pow(L.d, R.d); +#else + runtime_error(EMSG_NO_MATH); +#endif + break; + case '%': + if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO); + L.d -= (int)(L.d / R.d) * R.d; + break; + } + res = setvar_i(((opinfo&OPCLSMASK) == OC_BINARY) ? res : X.v, L.d); + break; + + case XC( OC_COMPARE ): + if (is_numeric(L.v) && is_numeric(R.v)) { + L.d = getvar_i(L.v) - getvar_i(R.v); + } else { + L.s = getvar_s(L.v); + R.s = getvar_s(R.v); + L.d = icase ? strcasecmp(L.s, R.s) : strcmp(L.s, R.s); + } + switch (opn & 0xfe) { + case 0: + R.i = (L.d > 0); + break; + case 2: + R.i = (L.d >= 0); + break; + case 4: + R.i = (L.d == 0); + break; + } + setvar_i(res, (opn & 0x1 ? R.i : !R.i) ? 1 : 0); + break; + + default: + runtime_error(EMSG_POSSIBLE_ERROR); + } + if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS) + op = op->a.n; + if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS) + break; + if (nextrec) + break; + } + nvfree(v1); + return res; +} + + +/* -------- main & co. -------- */ + +static int awk_exit(int r) { + + unsigned int i; + hash_item *hi; + static var tv; + + if (! exiting) { + exiting = TRUE; + nextrec = FALSE; + evaluate(endseq.first, &tv); + } + + /* waiting for children */ + for (i=0; icsize; i++) { + hi = fdhash->items[i]; + while(hi) { + if (hi->data.rs.F && hi->data.rs.is_pipe) + pclose(hi->data.rs.F); + hi = hi->next; + } + } + + exit(r); +} + +/* if expr looks like "var=value", perform assignment and return 1, + * otherwise return 0 */ +static int is_assignment(char *expr) { + + char *exprc, *s, *s0, *s1; + + exprc = bb_xstrdup(expr); + if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) { + free(exprc); + return FALSE; + } + + *(s++) = '\0'; + s0 = s1 = s; + while (*s) + *(s1++) = nextchar(&s); + + *s1 = '\0'; + setvar_u(newvar(exprc), s0); + free(exprc); + return TRUE; +} + +/* switch to next input file */ +static rstream *next_input_file(void) { + + static rstream rsm; + FILE *F = NULL; + char *fname, *ind; + static int files_happen = FALSE; + + if (rsm.F) fclose(rsm.F); + rsm.F = NULL; + rsm.pos = rsm.adv = 0; + + do { + if (getvar_i(V[ARGIND])+1 >= getvar_i(V[ARGC])) { + if (files_happen) + return NULL; + fname = "-"; + F = stdin; + } else { + ind = getvar_s(incvar(V[ARGIND])); + fname = getvar_s(findvar(iamarray(V[ARGV]), ind)); + if (fname && *fname && !is_assignment(fname)) + F = afopen(fname, "r"); + } + } while (!F); + + files_happen = TRUE; + setvar_s(V[FILENAME], fname); + rsm.F = F; + return &rsm; +} + +extern int awk_main(int argc, char **argv) { + + char *s, *s1; + int i, j, c; + var *v; + static var tv; + char **envp; + static int from_file = FALSE; + rstream *rsm; + FILE *F, *stdfiles[3]; + static char * stdnames = "/dev/stdin\0/dev/stdout\0/dev/stderr"; + + /* allocate global buffer */ + buf = xmalloc(MAXVARFMT+1); + + vhash = hash_init(); + ahash = hash_init(); + fdhash = hash_init(); + fnhash = hash_init(); + + /* initialize variables */ + for (i=0; *vNames; i++) { + V[i] = v = newvar(nextword(&vNames)); + if (*vValues != '\377') + setvar_s(v, nextword(&vValues)); + else + setvar_i(v, 0); + + if (*vNames == '*') { + v->type |= VF_SPECIAL; + vNames++; + } + } + + handle_special(V[FS]); + handle_special(V[RS]); + + stdfiles[0] = stdin; + stdfiles[1] = stdout; + stdfiles[2] = stderr; + for (i=0; i<3; i++) { + rsm = newfile(nextword(&stdnames)); + rsm->F = stdfiles[i]; + } + + for (envp=environ; *envp; envp++) { + s = bb_xstrdup(*envp); + s1 = strchr(s, '='); + if (!s1) { + goto keep_going; + } + *(s1++) = '\0'; + setvar_u(findvar(iamarray(V[ENVIRON]), s), s1); +keep_going: + free(s); + } + + while((c = getopt(argc, argv, "F:v:f:W:")) != EOF) { + switch (c) { + case 'F': + setvar_s(V[FS], optarg); + break; + case 'v': + if (! is_assignment(optarg)) + bb_show_usage(); + break; + case 'f': + from_file = TRUE; + F = afopen(programname = optarg, "r"); + s = NULL; + /* one byte is reserved for some trick in next_token */ + for (i=j=1; j>0; i+=j) { + s = (char *)xrealloc(s, i+4096); + j = fread(s+i, 1, 4094, F); + } + s[i] = '\0'; + fclose(F); + parse_program(s+1); + free(s); + break; + case 'W': + bb_error_msg("Warning: unrecognized option '-W %s' ignored\n", optarg); + break; + + default: + bb_show_usage(); + } + } + + if (!from_file) { + if (argc == optind) + bb_show_usage(); + programname="cmd. line"; + parse_program(argv[optind++]); + + } + + /* fill in ARGV array */ + setvar_i(V[ARGC], argc - optind + 1); + setari_u(V[ARGV], 0, "awk"); + for(i=optind; i < argc; i++) + setari_u(V[ARGV], i+1-optind, argv[i]); + + evaluate(beginseq.first, &tv); + if (! mainseq.first && ! endseq.first) + awk_exit(EXIT_SUCCESS); + + /* input file could already be opened in BEGIN block */ + if (! iF) iF = next_input_file(); + + /* passing through input files */ + while (iF) { + + nextfile = FALSE; + setvar_i(V[FNR], 0); + + while ((c = awk_getline(iF, V[F0])) > 0) { + + nextrec = FALSE; + incvar(V[NR]); + incvar(V[FNR]); + evaluate(mainseq.first, &tv); + + if (nextfile) + break; + } + + if (c < 0) + runtime_error(strerror(errno)); + + iF = next_input_file(); + + } + + awk_exit(EXIT_SUCCESS); + + return 0; +} + diff --git a/busybox/editors/patch.c b/busybox/editors/patch.c new file mode 100644 index 000000000..6a68d2ef8 --- /dev/null +++ b/busybox/editors/patch.c @@ -0,0 +1,290 @@ +/* vi: set sw=4 ts=4: */ +/* + * busybox patch applet to handle the unified diff format. + * Copyright (C) 2003 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. + * + * + * + * This applet is written to work with patches generated by GNU diff, + * where there is equivalent functionality busybox patch shall behave + * as per GNU patch. + * + * There is a SUSv3 specification for patch, however it looks to be + * incomplete, it doesnt even mention unified diff format. + * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html + * + * Issues + * - Non-interactive + * - Patches must apply cleanly or the hunk will fail. + * - Reject file isnt saved + * - + */ + +#include +#include +#include +#include +#include "busybox.h" +#include "libbb.h" + +static int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count) +{ + int i = 0; + + while (src_stream && (i < lines_count)) { + char *line; + line = bb_get_line_from_file(src_stream); + if (line == NULL) { + break; + } + if (fputs(line, dest_stream) == EOF) { + bb_perror_msg_and_die("Error writing to new file"); + } + free(line); + + i++; + } + return(i); +} + +/* If patch_level is -1 it will remove all directory names + * char *line must be greater than 4 chars + * returns NULL if the file doesnt exist or error + * returns malloc'ed filename + */ + +static unsigned char *extract_filename(char *line, unsigned short patch_level) +{ + char *filename_start_ptr = line + 4; + int i; + + /* Terminate string at end of source filename */ + { + char *line_ptr; + line_ptr = strchr(filename_start_ptr, '\t'); + if (!line_ptr) { + bb_perror_msg("Malformed line %s", line); + return(NULL); + } + *line_ptr = '\0'; + } + + /* Skip over (patch_level) number of leading directories */ + for (i = 0; i < patch_level; i++) { + char *dirname_ptr; + + dirname_ptr = strchr(filename_start_ptr, '/'); + if (!dirname_ptr) { + break; + } + filename_start_ptr = dirname_ptr + 1; + } + + return(bb_xstrdup(filename_start_ptr)); +} + +static int file_doesnt_exist(const char *filename) +{ + struct stat statbuf; + return(stat(filename, &statbuf)); +} + +extern int patch_main(int argc, char **argv) +{ + unsigned int patch_level = -1; + char *patch_line; + int ret = 0; + + /* Handle 'p' option */ + if (argv[1] && (argv[1][0] == '-') && (argv[1][1] == 'p')) { + patch_level = atoi(&argv[1][2]); + } + + patch_line = bb_get_line_from_file(stdin); + while (patch_line) { + FILE *src_stream; + FILE *dst_stream; + char *original_filename; + char *new_filename; + char *backup_filename; + unsigned int src_cur_line = 1; + unsigned int dest_cur_line = 0; + unsigned int dest_beg_line; + unsigned int bad_hunk_count = 0; + unsigned int hunk_count = 0; + char copy_trailing_lines_flag = 0; + + /* Skip everything upto the "---" marker + * No need to parse the lines "Only in ", and "diff " + */ + while (patch_line && strncmp(patch_line, "--- ", 4) != 0) { + free(patch_line); + patch_line = bb_get_line_from_file(stdin); + } + + /* Extract the filename used before the patch was generated */ + original_filename = extract_filename(patch_line, patch_level); + free(patch_line); + + patch_line = bb_get_line_from_file(stdin); + if (strncmp(patch_line, "+++ ", 4) != 0) { + ret = 2; + bb_error_msg("Invalid patch"); + continue; + } + new_filename = extract_filename(patch_line, patch_level); + free(patch_line); + + if (file_doesnt_exist(new_filename)) { + char *line_ptr; + /* Create leading directories */ + line_ptr = strrchr(new_filename, '/'); + if (line_ptr) { + *line_ptr = '\0'; + bb_make_directory(new_filename, -1, FILEUTILS_RECUR); + *line_ptr = '/'; + } + dst_stream = bb_xfopen(new_filename, "w+"); + backup_filename = NULL; + } else { + backup_filename = xmalloc(strlen(new_filename) + 6); + strcpy(backup_filename, new_filename); + strcat(backup_filename, ".orig"); + if (rename(new_filename, backup_filename) == -1) { + bb_perror_msg_and_die("Couldnt create file %s", backup_filename); + } + dst_stream = bb_xfopen(new_filename, "w"); + } + + if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) { + src_stream = NULL; + } else { + if (strcmp(original_filename, new_filename) == 0) { + src_stream = bb_xfopen(backup_filename, "r"); + } else { + src_stream = bb_xfopen(original_filename, "r"); + } + } + + printf("patching file %s\n", new_filename); + + /* Handle each hunk */ + patch_line = bb_get_line_from_file(stdin); + while (patch_line) { + unsigned int count; + unsigned int src_beg_line; + unsigned int unused; + unsigned int hunk_offset_start = 0; + int hunk_error = 0; + + /* This bit should be improved */ + if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) && + (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) && + (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) { + /* No more hunks for this file */ + break; + } + free(patch_line); + hunk_count++; + + if (src_beg_line && dest_beg_line) { + /* Copy unmodified lines upto start of hunk */ + /* src_beg_line will be 0 if its a new file */ + count = src_beg_line - src_cur_line; + if (copy_lines(src_stream, dst_stream, count) != count) { + bb_error_msg_and_die("Bad src file"); + } + src_cur_line += count; + dest_cur_line += count; + copy_trailing_lines_flag = 1; + } + hunk_offset_start = src_cur_line; + + while ((patch_line = bb_get_line_from_file(stdin)) != NULL) { + if ((*patch_line == '-') || (*patch_line == ' ')) { + char *src_line = NULL; + if (src_stream) { + src_line = bb_get_line_from_file(src_stream); + if (!src_line) { + hunk_error++; + break; + } else { + src_cur_line++; + } + if (strcmp(src_line, patch_line + 1) != 0) { + bb_error_msg("Hunk #%d FAILED at %d.", hunk_count, hunk_offset_start); + hunk_error++; + free(patch_line); + break; + } + free(src_line); + } + if (*patch_line == ' ') { + fputs(patch_line + 1, dst_stream); + dest_cur_line++; + } + } else if (*patch_line == '+') { + fputs(patch_line + 1, dst_stream); + dest_cur_line++; + } else { + break; + } + free(patch_line); + } + if (hunk_error) { + bad_hunk_count++; + } + } + + /* Cleanup last patched file */ + if (copy_trailing_lines_flag) { + copy_lines(src_stream, dst_stream, -1); + } + if (src_stream) { + fclose(src_stream); + } + if (dst_stream) { + fclose(dst_stream); + } + if (bad_hunk_count) { + if (!ret) { + ret = 1; + } + bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count); + } else { + /* It worked, we can remove the backup */ + if (backup_filename) { + unlink(backup_filename); + } + if ((dest_cur_line == 0) || (dest_beg_line == 0)) { + /* The new patched file is empty, remove it */ + if (unlink(new_filename) == -1) { + bb_perror_msg_and_die("Couldnt remove file %s", new_filename); + } + if (unlink(original_filename) == -1) { + bb_perror_msg_and_die("Couldnt remove original file %s", new_filename); + } + } + } + } + + /* 0 = SUCCESS + * 1 = Some hunks failed + * 2 = More serious problems + */ + return(ret); +} diff --git a/busybox/editors/sed.c b/busybox/editors/sed.c new file mode 100644 index 000000000..3d6871621 --- /dev/null +++ b/busybox/editors/sed.c @@ -0,0 +1,1220 @@ +/* vi: set sw=4 ts=4: */ +/* + * sed.c - very minimalist version of sed + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley + * Copyright (C) 1999,2000,2001 by Mark Whitley + * Copyright (C) 2002 Matt Kraai + * Copyright (C) 2003 by Glenn McGrath + * Copyright (C) 2003,2004 by Rob Landley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Code overview. + + Files are laid out to avoid unnecessary function declarations. So for + example, every function add_cmd calls occurs before add_cmd in this file. + + add_cmd() is called on each line of sed command text (from a file or from + the command line). It calls get_address() and parse_cmd_args(). The + resulting sed_cmd_t structures are appended to a linked list + (sed_cmd_head/sed_cmd_tail). + + process_file() does actual sedding, reading data lines from an input FILE * + (which could be stdin) and applying the sed command list (sed_cmd_head) to + each of the resulting lines. + + sed_main() is where external code calls into this, with a command line. +*/ + + +/* + Supported features and commands in this version of sed: + + - comments ('#') + - address matching: num|/matchstr/[,num|/matchstr/|$]command + - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) + - edit commands: (a)ppend, (i)nsert, (c)hange + - file commands: (r)ead + - backreferences in substitution expressions (\1, \2...\9) + - grouped commands: {cmd1;cmd2} + - transliteration (y/source-chars/dest-chars/) + - pattern space hold space storing / swapping (g, h, x) + - labels / branching (: label, b, t) + + (Note: Specifying an address (range) to match is *optional*; commands + default to the whole pattern space if no specific address match was + requested.) + + Unsupported features: + + - GNU extensions + - and more. + + Todo: + + - Create a wrapper around regex to make libc's regex conform with sed + - Fix bugs + + + Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html +*/ + +#include +#include /* for getopt() */ +#include +#include /* for strdup() */ +#include +#include /* for isspace() */ +#include +#include "busybox.h" + +typedef struct sed_cmd_s { + /* Ordered by alignment requirements: currently 36 bytes on x86 */ + + /* address storage */ + regex_t *beg_match; /* sed -e '/match/cmd' */ + regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */ + regex_t *sub_match; /* For 's/sub_match/string/' */ + int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ + int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */ + + FILE *file; /* File (sr) command writes to, -1 for none. */ + char *string; /* Data string for (saicytb) commands. */ + + unsigned short which_match; /* (s) Which match to replace (0 for all) */ + + /* Bitfields (gcc won't group them if we don't) */ + unsigned int invert:1; /* the '!' after the address */ + unsigned int in_match:1; /* Next line also included in match? */ + unsigned int no_newline:1; /* Last line written by (sr) had no '\n' */ + unsigned int sub_p:1; /* (s) print option */ + + + /* GENERAL FIELDS */ + char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */ + struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */ +} sed_cmd_t; + +/* globals */ +/* options */ +static int be_quiet = 0, in_place=0, regex_type=0; +FILE *nonstdout; +char *outname; + + +static const char bad_format_in_subst[] = + "bad format in substitution expression"; +const char *const semicolon_whitespace = "; \n\r\t\v"; + +regmatch_t regmatch[10]; +static regex_t *previous_regex_ptr = NULL; + +/* linked list of sed commands */ +static sed_cmd_t sed_cmd_head; +static sed_cmd_t *sed_cmd_tail = &sed_cmd_head; + +/* Linked list of append lines */ +struct append_list { + char *string; + struct append_list *next; +}; +struct append_list *append_head=NULL, *append_tail=NULL; + +#ifdef CONFIG_FEATURE_CLEAN_UP +static void free_and_close_stuff(void) +{ + sed_cmd_t *sed_cmd = sed_cmd_head.next; + + while(append_head) { + append_tail=append_head->next; + free(append_head->string); + free(append_head); + append_head=append_tail; + } + + while (sed_cmd) { + sed_cmd_t *sed_cmd_next = sed_cmd->next; + + if(sed_cmd->file) + bb_xprint_and_close_file(sed_cmd->file); + + if (sed_cmd->beg_match) { + regfree(sed_cmd->beg_match); + free(sed_cmd->beg_match); + } + if (sed_cmd->end_match) { + regfree(sed_cmd->end_match); + free(sed_cmd->end_match); + } + if (sed_cmd->sub_match) { + regfree(sed_cmd->sub_match); + free(sed_cmd->sub_match); + } + free(sed_cmd->string); + free(sed_cmd); + sed_cmd = sed_cmd_next; + } +} +#endif + +/* If something bad happens during -i operation, delete temp file */ + +static void cleanup_outname(void) +{ + if(outname) unlink(outname); +} + +/* strdup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */ + +static void parse_escapes(char *dest, const char *string, int len, char from, char to) +{ + int i=0; + + while(istring); + + /* determine the number of back references in the match string */ + /* Note: we compute this here rather than in the do_subst_command() + * function to save processor time, at the expense of a little more memory + * (4 bits) per sed_cmd */ + + /* process the flags */ + + sed_cmd->which_match=1; + while (substr[++idx]) { + /* Parse match number */ + if(isdigit(substr[idx])) { + if(match[0]!='^') { + /* Match 0 treated as all, multiple matches we take the last one. */ + char *pos=substr+idx; + sed_cmd->which_match=(unsigned short)strtol(substr+idx,&pos,10); + idx=pos-substr; + } + continue; + } + /* Skip spaces */ + if(isspace(substr[idx])) continue; + + switch (substr[idx]) { + /* Replace all occurrences */ + case 'g': + if (match[0] != '^') sed_cmd->which_match = 0; + break; + /* Print pattern space */ + case 'p': + sed_cmd->sub_p = 1; + break; + case 'w': + { + char *temp; + idx+=parse_file_cmd(sed_cmd,substr+idx,&temp); + + break; + } + /* Ignore case (gnu exension) */ + case 'I': + cflags |= REG_ICASE; + break; + case ';': + case '}': + goto out; + default: + bb_error_msg_and_die("bad option in substitution expression"); + } + } +out: + /* compile the match string into a regex */ + if (*match != '\0') { + /* If match is empty, we use last regex used at runtime */ + sed_cmd->sub_match = (regex_t *) xmalloc(sizeof(regex_t)); + xregcomp(sed_cmd->sub_match, match, cflags); + } + free(match); + + return idx; +} + +/* + * Process the commands arguments + */ +static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr) +{ + /* handle (s)ubstitution command */ + if (sed_cmd->cmd == 's') cmdstr += parse_subst_cmd(sed_cmd, cmdstr); + /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ + else if (strchr("aic", sed_cmd->cmd)) { + if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') + bb_error_msg_and_die + ("only a beginning address can be specified for edit commands"); + for(;;) { + if(*cmdstr=='\n' || *cmdstr=='\\') { + cmdstr++; + break; + } else if(isspace(*cmdstr)) cmdstr++; + else break; + } + sed_cmd->string = bb_xstrdup(cmdstr); + parse_escapes(sed_cmd->string,sed_cmd->string,strlen(cmdstr),0,0); + cmdstr += strlen(cmdstr); + /* handle file cmds: (r)ead */ + } else if(strchr("rw", sed_cmd->cmd)) { + if (sed_cmd->end_line || sed_cmd->end_match) + bb_error_msg_and_die("Command only uses one address"); + cmdstr += parse_file_cmd(sed_cmd, cmdstr, &sed_cmd->string); + if(sed_cmd->cmd=='w') + sed_cmd->file=bb_xfopen(sed_cmd->string,"w"); + /* handle branch commands */ + } else if (strchr(":bt", sed_cmd->cmd)) { + int length; + + while(isspace(*cmdstr)) cmdstr++; + length = strcspn(cmdstr, semicolon_whitespace); + if (length) { + sed_cmd->string = strndup(cmdstr, length); + cmdstr += length; + } + } + /* translation command */ + else if (sed_cmd->cmd == 'y') { + char *match, *replace; + int i=cmdstr[0]; + + cmdstr+=parse_regex_delim(cmdstr, &match, &replace)+1; + /* \n already parsed, but \delimiter needs unescaping. */ + parse_escapes(match,match,strlen(match),i,i); + parse_escapes(replace,replace,strlen(replace),i,i); + + sed_cmd->string = xcalloc(1, (strlen(match) + 1) * 2); + for (i = 0; match[i] && replace[i]; i++) { + sed_cmd->string[i * 2] = match[i]; + sed_cmd->string[(i * 2) + 1] = replace[i]; + } + free(match); + free(replace); + } + /* if it wasnt a single-letter command that takes no arguments + * then it must be an invalid command. + */ + else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) { + bb_error_msg_and_die("Unsupported command %c", sed_cmd->cmd); + } + + /* give back whatever's left over */ + return (cmdstr); +} + + +/* Parse address+command sets, skipping comment lines. */ + +void add_cmd(char *cmdstr) +{ + static char *add_cmd_line=NULL; + sed_cmd_t *sed_cmd; + int temp; + + /* Append this line to any unfinished line from last time. */ + if(add_cmd_line) { + int lastlen=strlen(add_cmd_line); + char *tmp=xmalloc(lastlen+strlen(cmdstr)+2); + + memcpy(tmp,add_cmd_line,lastlen); + tmp[lastlen]='\n'; + strcpy(tmp+lastlen+1,cmdstr); + free(add_cmd_line); + cmdstr=add_cmd_line=tmp; + } else add_cmd_line=NULL; + + /* If this line ends with backslash, request next line. */ + temp=strlen(cmdstr); + if(temp && cmdstr[temp-1]=='\\') { + if(!add_cmd_line) add_cmd_line=strdup(cmdstr); + add_cmd_line[temp-1]=0; + return; + } + + /* Loop parsing all commands in this line. */ + while(*cmdstr) { + /* Skip leading whitespace and semicolons */ + cmdstr += strspn(cmdstr, semicolon_whitespace); + + /* If no more commands, exit. */ + if(!*cmdstr) break; + + /* if this is a comment, jump past it and keep going */ + if (*cmdstr == '#') { + /* "#n" is the same as using -n on the command line */ + if (cmdstr[1] == 'n') be_quiet++; + if(!(cmdstr=strpbrk(cmdstr, "\n\r"))) break; + continue; + } + + /* parse the command + * format is: [addr][,addr][!]cmd + * |----||-----||-| + * part1 part2 part3 + */ + + sed_cmd = xcalloc(1, sizeof(sed_cmd_t)); + + /* first part (if present) is an address: either a '$', a number or a /regex/ */ + cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match); + + /* second part (if present) will begin with a comma */ + if (*cmdstr == ',') { + int idx; + + cmdstr++; + idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); + if (!idx) bb_error_msg_and_die("get_address: no address found in string\n"); + cmdstr += idx; + } + + /* skip whitespace before the command */ + while (isspace(*cmdstr)) cmdstr++; + + /* Check for inversion flag */ + if (*cmdstr == '!') { + sed_cmd->invert = 1; + cmdstr++; + + /* skip whitespace before the command */ + while (isspace(*cmdstr)) cmdstr++; + } + + /* last part (mandatory) will be a command */ + if (!*cmdstr) bb_error_msg_and_die("missing command"); + sed_cmd->cmd = *(cmdstr++); + cmdstr = parse_cmd_args(sed_cmd, cmdstr); + + /* Add the command to the command array */ + sed_cmd_tail->next = sed_cmd; + sed_cmd_tail = sed_cmd_tail->next; + } + + /* If we glued multiple lines together, free the memory. */ + if(add_cmd_line) { + free(add_cmd_line); + add_cmd_line=NULL; + } +} + +struct pipeline { + char *buf; /* Space to hold string */ + int idx; /* Space used */ + int len; /* Space allocated */ +} pipeline; + +#define PIPE_GROW 64 + +void pipe_putc(char c) +{ + if(pipeline.idx==pipeline.len) { + pipeline.buf = xrealloc(pipeline.buf, pipeline.len + PIPE_GROW); + pipeline.len+=PIPE_GROW; + } + pipeline.buf[pipeline.idx++] = (c); +} + +static void do_subst_w_backrefs(const char *line, const char *replace) +{ + int i,j; + + /* go through the replacement string */ + for (i = 0; replace[i]; i++) { + /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */ + if (replace[i] == '\\' && replace[i+1]>'0' && replace[i+1]<='9') { + int backref=replace[++i]-'0'; + + /* print out the text held in regmatch[backref] */ + if(regmatch[backref].rm_so != -1) + for (j = regmatch[backref].rm_so; j < regmatch[backref].rm_eo; j++) + pipe_putc(line[j]); + } + + /* if we find a backslash escaped character, print the character */ + else if (replace[i] == '\\') pipe_putc(replace[++i]); + + /* if we find an unescaped '&' print out the whole matched text. */ + else if (replace[i] == '&') + for (j = regmatch[0].rm_so; j < regmatch[0].rm_eo; j++) + pipe_putc(line[j]); + /* Otherwise just output the character. */ + else pipe_putc(replace[i]); + } +} + +static int do_subst_command(sed_cmd_t * sed_cmd, char **line) +{ + char *oldline = *line; + int altered = 0; + int match_count=0; + regex_t *current_regex; + + /* Handle empty regex. */ + if (sed_cmd->sub_match == NULL) { + current_regex = previous_regex_ptr; + if(!current_regex) + bb_error_msg_and_die("No previous regexp."); + } else previous_regex_ptr = current_regex = sed_cmd->sub_match; + + /* Find the first match */ + if(REG_NOMATCH==regexec(current_regex, oldline, 10, regmatch, 0)) + return 0; + + /* Initialize temporary output buffer. */ + pipeline.buf=xmalloc(PIPE_GROW); + pipeline.len=PIPE_GROW; + pipeline.idx=0; + + /* Now loop through, substituting for matches */ + do { + int i; + + /* Work around bug in glibc regexec, demonstrated by: + echo " a.b" | busybox sed 's [^ .]* x g' + The match_count check is so not to break + echo "hi" | busybox sed 's/^/!/g' */ + if(!regmatch[0].rm_so && !regmatch[0].rm_eo && match_count) { + pipe_putc(*(oldline++)); + continue; + } + + match_count++; + + /* If we aren't interested in this match, output old line to + end of match and continue */ + if(sed_cmd->which_match && sed_cmd->which_match!=match_count) { + for(i=0;istring); + + /* advance past the match */ + oldline += regmatch[0].rm_eo; + /* flag that something has changed */ + altered++; + + /* if we're not doing this globally, get out now */ + if (sed_cmd->which_match) break; + } while (*oldline && (regexec(current_regex, oldline, 10, regmatch, 0) != REG_NOMATCH)); + + /* Copy rest of string into output pipeline */ + + while(*oldline) pipe_putc(*(oldline++)); + pipe_putc(0); + + free(*line); + *line = pipeline.buf; + return altered; +} + +/* Set command pointer to point to this label. (Does not handle null label.) */ +static sed_cmd_t *branch_to(const char *label) +{ + sed_cmd_t *sed_cmd; + + for (sed_cmd = sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { + if ((sed_cmd->cmd == ':') && (sed_cmd->string) && (strcmp(sed_cmd->string, label) == 0)) { + return (sed_cmd); + } + } + bb_error_msg_and_die("Can't find label for jump to `%s'", label); +} + +/* Append copy of string to append buffer */ +static void append(char *s) +{ + struct append_list *temp=calloc(1,sizeof(struct append_list)); + + if(append_head) + append_tail=(append_tail->next=temp); + else append_head=append_tail=temp; + temp->string=strdup(s); +} + +static void flush_append(void) +{ + /* Output appended lines. */ + while(append_head) { + fprintf(nonstdout,"%s\n",append_head->string); + append_tail=append_head->next; + free(append_head->string); + free(append_head); + append_head=append_tail; + } + append_head=append_tail=NULL; +} + +/* Get next line of input, flushing append buffer and noting if we hit EOF + * without a newline on the last line. + */ +static char *get_next_line(FILE * file, int *no_newline) +{ + char *temp; + int len; + + flush_append(); + temp=bb_get_line_from_file(file); + if(temp) { + len=strlen(temp); + if(len && temp[len-1]=='\n') temp[len-1]=0; + else *no_newline=1; + } + + return temp; +} + +/* Output line of text. missing_newline means the last line output did not + end with a newline. no_newline means this line does not end with a + newline. */ + +static int puts_maybe_newline(char *s, FILE *file, int missing_newline, int no_newline) +{ + if(missing_newline) fputc('\n',file); + fputs(s,file); + if(!no_newline) fputc('\n',file); + + if(ferror(file)) { + fprintf(stderr,"Write failed.\n"); + exit(4); /* It's what gnu sed exits with... */ + } + + return no_newline; +} + +#define sed_puts(s,n) missing_newline=puts_maybe_newline(s,nonstdout,missing_newline,n) + +static void process_file(FILE *file) +{ + char *pattern_space, *next_line, *hold_space=NULL; + static int linenum = 0, missing_newline=0; + int no_newline,next_no_newline=0; + + next_line = get_next_line(file,&next_no_newline); + + /* go through every line in the file */ + for(;;) { + sed_cmd_t *sed_cmd; + int substituted=0; + + /* Advance to next line. Stop if out of lines. */ + if(!(pattern_space=next_line)) break; + no_newline=next_no_newline; + + /* Read one line in advance so we can act on the last line, the '$' address */ + next_line = get_next_line(file,&next_no_newline); + linenum++; +restart: + /* for every line, go through all the commands */ + for (sed_cmd = sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { + int old_matched, matched; + + old_matched = sed_cmd->in_match; + + /* Determine if this command matches this line: */ + + /* Are we continuing a previous multi-line match? */ + + sed_cmd->in_match = sed_cmd->in_match + + /* Or is no range necessary? */ + || (!sed_cmd->beg_line && !sed_cmd->end_line + && !sed_cmd->beg_match && !sed_cmd->end_match) + + /* Or did we match the start of a numerical range? */ + || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum)) + + /* Or does this line match our begin address regex? */ + || (sed_cmd->beg_match && + !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0)) + + /* Or did we match last line of input? */ + || (sed_cmd->beg_line == -1 && next_line == NULL); + + /* Snapshot the value */ + + matched = sed_cmd->in_match; + + /* Is this line the end of the current match? */ + + if(matched) { + sed_cmd->in_match = !( + /* has the ending line come, or is this a single address command? */ + (sed_cmd->end_line ? + sed_cmd->end_line==-1 ? + !next_line + : sed_cmd->end_line<=linenum + : !sed_cmd->end_match) + /* or does this line matches our last address regex */ + || (sed_cmd->end_match && old_matched && (regexec(sed_cmd->end_match, pattern_space, 0, NULL, 0) == 0)) + ); + } + + /* Skip blocks of commands we didn't match. */ + if (sed_cmd->cmd == '{') { + if(sed_cmd->invert ? matched : !matched) + while(sed_cmd && sed_cmd->cmd!='}') sed_cmd=sed_cmd->next; + if(!sed_cmd) bb_error_msg_and_die("Unterminated {"); + continue; + } + + /* Okay, so did this line match? */ + if (sed_cmd->invert ? !matched : matched) { + /* Update last used regex in case a blank substitute BRE is found */ + if (sed_cmd->beg_match) { + previous_regex_ptr = sed_cmd->beg_match; + } + + /* actual sedding */ + switch (sed_cmd->cmd) { + + /* Print line number */ + case '=': + fprintf(nonstdout,"%d\n", linenum); + break; + + /* Write the current pattern space up to the first newline */ + case 'P': + { + char *tmp = strchr(pattern_space, '\n'); + + if (tmp) { + *tmp = '\0'; + sed_puts(pattern_space,1); + *tmp = '\n'; + break; + } + /* Fall Through */ + } + + /* Write the current pattern space to output */ + case 'p': + sed_puts(pattern_space,no_newline); + break; + /* Delete up through first newline */ + case 'D': + { + char *tmp = strchr(pattern_space,'\n'); + + if(tmp) { + tmp=bb_xstrdup(tmp+1); + free(pattern_space); + pattern_space=tmp; + goto restart; + } + } + /* discard this line. */ + case 'd': + goto discard_line; + + /* Substitute with regex */ + case 's': + if(do_subst_command(sed_cmd, &pattern_space)) { + substituted|=1; + + /* handle p option */ + if(sed_cmd->sub_p) + sed_puts(pattern_space,no_newline); + /* handle w option */ + if(sed_cmd->file) + sed_cmd->no_newline=puts_maybe_newline(pattern_space, sed_cmd->file, sed_cmd->no_newline, no_newline); + + } + break; + + /* Append line to linked list to be printed later */ + case 'a': + { + append(sed_cmd->string); + break; + } + + /* Insert text before this line */ + case 'i': + sed_puts(sed_cmd->string,1); + break; + + /* Cut and paste text (replace) */ + case 'c': + /* Only triggers on last line of a matching range. */ + if (!sed_cmd->in_match) sed_puts(sed_cmd->string,1); + goto discard_line; + + /* Read file, append contents to output */ + case 'r': + { + FILE *outfile; + + outfile = fopen(sed_cmd->string, "r"); + if (outfile) { + char *line; + + while ((line = bb_get_chomped_line_from_file(outfile)) + != NULL) + append(line); + bb_xprint_and_close_file(outfile); + } + + break; + } + + /* Write pattern space to file. */ + case 'w': + sed_cmd->no_newline=puts_maybe_newline(pattern_space,sed_cmd->file, sed_cmd->no_newline,no_newline); + break; + + /* Read next line from input */ + case 'n': + if (!be_quiet) + sed_puts(pattern_space,no_newline); + if (next_line) { + free(pattern_space); + pattern_space = next_line; + no_newline=next_no_newline; + next_line = get_next_line(file,&next_no_newline); + linenum++; + break; + } + /* fall through */ + + /* Quit. End of script, end of input. */ + case 'q': + /* Exit the outer while loop */ + free(next_line); + next_line = NULL; + goto discard_commands; + + /* Append the next line to the current line */ + case 'N': + { + /* If no next line, jump to end of script and exit. */ + if (next_line == NULL) { + /* Jump to end of script and exit */ + free(next_line); + next_line = NULL; + goto discard_line; + /* append next_line, read new next_line. */ + } else { + int len=strlen(pattern_space); + + pattern_space = realloc(pattern_space, len + strlen(next_line) + 2); + pattern_space[len]='\n'; + strcpy(pattern_space+len+1, next_line); + no_newline=next_no_newline; + next_line = get_next_line(file,&next_no_newline); + linenum++; + } + break; + } + + /* Test if substition worked, branch if so. */ + case 't': + if (!substituted) break; + substituted=0; + /* Fall through */ + /* Branch to label */ + case 'b': + if (!sed_cmd->string) goto discard_commands; + else sed_cmd = branch_to(sed_cmd->string); + break; + /* Transliterate characters */ + case 'y': + { + int i; + + for (i = 0; pattern_space[i]; i++) { + int j; + + for (j = 0; sed_cmd->string[j]; j += 2) { + if (pattern_space[i] == sed_cmd->string[j]) { + pattern_space[i] = sed_cmd->string[j + 1]; + } + } + } + + break; + } + case 'g': /* Replace pattern space with hold space */ + free(pattern_space); + if (hold_space) { + pattern_space = strdup(hold_space); + no_newline=0; + } + break; + case 'G': /* Append newline and hold space to pattern space */ + { + int pattern_space_size = 2; + int hold_space_size = 0; + + if (pattern_space) + pattern_space_size += strlen(pattern_space); + if (hold_space) hold_space_size = strlen(hold_space); + pattern_space = xrealloc(pattern_space, pattern_space_size + hold_space_size); + if (pattern_space_size == 2) pattern_space[0]=0; + strcat(pattern_space, "\n"); + if (hold_space) strcat(pattern_space, hold_space); + no_newline=0; + + break; + } + case 'h': /* Replace hold space with pattern space */ + free(hold_space); + hold_space = strdup(pattern_space); + break; + case 'H': /* Append newline and pattern space to hold space */ + { + int hold_space_size = 2; + int pattern_space_size = 0; + + if (hold_space) hold_space_size += strlen(hold_space); + if (pattern_space) + pattern_space_size = strlen(pattern_space); + hold_space = xrealloc(hold_space, + hold_space_size + pattern_space_size); + + if (hold_space_size == 2) hold_space[0]=0; + strcat(hold_space, "\n"); + if (pattern_space) strcat(hold_space, pattern_space); + + break; + } + case 'x': /* Exchange hold and pattern space */ + { + char *tmp = pattern_space; + pattern_space = hold_space; + no_newline=0; + hold_space = tmp; + break; + } + } + } + } + + /* + * exit point from sedding... + */ +discard_commands: + /* we will print the line unless we were told to be quiet ('-n') + or if the line was suppressed (ala 'd'elete) */ + if (!be_quiet) sed_puts(pattern_space,no_newline); + + /* Delete and such jump here. */ +discard_line: + flush_append(); + free(pattern_space); + } +} + +/* It is possible to have a command line argument with embedded + newlines. This counts as multiple command lines. */ + +static void add_cmd_block(char *cmdstr) +{ + int go=1; + char *temp=bb_xstrdup(cmdstr),*temp2=temp; + + while(go) { + int len=strcspn(temp2,"\n"); + if(!temp2[len]) go=0; + else temp2[len]=0; + add_cmd(temp2); + temp2+=len+1; + } + free(temp); +} + +extern int sed_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int opt; + uint8_t getpat = 1; + +#ifdef CONFIG_FEATURE_CLEAN_UP + /* destroy command strings on exit */ + if (atexit(free_and_close_stuff) == -1) + bb_perror_msg_and_die("atexit"); +#endif + +#define LIE_TO_AUTOCONF +#ifdef LIE_TO_AUTOCONF + if(argc==2 && !strcmp(argv[1],"--version")) { + printf("This is not GNU sed version 4.0\n"); + exit(0); + } +#endif + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "irne:f:")) > 0) { + switch (opt) { + case 'i': + in_place++; + atexit(cleanup_outname); + break; + case 'r': + regex_type|=REG_EXTENDED; + break; + case 'n': + be_quiet++; + break; + case 'e': + add_cmd_block(optarg); + getpat=0; + break; + case 'f': + { + FILE *cmdfile; + char *line; + + cmdfile = bb_xfopen(optarg, "r"); + + while ((line = bb_get_chomped_line_from_file(cmdfile)) + != NULL) { + add_cmd(line); + getpat=0; + free(line); + } + bb_xprint_and_close_file(cmdfile); + + break; + } + default: + bb_show_usage(); + } + } + + /* if we didn't get a pattern from a -e and no command file was specified, + * argv[optind] should be the pattern. no pattern, no worky */ + if(getpat) { + if (argv[optind] == NULL) + bb_show_usage(); + else + add_cmd_block(argv[optind++]); + } + /* Flush any unfinished commands. */ + add_cmd(""); + + /* By default, we write to stdout */ + nonstdout=stdout; + + /* argv[(optind)..(argc-1)] should be names of file to process. If no + * files were specified or '-' was specified, take input from stdin. + * Otherwise, we process all the files specified. */ + if (argv[optind] == NULL) { + if(in_place) { + fprintf(stderr,"sed: Filename required for -i\n"); + exit(1); + } + process_file(stdin); + } else { + int i; + FILE *file; + + for (i = optind; i < argc; i++) { + if(!strcmp(argv[i], "-") && !in_place) { + process_file(stdin); + } else { + file = bb_wfopen(argv[i], "r"); + if (file) { + if(in_place) { + struct stat statbuf; + outname=bb_xstrndup(argv[i],strlen(argv[i])+6); + strcat(outname,"XXXXXX"); + /* Set permissions of output file */ + fstat(fileno(file),&statbuf); + mkstemp(outname); + nonstdout=bb_wfopen(outname,"w"); + /* Set permissions of output file */ + fstat(fileno(file),&statbuf); + fchmod(fileno(nonstdout),statbuf.st_mode); + atexit(cleanup_outname); + } + process_file(file); + fclose(file); + if(in_place) { + fclose(nonstdout); + nonstdout=stdout; + unlink(argv[i]); + rename(outname,argv[i]); + free(outname); + outname=0; + } + } else { + status = EXIT_FAILURE; + } + } + } + } + + return status; +} diff --git a/busybox/editors/vi.c b/busybox/editors/vi.c new file mode 100644 index 000000000..cd6cf0ea1 --- /dev/null +++ b/busybox/editors/vi.c @@ -0,0 +1,3983 @@ +/* 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.38 2004/08/19 19:15:06 andersen Exp $"; + +/* + * To compile for standalone use: + * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c + * or + * gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c # include testing features + * strip vi + */ + +/* + * Things To Do: + * EXINIT + * $HOME/.exrc and ./.exrc + * add magic to search /foo.*bar + * add :help command + * :map macros + * how about mode lines: vi: set sw=8 ts=8: + * if mark[] values were line numbers rather than pointers + * it would be easier to change the mark when add/delete lines + * More intelligence in refresh() + * ":r !cmd" and "!cmd" to filter text through an external command + * A true "undo" facility + * An "ex" line oriented mode- maybe using "cmdedit" + */ + +//---- Feature -------------- Bytes to implement +#ifdef STANDALONE +#define vi_main main +#define CONFIG_FEATURE_VI_COLON // 4288 +#define CONFIG_FEATURE_VI_YANKMARK // 1408 +#define CONFIG_FEATURE_VI_SEARCH // 1088 +#define CONFIG_FEATURE_VI_USE_SIGNALS // 1056 +#define CONFIG_FEATURE_VI_DOT_CMD // 576 +#define CONFIG_FEATURE_VI_READONLY // 128 +#define CONFIG_FEATURE_VI_SETOPTS // 576 +#define CONFIG_FEATURE_VI_SET // 224 +#define CONFIG_FEATURE_VI_WIN_RESIZE // 256 WIN_RESIZE +// To test editor using CRASHME: +// vi -C filename +// To stop testing, wait until all to text[] is deleted, or +// Ctrl-Z and kill -9 %1 +// while in the editor Ctrl-T will toggle the crashme function on and off. +//#define CONFIG_FEATURE_VI_CRASHME // randomly pick commands to execute +#endif /* STANDALONE */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef STANDALONE +#include "busybox.h" +#endif /* STANDALONE */ + +#ifdef CONFIG_LOCALE_SUPPORT +#define Isprint(c) isprint((c)) +#else +#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') ) +#endif + +#ifndef TRUE +#define TRUE ((int)1) +#define FALSE ((int)0) +#endif /* TRUE */ +#define MAX_SCR_COLS BUFSIZ + +// Misc. non-Ascii keys that report an escape sequence +#define VI_K_UP 128 // cursor key Up +#define VI_K_DOWN 129 // cursor key Down +#define VI_K_RIGHT 130 // Cursor Key Right +#define VI_K_LEFT 131 // cursor key Left +#define VI_K_HOME 132 // Cursor Key Home +#define VI_K_END 133 // Cursor Key End +#define VI_K_INSERT 134 // Cursor Key Insert +#define VI_K_PAGEUP 135 // Cursor Key Page Up +#define VI_K_PAGEDOWN 136 // Cursor Key Page Down +#define VI_K_FUN1 137 // Function Key F1 +#define VI_K_FUN2 138 // Function Key F2 +#define VI_K_FUN3 139 // Function Key F3 +#define VI_K_FUN4 140 // Function Key F4 +#define VI_K_FUN5 141 // Function Key F5 +#define VI_K_FUN6 142 // Function Key F6 +#define VI_K_FUN7 143 // Function Key F7 +#define VI_K_FUN8 144 // Function Key F8 +#define VI_K_FUN9 145 // Function Key F9 +#define VI_K_FUN10 146 // Function Key F10 +#define VI_K_FUN11 147 // Function Key F11 +#define VI_K_FUN12 148 // Function Key F12 + +/* vt102 typical ESC sequence */ +/* terminal standout start/normal ESC sequence */ +static const char SOs[] = "\033[7m"; +static const char SOn[] = "\033[0m"; +/* terminal bell sequence */ +static const char bell[] = "\007"; +/* Clear-end-of-line and Clear-end-of-screen ESC sequence */ +static const char Ceol[] = "\033[0K"; +static const char Ceos [] = "\033[0J"; +/* Cursor motion arbitrary destination ESC sequence */ +static const char CMrc[] = "\033[%d;%dH"; +/* Cursor motion up and down ESC sequence */ +static const char CMup[] = "\033[A"; +static const char CMdown[] = "\n"; + + +static const int YANKONLY = FALSE; +static const int YANKDEL = TRUE; +static const int FORWARD = 1; // code depends on "1" for array index +static const int BACK = -1; // code depends on "-1" for array index +static const int LIMITED = 0; // how much of text[] in char_search +static const int FULL = 1; // how much of text[] in char_search + +static const int S_BEFORE_WS = 1; // used in skip_thing() for moving "dot" +static const int S_TO_WS = 2; // used in skip_thing() for moving "dot" +static const int S_OVER_WS = 3; // used in skip_thing() for moving "dot" +static const int S_END_PUNCT = 4; // used in skip_thing() for moving "dot" +static const int S_END_ALNUM = 5; // used in skip_thing() for moving "dot" + +typedef unsigned char Byte; + +static int vi_setops; +#define VI_AUTOINDENT 1 +#define VI_SHOWMATCH 2 +#define VI_IGNORECASE 4 +#define VI_ERR_METHOD 8 +#define autoindent (vi_setops & VI_AUTOINDENT) +#define showmatch (vi_setops & VI_SHOWMATCH ) +#define ignorecase (vi_setops & VI_IGNORECASE) +/* indicate error with beep or flash */ +#define err_method (vi_setops & VI_ERR_METHOD) + + +static int editing; // >0 while we are editing a file +static int cmd_mode; // 0=command 1=insert +static int file_modified; // buffer contents changed +static int fn_start; // index of first cmd line file name +static int save_argc; // how many file names on cmd line +static int cmdcnt; // repetition count +static fd_set rfds; // use select() for small sleeps +static struct timeval tv; // use select() for small sleeps +static int rows, columns; // the terminal screen is this size +static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset +static Byte *status_buffer; // mesages to the user +static Byte *cfn; // previous, current, and next file name +static Byte *text, *end, *textend; // pointers to the user data in memory +static Byte *screen; // pointer to the virtual screen buffer +static int screensize; // and its size +static Byte *screenbegin; // index into text[], of top line on the screen +static Byte *dot; // where all the action takes place +static int tabstop; +static struct termios term_orig, term_vi; // remember what the cooked mode was +static Byte erase_char; // the users erase character +static Byte last_input_char; // last char read from user +static Byte last_forward_char; // last char searched for with 'f' + +#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR +static int last_row; // where the cursor was last moved to +#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */ +#ifdef CONFIG_FEATURE_VI_USE_SIGNALS +static jmp_buf restart; // catch_sig() +#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ +#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME) +static int my_pid; +#endif +#ifdef CONFIG_FEATURE_VI_DOT_CMD +static int adding2q; // are we currently adding user input to q +static Byte *last_modifying_cmd; // last modifying cmd for "." +static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read" +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ +#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK) +static Byte *modifying_cmds; // cmds that modify text[] +#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */ +#ifdef CONFIG_FEATURE_VI_READONLY +static int vi_readonly, readonly; +#endif /* CONFIG_FEATURE_VI_READONLY */ +#ifdef CONFIG_FEATURE_VI_YANKMARK +static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27 +static int YDreg, Ureg; // default delete register and orig line for "U" +static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context '' +static Byte *context_start, *context_end; +#endif /* CONFIG_FEATURE_VI_YANKMARK */ +#ifdef CONFIG_FEATURE_VI_SEARCH +static Byte *last_search_pattern; // last pattern from a '/' or '?' search +#endif /* CONFIG_FEATURE_VI_SEARCH */ + + +static void edit_file(Byte *); // edit one file +static void do_cmd(Byte); // execute a command +static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot +static Byte *begin_line(Byte *); // return pointer to cur line B-o-l +static Byte *end_line(Byte *); // return pointer to cur line E-o-l +static Byte *prev_line(Byte *); // return pointer to prev line B-o-l +static Byte *next_line(Byte *); // return pointer to next line B-o-l +static Byte *end_screen(void); // get pointer to last char on screen +static int count_lines(Byte *, Byte *); // count line from start to stop +static Byte *find_line(int); // find begining of line #li +static Byte *move_to_col(Byte *, int); // move "p" to column l +static int isblnk(Byte); // is the char a blank or tab +static void dot_left(void); // move dot left- dont leave line +static void dot_right(void); // move dot right- dont leave line +static void dot_begin(void); // move dot to B-o-l +static void dot_end(void); // move dot to E-o-l +static void dot_next(void); // move dot to next line B-o-l +static void dot_prev(void); // move dot to prev line B-o-l +static void dot_scroll(int, int); // move the screen up or down +static void dot_skip_over_ws(void); // move dot pat WS +static void dot_delete(void); // delete the char at 'dot' +static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end" +static Byte *new_screen(int, int); // malloc virtual screen memory +static Byte *new_text(int); // malloc memory for text[] buffer +static Byte *char_insert(Byte *, Byte); // insert the char c at 'p' +static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p' +static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object +static int st_test(Byte *, int, int, Byte *); // helper for skip_thing() +static Byte *skip_thing(Byte *, int, int, int); // skip some object +static Byte *find_pair(Byte *, Byte); // find matching pair () [] {} +static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole +static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole +static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete +static void show_help(void); // display some help info +static void rawmode(void); // set "raw" mode on tty +static void cookmode(void); // return to "cooked" mode on tty +static int mysleep(int); // sleep for 'h' 1/100 seconds +static Byte readit(void); // read (maybe cursor) key from stdin +static Byte get_one_char(void); // read 1 char from stdin +static int file_size(const Byte *); // what is the byte size of "fn" +static int file_insert(Byte *, Byte *, int); +static int file_write(Byte *, Byte *, Byte *); +static void place_cursor(int, int, int); +static void screen_erase(void); +static void clear_to_eol(void); +static void clear_to_eos(void); +static void standout_start(void); // send "start reverse video" sequence +static void standout_end(void); // send "end reverse video" sequence +static void flash(int); // flash the terminal screen +static void show_status_line(void); // put a message on the bottom line +static void psb(const char *, ...); // Print Status Buf +static void psbs(const char *, ...); // Print Status Buf in standout mode +static void ni(Byte *); // display messages +static void edit_status(void); // show file status on status line +static void redraw(int); // force a full screen refresh +static void format_line(Byte*, Byte*, int); +static void refresh(int); // update the terminal from screen[] + +static void Indicate_Error(void); // use flash or beep to indicate error +#define indicate_error(c) Indicate_Error() + +#ifdef CONFIG_FEATURE_VI_SEARCH +static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p +static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase" +#endif /* CONFIG_FEATURE_VI_SEARCH */ +#ifdef CONFIG_FEATURE_VI_COLON +static void Hit_Return(void); +static Byte *get_one_address(Byte *, int *); // get colon addr, if present +static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present +static void colon(Byte *); // execute the "colon" mode cmds +#endif /* CONFIG_FEATURE_VI_COLON */ +#ifdef CONFIG_FEATURE_VI_USE_SIGNALS +static void winch_sig(int); // catch window size changes +static void suspend_sig(int); // catch ctrl-Z +static void catch_sig(int); // catch ctrl-C and alarm time-outs +static void core_sig(int); // catch a core dump signal +#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ +#ifdef CONFIG_FEATURE_VI_DOT_CMD +static void start_new_cmd_q(Byte); // new queue for command +static void end_cmd_q(void); // stop saving input chars +#else /* CONFIG_FEATURE_VI_DOT_CMD */ +#define end_cmd_q() +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ +#ifdef CONFIG_FEATURE_VI_WIN_RESIZE +static void window_size_get(int); // find out what size the window is +#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */ +#ifdef CONFIG_FEATURE_VI_SETOPTS +static void showmatching(Byte *); // show the matching pair () [] {} +#endif /* CONFIG_FEATURE_VI_SETOPTS */ +#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME) +static Byte *string_insert(Byte *, Byte *); // insert the string at 'p' +#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */ +#ifdef CONFIG_FEATURE_VI_YANKMARK +static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register +static Byte what_reg(void); // what is letter of current YDreg +static void check_context(Byte); // remember context for '' command +#endif /* CONFIG_FEATURE_VI_YANKMARK */ +#ifdef CONFIG_FEATURE_VI_CRASHME +static void crash_dummy(); +static void crash_test(); +static int crashme = 0; +#endif /* CONFIG_FEATURE_VI_CRASHME */ + + +static void write1(const char *out) +{ + fputs(out, stdout); +} + +extern int vi_main(int argc, char **argv) +{ + int c; + RESERVE_CONFIG_BUFFER(STATUS_BUFFER, 200); + +#ifdef CONFIG_FEATURE_VI_YANKMARK + int i; +#endif /* CONFIG_FEATURE_VI_YANKMARK */ +#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME) + my_pid = getpid(); +#endif +#ifdef CONFIG_FEATURE_VI_CRASHME + (void) srand((long) my_pid); +#endif /* CONFIG_FEATURE_VI_CRASHME */ + + status_buffer = STATUS_BUFFER; + +#ifdef CONFIG_FEATURE_VI_READONLY + vi_readonly = readonly = FALSE; + if (strncmp(argv[0], "view", 4) == 0) { + readonly = TRUE; + vi_readonly = TRUE; + } +#endif /* CONFIG_FEATURE_VI_READONLY */ + vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD; +#ifdef CONFIG_FEATURE_VI_YANKMARK + for (i = 0; i < 28; i++) { + reg[i] = 0; + } // init the yank regs +#endif /* CONFIG_FEATURE_VI_YANKMARK */ +#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK) + modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[] +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ + + // 1- process $HOME/.exrc file + // 2- process EXINIT variable from environment + // 3- process command line args + while ((c = getopt(argc, argv, "hCR")) != -1) { + switch (c) { +#ifdef CONFIG_FEATURE_VI_CRASHME + case 'C': + crashme = 1; + break; +#endif /* CONFIG_FEATURE_VI_CRASHME */ +#ifdef CONFIG_FEATURE_VI_READONLY + case 'R': // Read-only flag + readonly = TRUE; + vi_readonly = TRUE; + break; +#endif /* CONFIG_FEATURE_VI_READONLY */ + //case 'r': // recover flag- ignore- we don't use tmp file + //case 'x': // encryption flag- ignore + //case 'c': // execute command first + //case 'h': // help -- just use default + default: + show_help(); + return 1; + } + } + + // The argv array can be used by the ":next" and ":rewind" commands + // save optind. + fn_start = optind; // remember first file name for :next and :rew + save_argc = argc; + + //----- This is the main file handling loop -------------- + if (optind >= argc) { + editing = 1; // 0= exit, 1= one file, 2= multiple files + edit_file(0); + } else { + for (; optind < argc; optind++) { + editing = 1; // 0=exit, 1=one file, 2+ =many files + free(cfn); + cfn = (Byte *) bb_xstrdup(argv[optind]); + edit_file(cfn); + } + } + //----------------------------------------------------------- + + return (0); +} + +#ifdef CONFIG_FEATURE_VI_WIN_RESIZE +//----- See what the window size currently is -------------------- +static inline void window_size_get(int fd) +{ + get_terminal_width_height(fd, &columns, &rows); +} +#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */ + +static void edit_file(Byte * fn) +{ + Byte c; + int cnt, size, ch; + +#ifdef CONFIG_FEATURE_VI_USE_SIGNALS + int sig; +#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ +#ifdef CONFIG_FEATURE_VI_YANKMARK + static Byte *cur_line; +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + + rawmode(); + rows = 24; + columns = 80; + ch= -1; +#ifdef CONFIG_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */ + new_screen(rows, columns); // get memory for virtual screen + + cnt = file_size(fn); // file size + size = 2 * cnt; // 200% of file size + new_text(size); // get a text[] buffer + screenbegin = dot = end = text; + if (fn != 0) { + ch= file_insert(fn, text, cnt); + } + if (ch < 1) { + (void) char_insert(text, '\n'); // start empty buf with dummy line + } + file_modified = FALSE; +#ifdef CONFIG_FEATURE_VI_YANKMARK + YDreg = 26; // default Yank/Delete reg + Ureg = 27; // hold orig line for "U" cmd + for (cnt = 0; cnt < 28; cnt++) { + mark[cnt] = 0; + } // init the marks + mark[26] = mark[27] = text; // init "previous context" +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + + last_forward_char = last_input_char = '\0'; + crow = 0; + ccol = 0; + edit_status(); + +#ifdef CONFIG_FEATURE_VI_USE_SIGNALS + catch_sig(0); + core_sig(0); + signal(SIGWINCH, winch_sig); + signal(SIGTSTP, suspend_sig); + sig = setjmp(restart); + if (sig != 0) { + const char *msg = ""; + + if (sig == SIGWINCH) + msg = "(window resize)"; + if (sig == SIGHUP) + msg = "(hangup)"; + if (sig == SIGINT) + msg = "(interrupt)"; + if (sig == SIGTERM) + msg = "(terminate)"; + if (sig == SIGBUS) + msg = "(bus error)"; + if (sig == SIGSEGV) + msg = "(I tried to touch invalid memory)"; + if (sig == SIGALRM) + msg = "(alarm)"; + + psbs("-- caught signal %d %s--", sig, msg); + screenbegin = dot = text; + } +#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ + + editing = 1; + cmd_mode = 0; // 0=command 1=insert 2='R'eplace + cmdcnt = 0; + tabstop = 8; + offset = 0; // no horizontal offset + c = '\0'; +#ifdef CONFIG_FEATURE_VI_DOT_CMD + free(last_modifying_cmd); + free(ioq_start); + ioq = ioq_start = last_modifying_cmd = 0; + adding2q = 0; +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ + redraw(FALSE); // dont force every col re-draw + show_status_line(); + + //------This is the main Vi cmd handling loop ----------------------- + while (editing > 0) { +#ifdef CONFIG_FEATURE_VI_CRASHME + if (crashme > 0) { + if ((end - text) > 1) { + crash_dummy(); // generate a random command + } else { + crashme = 0; + dot = + string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string + refresh(FALSE); + } + } +#endif /* CONFIG_FEATURE_VI_CRASHME */ + last_input_char = c = get_one_char(); // get a cmd from user +#ifdef CONFIG_FEATURE_VI_YANKMARK + // save a copy of the current line- for the 'U" command + if (begin_line(dot) != cur_line) { + cur_line = begin_line(dot); + text_yank(begin_line(dot), end_line(dot), Ureg); + } +#endif /* CONFIG_FEATURE_VI_YANKMARK */ +#ifdef CONFIG_FEATURE_VI_DOT_CMD + // These are commands that change text[]. + // Remember the input for the "." command + if (!adding2q && ioq_start == 0 + && strchr((char *) modifying_cmds, c) != NULL) { + start_new_cmd_q(c); + } +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ + do_cmd(c); // execute the user command + // + // poll to see if there is input already waiting. if we are + // not able to display output fast enough to keep up, skip + // the display update until we catch up with input. + if (mysleep(0) == 0) { + // no input pending- so update output + refresh(FALSE); + show_status_line(); + } +#ifdef CONFIG_FEATURE_VI_CRASHME + if (crashme > 0) + crash_test(); // test editor variables +#endif /* CONFIG_FEATURE_VI_CRASHME */ + } + //------------------------------------------------------------------- + + place_cursor(rows, 0, FALSE); // go to bottom of screen + clear_to_eol(); // Erase to end of line + cookmode(); +} + +//----- The Colon commands ------------------------------------- +#ifdef CONFIG_FEATURE_VI_COLON +static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present +{ + int st; + Byte *q; + +#ifdef CONFIG_FEATURE_VI_YANKMARK + Byte c; +#endif /* CONFIG_FEATURE_VI_YANKMARK */ +#ifdef CONFIG_FEATURE_VI_SEARCH + Byte *pat, buf[BUFSIZ]; +#endif /* CONFIG_FEATURE_VI_SEARCH */ + + *addr = -1; // assume no addr + if (*p == '.') { // the current line + p++; + q = begin_line(dot); + *addr = count_lines(text, q); +#ifdef CONFIG_FEATURE_VI_YANKMARK + } else if (*p == '\'') { // is this a mark addr + p++; + c = tolower(*p); + p++; + if (c >= 'a' && c <= 'z') { + // we have a mark + c = c - 'a'; + q = mark[(int) c]; + if (q != NULL) { // is mark valid + *addr = count_lines(text, q); // count lines + } + } +#endif /* CONFIG_FEATURE_VI_YANKMARK */ +#ifdef CONFIG_FEATURE_VI_SEARCH + } else if (*p == '/') { // a search pattern + q = buf; + for (p++; *p; p++) { + if (*p == '/') + break; + *q++ = *p; + *q = '\0'; + } + pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern + if (*p == '/') + p++; + q = char_search(dot, pat, FORWARD, FULL); + if (q != NULL) { + *addr = count_lines(text, q); + } + free(pat); +#endif /* CONFIG_FEATURE_VI_SEARCH */ + } else if (*p == '$') { // the last line in file + p++; + q = begin_line(end - 1); + *addr = count_lines(text, q); + } else if (isdigit(*p)) { // specific line number + sscanf((char *) p, "%d%n", addr, &st); + p += st; + } else { // I don't reconise this + // unrecognised address- assume -1 + *addr = -1; + } + return (p); +} + +static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present +{ + //----- get the address' i.e., 1,3 'a,'b ----- + // get FIRST addr, if present + while (isblnk(*p)) + p++; // skip over leading spaces + if (*p == '%') { // alias for 1,$ + p++; + *b = 1; + *e = count_lines(text, end-1); + goto ga0; + } + p = get_one_address(p, b); + while (isblnk(*p)) + p++; + if (*p == ',') { // is there a address separator + p++; + while (isblnk(*p)) + p++; + // get SECOND addr, if present + p = get_one_address(p, e); + } +ga0: + while (isblnk(*p)) + p++; // skip over trailing spaces + return (p); +} + +#ifdef CONFIG_FEATURE_VI_SETOPTS +static void setops(const Byte *args, const char *opname, int flg_no, + const char *short_opname, int opt) +{ + const char *a = (char *) args + flg_no; + int l = strlen(opname) - 1; /* opname have + ' ' */ + + if (strncasecmp(a, opname, l) == 0 || + strncasecmp(a, short_opname, 2) == 0) { + if(flg_no) + vi_setops &= ~opt; + else + vi_setops |= opt; + } +} +#endif + +static void colon(Byte * buf) +{ + Byte c, *orig_buf, *buf1, *q, *r; + Byte *fn, cmd[BUFSIZ], args[BUFSIZ]; + int i, l, li, ch, st, b, e; + int useforce = FALSE, forced = FALSE; + struct stat st_buf; + + // :3154 // if (-e line 3154) goto it else stay put + // :4,33w! foo // write a portion of buffer to file "foo" + // :w // write all of buffer to current file + // :q // quit + // :q! // quit- dont care about modified file + // :'a,'z!sort -u // filter block through sort + // :'f // goto mark "f" + // :'fl // list literal the mark "f" line + // :.r bar // read file "bar" into buffer before dot + // :/123/,/abc/d // delete lines from "123" line to "abc" line + // :/xyz/ // goto the "xyz" line + // :s/find/replace/ // substitute pattern "find" with "replace" + // :! // run then return + // + + if (strlen((char *) buf) <= 0) + goto vc1; + if (*buf == ':') + buf++; // move past the ':' + + li = st = ch = i = 0; + b = e = -1; + q = text; // assume 1,$ for the range + r = end - 1; + li = count_lines(text, end - 1); + fn = cfn; // default to current file + memset(cmd, '\0', BUFSIZ); // clear cmd[] + memset(args, '\0', BUFSIZ); // clear args[] + + // look for optional address(es) :. :1 :1,9 :'q,'a :% + buf = get_address(buf, &b, &e); + + // remember orig command line + orig_buf = buf; + + // get the COMMAND into cmd[] + buf1 = cmd; + while (*buf != '\0') { + if (isspace(*buf)) + break; + *buf1++ = *buf++; + } + // get any ARGuments + while (isblnk(*buf)) + buf++; + strcpy((char *) args, (char *) buf); + buf1 = last_char_is((char *)cmd, '!'); + if (buf1) { + useforce = TRUE; + *buf1 = '\0'; // get rid of ! + } + if (b >= 0) { + // if there is only one addr, then the addr + // is the line number of the single line the + // user wants. So, reset the end + // pointer to point at end of the "b" line + q = find_line(b); // what line is #b + r = end_line(q); + li = 1; + } + if (e >= 0) { + // we were given two addrs. change the + // end pointer to the addr given by user. + r = find_line(e); // what line is #e + r = end_line(r); + li = e - b + 1; + } + // ------------ now look for the command ------------ + i = strlen((char *) cmd); + if (i == 0) { // :123CR goto line #123 + if (b >= 0) { + dot = find_line(b); // what line is #b + dot_skip_over_ws(); + } + } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd + // :!ls run the + (void) alarm(0); // wait for input- no alarms + place_cursor(rows - 1, 0, FALSE); // go to Status line + clear_to_eol(); // clear the line + cookmode(); + system(orig_buf+1); // run the cmd + rawmode(); + Hit_Return(); // let user see results + (void) alarm(3); // done waiting for input + } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address + if (b < 0) { // no addr given- use defaults + b = e = count_lines(text, dot); + } + psb("%d", b); + } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines + if (b < 0) { // no addr given- use defaults + q = begin_line(dot); // assume .,. for the range + r = end_line(dot); + } + dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines + dot_skip_over_ws(); + } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file + int sr; + sr= 0; + // don't edit, if the current file has been modified + if (file_modified && ! useforce) { + psbs("No write since last change (:edit! overrides)"); + goto vc1; + } + if (strlen(args) > 0) { + // the user supplied a file name + fn= args; + } else if (cfn != 0 && strlen(cfn) > 0) { + // no user supplied name- use the current filename + fn= cfn; + goto vc5; + } else { + // no user file name, no current name- punt + psbs("No current filename"); + goto vc1; + } + + // see if file exists- if not, its just a new file request + if ((sr=stat((char*)fn, &st_buf)) < 0) { + // This is just a request for a new file creation. + // The file_insert below will fail but we get + // an empty buffer with a file name. Then the "write" + // command can do the create. + } else { + if ((st_buf.st_mode & (S_IFREG)) == 0) { + // This is not a regular file + psbs("\"%s\" is not a regular file", fn); + goto vc1; + } + if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) { + // dont have any read permissions + psbs("\"%s\" is not readable", fn); + goto vc1; + } + } + + // There is a read-able regular file + // make this the current file + q = (Byte *) bb_xstrdup((char *) fn); // save the cfn + free(cfn); // free the old name + cfn = q; // remember new cfn + + vc5: + // delete all the contents of text[] + new_text(2 * file_size(fn)); + screenbegin = dot = end = text; + + // insert new file + ch = file_insert(fn, text, file_size(fn)); + + if (ch < 1) { + // start empty buf with dummy line + (void) char_insert(text, '\n'); + ch= 1; + } + file_modified = FALSE; +#ifdef CONFIG_FEATURE_VI_YANKMARK + if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) { + free(reg[Ureg]); // free orig line reg- for 'U' + reg[Ureg]= 0; + } + if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) { + free(reg[YDreg]); // free default yank/delete register + reg[YDreg]= 0; + } + for (li = 0; li < 28; li++) { + mark[li] = 0; + } // init the marks +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + // how many lines in text[]? + li = count_lines(text, end - 1); + psb("\"%s\"%s" +#ifdef CONFIG_FEATURE_VI_READONLY + "%s" +#endif /* CONFIG_FEATURE_VI_READONLY */ + " %dL, %dC", cfn, + (sr < 0 ? " [New file]" : ""), +#ifdef CONFIG_FEATURE_VI_READONLY + ((vi_readonly || readonly) ? " [Read only]" : ""), +#endif /* CONFIG_FEATURE_VI_READONLY */ + li, ch); + } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this + if (b != -1 || e != -1) { + ni((Byte *) "No address allowed on this command"); + goto vc1; + } + if (strlen((char *) args) > 0) { + // user wants a new filename + free(cfn); + cfn = (Byte *) bb_xstrdup((char *) args); + } else { + // user wants file status info + edit_status(); + } + } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available + // print out values of all features + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen + clear_to_eol(); // clear the line + cookmode(); + show_help(); + rawmode(); + Hit_Return(); + } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line + if (b < 0) { // no addr given- use defaults + q = begin_line(dot); // assume .,. for the range + r = end_line(dot); + } + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen + clear_to_eol(); // clear the line + puts("\r"); + for (; q <= r; q++) { + int c_is_no_print; + + c = *q; + c_is_no_print = c > 127 && !Isprint(c); + if (c_is_no_print) { + c = '.'; + standout_start(); + } + if (c == '\n') { + write1("$\r"); + } else if (c < ' ' || c == 127) { + putchar('^'); + if(c == 127) + c = '?'; + else + c += '@'; + } + putchar(c); + if (c_is_no_print) + standout_end(); + } +#ifdef CONFIG_FEATURE_VI_SET + vc2: +#endif /* CONFIG_FEATURE_VI_SET */ + Hit_Return(); + } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit + (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file + if (useforce) { + // force end of argv list + if (*cmd == 'q') { + optind = save_argc; + } + editing = 0; + goto vc1; + } + // don't exit if the file been modified + if (file_modified) { + psbs("No write since last change (:%s! overrides)", + (*cmd == 'q' ? "quit" : "next")); + goto vc1; + } + // are there other file to edit + if (*cmd == 'q' && optind < save_argc - 1) { + psbs("%d more file to edit", (save_argc - optind - 1)); + goto vc1; + } + if (*cmd == 'n' && optind >= save_argc - 1) { + psbs("No more files to edit"); + goto vc1; + } + editing = 0; + } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[] + fn = args; + if (strlen((char *) fn) <= 0) { + psbs("No filename given"); + goto vc1; + } + if (b < 0) { // no addr given- use defaults + q = begin_line(dot); // assume "dot" + } + // read after current line- unless user said ":0r foo" + if (b != 0) + q = next_line(q); +#ifdef CONFIG_FEATURE_VI_READONLY + l= readonly; // remember current files' status +#endif + ch = file_insert(fn, q, file_size(fn)); +#ifdef CONFIG_FEATURE_VI_READONLY + readonly= l; +#endif + if (ch < 0) + goto vc1; // nothing was inserted + // how many lines in text[]? + li = count_lines(q, q + ch - 1); + psb("\"%s\"" +#ifdef CONFIG_FEATURE_VI_READONLY + "%s" +#endif /* CONFIG_FEATURE_VI_READONLY */ + " %dL, %dC", fn, +#ifdef CONFIG_FEATURE_VI_READONLY + ((vi_readonly || readonly) ? " [Read only]" : ""), +#endif /* CONFIG_FEATURE_VI_READONLY */ + li, ch); + if (ch > 0) { + // if the insert is before "dot" then we need to update + if (q <= dot) + dot += ch; + file_modified = TRUE; + } + } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args + if (file_modified && ! useforce) { + psbs("No write since last change (:rewind! overrides)"); + } else { + // reset the filenames to edit + optind = fn_start - 1; + editing = 0; + } +#ifdef CONFIG_FEATURE_VI_SET + } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features + i = 0; // offset into args + if (strlen((char *) args) == 0) { + // print out values of all options + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen + clear_to_eol(); // clear the line + printf("----------------------------------------\r\n"); +#ifdef CONFIG_FEATURE_VI_SETOPTS + if (!autoindent) + printf("no"); + printf("autoindent "); + if (!err_method) + printf("no"); + printf("flash "); + if (!ignorecase) + printf("no"); + printf("ignorecase "); + if (!showmatch) + printf("no"); + printf("showmatch "); + printf("tabstop=%d ", tabstop); +#endif /* CONFIG_FEATURE_VI_SETOPTS */ + printf("\r\n"); + goto vc2; + } + if (strncasecmp((char *) args, "no", 2) == 0) + i = 2; // ":set noautoindent" +#ifdef CONFIG_FEATURE_VI_SETOPTS + setops(args, "autoindent ", i, "ai", VI_AUTOINDENT); + setops(args, "flash ", i, "fl", VI_ERR_METHOD); + setops(args, "ignorecase ", i, "ic", VI_IGNORECASE); + setops(args, "showmatch ", i, "ic", VI_SHOWMATCH); + if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) { + sscanf(strchr((char *) args + i, '='), "=%d", &ch); + if (ch > 0 && ch < columns - 1) + tabstop = ch; + } +#endif /* CONFIG_FEATURE_VI_SETOPTS */ +#endif /* CONFIG_FEATURE_VI_SET */ +#ifdef CONFIG_FEATURE_VI_SEARCH + } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern + Byte *ls, *F, *R; + int gflag; + + // F points to the "find" pattern + // R points to the "replace" pattern + // replace the cmd line delimiters "/" with NULLs + gflag = 0; // global replace flag + c = orig_buf[1]; // what is the delimiter + F = orig_buf + 2; // start of "find" + R = (Byte *) strchr((char *) F, c); // middle delimiter + if (!R) goto colon_s_fail; + *R++ = '\0'; // terminate "find" + buf1 = (Byte *) strchr((char *) R, c); + if (!buf1) goto colon_s_fail; + *buf1++ = '\0'; // terminate "replace" + if (*buf1 == 'g') { // :s/foo/bar/g + buf1++; + gflag++; // turn on gflag + } + q = begin_line(q); + if (b < 0) { // maybe :s/foo/bar/ + q = begin_line(dot); // start with cur line + b = count_lines(text, q); // cur line number + } + if (e < 0) + e = b; // maybe :.s/foo/bar/ + for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 + ls = q; // orig line start + vc4: + buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find" + if (buf1 != NULL) { + // we found the "find" pattern- delete it + (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1); + // inset the "replace" patern + (void) string_insert(buf1, R); // insert the string + // check for "global" :s/foo/bar/g + if (gflag == 1) { + if ((buf1 + strlen((char *) R)) < end_line(ls)) { + q = buf1 + strlen((char *) R); + goto vc4; // don't let q move past cur line + } + } + } + q = next_line(ls); + } +#endif /* CONFIG_FEATURE_VI_SEARCH */ + } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version + psb("%s", vi_Version); + } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file + (strncasecmp((char *) cmd, "wq", i) == 0) || + (strncasecmp((char *) cmd, "x", i) == 0)) { + // is there a file name to write to? + if (strlen((char *) args) > 0) { + fn = args; + } +#ifdef CONFIG_FEATURE_VI_READONLY + if ((vi_readonly || readonly) && ! useforce) { + psbs("\"%s\" File is read only", fn); + goto vc3; + } +#endif /* CONFIG_FEATURE_VI_READONLY */ + // how many lines in text[]? + li = count_lines(q, r); + ch = r - q + 1; + // see if file exists- if not, its just a new file request + if (useforce) { + // if "fn" is not write-able, chmod u+w + // sprintf(syscmd, "chmod u+w %s", fn); + // system(syscmd); + forced = TRUE; + } + l = file_write(fn, q, r); + if (useforce && forced) { + // chmod u-w + // sprintf(syscmd, "chmod u-w %s", fn); + // system(syscmd); + forced = FALSE; + } + psb("\"%s\" %dL, %dC", fn, li, l); + if (q == text && r == end - 1 && l == ch) + file_modified = FALSE; + if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) { + editing = 0; + } +#ifdef CONFIG_FEATURE_VI_READONLY + vc3:; +#endif /* CONFIG_FEATURE_VI_READONLY */ +#ifdef CONFIG_FEATURE_VI_YANKMARK + } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines + if (b < 0) { // no addr given- use defaults + q = begin_line(dot); // assume .,. for the range + r = end_line(dot); + } + text_yank(q, r, YDreg); + li = count_lines(q, r); + psb("Yank %d lines (%d chars) into [%c]", + li, strlen((char *) reg[YDreg]), what_reg()); +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + } else { + // cmd unknown + ni((Byte *) cmd); + } + vc1: + dot = bound_dot(dot); // make sure "dot" is valid + return; +#ifdef CONFIG_FEATURE_VI_SEARCH +colon_s_fail: + psb(":s expression missing delimiters"); +#endif +} + +static void Hit_Return(void) +{ + char c; + + standout_start(); // start reverse video + write1("[Hit return to continue]"); + standout_end(); // end reverse video + while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */ + ; + redraw(TRUE); // force redraw all +} +#endif /* CONFIG_FEATURE_VI_COLON */ + +//----- Synchronize the cursor to Dot -------------------------- +static void sync_cursor(Byte * d, int *row, int *col) +{ + Byte *beg_cur, *end_cur; // begin and end of "d" line + Byte *beg_scr, *end_scr; // begin and end of screen + Byte *tp; + int cnt, ro, co; + + beg_cur = begin_line(d); // first char of cur line + end_cur = end_line(d); // last char of cur line + + beg_scr = end_scr = screenbegin; // first char of screen + end_scr = end_screen(); // last char of screen + + if (beg_cur < screenbegin) { + // "d" is before top line on screen + // how many lines do we have to move + cnt = count_lines(beg_cur, screenbegin); + sc1: + screenbegin = beg_cur; + if (cnt > (rows - 1) / 2) { + // we moved too many lines. put "dot" in middle of screen + for (cnt = 0; cnt < (rows - 1) / 2; cnt++) { + screenbegin = prev_line(screenbegin); + } + } + } else if (beg_cur > end_scr) { + // "d" is after bottom line on screen + // how many lines do we have to move + cnt = count_lines(end_scr, beg_cur); + if (cnt > (rows - 1) / 2) + goto sc1; // too many lines + for (ro = 0; ro < cnt - 1; ro++) { + // move screen begin the same amount + screenbegin = next_line(screenbegin); + // now, move the end of screen + end_scr = next_line(end_scr); + end_scr = end_line(end_scr); + } + } + // "d" is on screen- find out which row + tp = screenbegin; + for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row + if (tp == beg_cur) + break; + tp = next_line(tp); + } + + // find out what col "d" is on + co = 0; + do { // drive "co" to correct column + if (*tp == '\n' || *tp == '\0') + break; + if (*tp == '\t') { + // 7 - (co % 8 ) + co += ((tabstop - 1) - (co % tabstop)); + } else if (*tp < ' ' || *tp == 127) { + co++; // display as ^X, use 2 columns + } + } while (tp++ < d && ++co); + + // "co" is the column where "dot" is. + // The screen has "columns" columns. + // The currently displayed columns are 0+offset -- columns+ofset + // |-------------------------------------------------------------| + // ^ ^ ^ + // offset | |------- columns ----------------| + // + // If "co" is already in this range then we do not have to adjust offset + // but, we do have to subtract the "offset" bias from "co". + // If "co" is outside this range then we have to change "offset". + // If the first char of a line is a tab the cursor will try to stay + // in column 7, but we have to set offset to 0. + + if (co < 0 + offset) { + offset = co; + } + if (co >= columns + offset) { + offset = co - columns + 1; + } + // if the first char of the line is a tab, and "dot" is sitting on it + // force offset to 0. + if (d == beg_cur && *d == '\t') { + offset = 0; + } + co -= offset; + + *row = ro; + *col = co; +} + +//----- Text Movement Routines --------------------------------- +static Byte *begin_line(Byte * p) // return pointer to first char cur line +{ + while (p > text && p[-1] != '\n') + p--; // go to cur line B-o-l + return (p); +} + +static Byte *end_line(Byte * p) // return pointer to NL of cur line line +{ + while (p < end - 1 && *p != '\n') + p++; // go to cur line E-o-l + return (p); +} + +static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line +{ + while (p < end - 1 && *p != '\n') + p++; // go to cur line E-o-l + // Try to stay off of the Newline + if (*p == '\n' && (p - begin_line(p)) > 0) + p--; + return (p); +} + +static Byte *prev_line(Byte * p) // return pointer first char prev line +{ + p = begin_line(p); // goto begining of cur line + if (p[-1] == '\n' && p > text) + p--; // step to prev line + p = begin_line(p); // goto begining of prev line + return (p); +} + +static Byte *next_line(Byte * p) // return pointer first char next line +{ + p = end_line(p); + if (*p == '\n' && p < end - 1) + p++; // step to next line + return (p); +} + +//----- Text Information Routines ------------------------------ +static Byte *end_screen(void) +{ + Byte *q; + int cnt; + + // find new bottom line + q = screenbegin; + for (cnt = 0; cnt < rows - 2; cnt++) + q = next_line(q); + q = end_line(q); + return (q); +} + +static int count_lines(Byte * start, Byte * stop) // count line from start to stop +{ + Byte *q; + int cnt; + + if (stop < start) { // start and stop are backwards- reverse them + q = start; + start = stop; + stop = q; + } + cnt = 0; + stop = end_line(stop); // get to end of this line + for (q = start; q <= stop && q <= end - 1; q++) { + if (*q == '\n') + cnt++; + } + return (cnt); +} + +static Byte *find_line(int li) // find begining of line #li +{ + Byte *q; + + for (q = text; li > 1; li--) { + q = next_line(q); + } + return (q); +} + +//----- Dot Movement Routines ---------------------------------- +static void dot_left(void) +{ + if (dot > text && dot[-1] != '\n') + dot--; +} + +static void dot_right(void) +{ + if (dot < end - 1 && *dot != '\n') + dot++; +} + +static void dot_begin(void) +{ + dot = begin_line(dot); // return pointer to first char cur line +} + +static void dot_end(void) +{ + dot = end_line(dot); // return pointer to last char cur line +} + +static Byte *move_to_col(Byte * p, int l) +{ + int co; + + p = begin_line(p); + co = 0; + do { + if (*p == '\n' || *p == '\0') + break; + if (*p == '\t') { + // 7 - (co % 8 ) + co += ((tabstop - 1) - (co % tabstop)); + } else if (*p < ' ' || *p == 127) { + co++; // display as ^X, use 2 columns + } + } while (++co <= l && p++ < end); + return (p); +} + +static void dot_next(void) +{ + dot = next_line(dot); +} + +static void dot_prev(void) +{ + dot = prev_line(dot); +} + +static void dot_scroll(int cnt, int dir) +{ + Byte *q; + + for (; cnt > 0; cnt--) { + if (dir < 0) { + // scroll Backwards + // ctrl-Y scroll up one line + screenbegin = prev_line(screenbegin); + } else { + // scroll Forwards + // ctrl-E scroll down one line + screenbegin = next_line(screenbegin); + } + } + // make sure "dot" stays on the screen so we dont scroll off + if (dot < screenbegin) + dot = screenbegin; + q = end_screen(); // find new bottom line + if (dot > q) + dot = begin_line(q); // is dot is below bottom line? + dot_skip_over_ws(); +} + +static void dot_skip_over_ws(void) +{ + // skip WS + while (isspace(*dot) && *dot != '\n' && dot < end - 1) + dot++; +} + +static void dot_delete(void) // delete the char at 'dot' +{ + (void) text_hole_delete(dot, dot); +} + +static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end" +{ + if (p >= end && end > text) { + p = end - 1; + indicate_error('1'); + } + if (p < text) { + p = text; + indicate_error('2'); + } + return (p); +} + +//----- Helper Utility Routines -------------------------------- + +//---------------------------------------------------------------- +//----- Char Routines -------------------------------------------- +/* Chars that are part of a word- + * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz + * Chars that are Not part of a word (stoppers) + * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~ + * Chars that are WhiteSpace + * TAB NEWLINE VT FF RETURN SPACE + * DO NOT COUNT NEWLINE AS WHITESPACE + */ + +static Byte *new_screen(int ro, int co) +{ + int li; + + free(screen); + screensize = ro * co + 8; + screen = (Byte *) xmalloc(screensize); + // initialize the new screen. assume this will be a empty file. + screen_erase(); + // non-existent text[] lines start with a tilde (~). + for (li = 1; li < ro - 1; li++) { + screen[(li * co) + 0] = '~'; + } + return (screen); +} + +static Byte *new_text(int size) +{ + if (size < 10240) + size = 10240; // have a minimum size for new files + free(text); + text = (Byte *) xmalloc(size + 8); + memset(text, '\0', size); // clear new text[] + //text += 4; // leave some room for "oops" + textend = text + size - 1; + //textend -= 4; // leave some root for "oops" + return (text); +} + +#ifdef CONFIG_FEATURE_VI_SEARCH +static int mycmp(Byte * s1, Byte * s2, int len) +{ + int i; + + i = strncmp((char *) s1, (char *) s2, len); +#ifdef CONFIG_FEATURE_VI_SETOPTS + if (ignorecase) { + i = strncasecmp((char *) s1, (char *) s2, len); + } +#endif /* CONFIG_FEATURE_VI_SETOPTS */ + return (i); +} + +static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p +{ +#ifndef REGEX_SEARCH + Byte *start, *stop; + int len; + + len = strlen((char *) pat); + if (dir == FORWARD) { + stop = end - 1; // assume range is p - end-1 + if (range == LIMITED) + stop = next_line(p); // range is to next line + for (start = p; start < stop; start++) { + if (mycmp(start, pat, len) == 0) { + return (start); + } + } + } else if (dir == BACK) { + stop = text; // assume range is text - p + if (range == LIMITED) + stop = prev_line(p); // range is to prev line + for (start = p - len; start >= stop; start--) { + if (mycmp(start, pat, len) == 0) { + return (start); + } + } + } + // pattern not found + return (NULL); +#else /*REGEX_SEARCH */ + char *q; + struct re_pattern_buffer preg; + int i; + int size, range; + + re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; + preg.translate = 0; + preg.fastmap = 0; + preg.buffer = 0; + preg.allocated = 0; + + // assume a LIMITED forward search + q = next_line(p); + q = end_line(q); + q = end - 1; + if (dir == BACK) { + q = prev_line(p); + q = text; + } + // count the number of chars to search over, forward or backward + size = q - p; + if (size < 0) + size = p - q; + // RANGE could be negative if we are searching backwards + range = q - p; + + q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg); + if (q != 0) { + // The pattern was not compiled + psbs("bad search pattern: \"%s\": %s", pat, q); + i = 0; // return p if pattern not compiled + goto cs1; + } + + q = p; + if (range < 0) { + q = p - size; + if (q < text) + q = text; + } + // search for the compiled pattern, preg, in p[] + // range < 0- search backward + // range > 0- search forward + // 0 < start < size + // re_search() < 0 not found or error + // re_search() > 0 index of found pattern + // struct pattern char int int int struct reg + // re_search (*pattern_buffer, *string, size, start, range, *regs) + i = re_search(&preg, q, size, 0, range, 0); + if (i == -1) { + p = 0; + i = 0; // return NULL if pattern not found + } + cs1: + if (dir == FORWARD) { + p = p + i; + } else { + p = p - i; + } + return (p); +#endif /*REGEX_SEARCH */ +} +#endif /* CONFIG_FEATURE_VI_SEARCH */ + +static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p' +{ + if (c == 22) { // Is this an ctrl-V? + p = stupid_insert(p, '^'); // use ^ to indicate literal next + p--; // backup onto ^ + refresh(FALSE); // show the ^ + c = get_one_char(); + *p = c; + p++; + file_modified = TRUE; // has the file been modified + } else if (c == 27) { // Is this an ESC? + cmd_mode = 0; + cmdcnt = 0; + end_cmd_q(); // stop adding to q + strcpy((char *) status_buffer, " "); // clear the status buffer + if ((p[-1] != '\n') && (dot>text)) { + p--; + } + } else if (c == erase_char) { // Is this a BS + // 123456789 + if ((p[-1] != '\n') && (dot>text)) { + p--; + p = text_hole_delete(p, p); // shrink buffer 1 char +#ifdef CONFIG_FEATURE_VI_DOT_CMD + // also rmove char from last_modifying_cmd + if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) { + Byte *q; + + q = last_modifying_cmd; + q[strlen((char *) q) - 1] = '\0'; // erase BS + q[strlen((char *) q) - 1] = '\0'; // erase prev char + } +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ + } + } else { + // insert a char into text[] + Byte *sp; // "save p" + + if (c == 13) + c = '\n'; // translate \r to \n + sp = p; // remember addr of insert + p = stupid_insert(p, c); // insert the char +#ifdef CONFIG_FEATURE_VI_SETOPTS + if (showmatch && strchr(")]}", *sp) != NULL) { + showmatching(sp); + } + if (autoindent && c == '\n') { // auto indent the new line + Byte *q; + + q = prev_line(p); // use prev line as templet + for (; isblnk(*q); q++) { + p = stupid_insert(p, *q); // insert the char + } + } +#endif /* CONFIG_FEATURE_VI_SETOPTS */ + } + return (p); +} + +static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p' +{ + p = text_hole_make(p, 1); + if (p != 0) { + *p = c; + file_modified = TRUE; // has the file been modified + p++; + } + return (p); +} + +static Byte find_range(Byte ** start, Byte ** stop, Byte c) +{ + Byte *save_dot, *p, *q; + int cnt; + + save_dot = dot; + p = q = dot; + + if (strchr("cdy><", c)) { + // these cmds operate on whole lines + p = q = begin_line(p); + for (cnt = 1; cnt < cmdcnt; cnt++) { + q = next_line(q); + } + q = end_line(q); + } else if (strchr("^%$0bBeEft", c)) { + // These cmds operate on char positions + do_cmd(c); // execute movement cmd + q = dot; + } else if (strchr("wW", c)) { + do_cmd(c); // execute movement cmd + // if we are at the next word's first char + // step back one char + // but check the possibilities when it is true + if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0])) + || (ispunct(dot[-1]) && !ispunct(dot[0])) + || (isalnum(dot[-1]) && !isalnum(dot[0])))) + dot--; // move back off of next word + if (dot > text && *dot == '\n') + dot--; // stay off NL + q = dot; + } else if (strchr("H-k{", c)) { + // these operate on multi-lines backwards + q = end_line(dot); // find NL + do_cmd(c); // execute movement cmd + dot_begin(); + p = dot; + } else if (strchr("L+j}\r\n", c)) { + // these operate on multi-lines forwards + p = begin_line(dot); + do_cmd(c); // execute movement cmd + dot_end(); // find NL + q = dot; + } else { + c = 27; // error- return an ESC char + //break; + } + *start = p; + *stop = q; + if (q < p) { + *start = q; + *stop = p; + } + dot = save_dot; + return (c); +} + +static int st_test(Byte * p, int type, int dir, Byte * tested) +{ + Byte c, c0, ci; + int test, inc; + + inc = dir; + c = c0 = p[0]; + ci = p[inc]; + test = 0; + + if (type == S_BEFORE_WS) { + c = ci; + test = ((!isspace(c)) || c == '\n'); + } + if (type == S_TO_WS) { + c = c0; + test = ((!isspace(c)) || c == '\n'); + } + if (type == S_OVER_WS) { + c = c0; + test = ((isspace(c))); + } + if (type == S_END_PUNCT) { + c = ci; + test = ((ispunct(c))); + } + if (type == S_END_ALNUM) { + c = ci; + test = ((isalnum(c)) || c == '_'); + } + *tested = c; + return (test); +} + +static Byte *skip_thing(Byte * p, int linecnt, int dir, int type) +{ + Byte c; + + while (st_test(p, type, dir, &c)) { + // make sure we limit search to correct number of lines + if (c == '\n' && --linecnt < 1) + break; + if (dir >= 0 && p >= end - 1) + break; + if (dir < 0 && p <= text) + break; + p += dir; // move to next char + } + return (p); +} + +// find matching char of pair () [] {} +static Byte *find_pair(Byte * p, Byte c) +{ + Byte match, *q; + int dir, level; + + match = ')'; + level = 1; + dir = 1; // assume forward + switch (c) { + case '(': + match = ')'; + break; + case '[': + match = ']'; + break; + case '{': + match = '}'; + break; + case ')': + match = '('; + dir = -1; + break; + case ']': + match = '['; + dir = -1; + break; + case '}': + match = '{'; + dir = -1; + break; + } + for (q = p + dir; text <= q && q < end; q += dir) { + // look for match, count levels of pairs (( )) + if (*q == c) + level++; // increase pair levels + if (*q == match) + level--; // reduce pair level + if (level == 0) + break; // found matching pair + } + if (level != 0) + q = NULL; // indicate no match + return (q); +} + +#ifdef CONFIG_FEATURE_VI_SETOPTS +// show the matching char of a pair, () [] {} +static void showmatching(Byte * p) +{ + Byte *q, *save_dot; + + // we found half of a pair + q = find_pair(p, *p); // get loc of matching char + if (q == NULL) { + indicate_error('3'); // no matching char + } else { + // "q" now points to matching pair + save_dot = dot; // remember where we are + dot = q; // go to new loc + refresh(FALSE); // let the user see it + (void) mysleep(40); // give user some time + dot = save_dot; // go back to old loc + refresh(FALSE); + } +} +#endif /* CONFIG_FEATURE_VI_SETOPTS */ + +// open a hole in text[] +static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole +{ + Byte *src, *dest; + int cnt; + + if (size <= 0) + goto thm0; + src = p; + dest = p + size; + cnt = end - src; // the rest of buffer + if (memmove(dest, src, cnt) != dest) { + psbs("can't create room for new characters"); + } + memset(p, ' ', size); // clear new hole + end = end + size; // adjust the new END + file_modified = TRUE; // has the file been modified + thm0: + return (p); +} + +// close a hole in text[] +static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive +{ + Byte *src, *dest; + int cnt, hole_size; + + // move forwards, from beginning + // assume p <= q + src = q + 1; + dest = p; + if (q < p) { // they are backward- swap them + src = p + 1; + dest = q; + } + hole_size = q - p + 1; + cnt = end - src; + if (src < text || src > end) + goto thd0; + if (dest < text || dest >= end) + goto thd0; + if (src >= end) + goto thd_atend; // just delete the end of the buffer + if (memmove(dest, src, cnt) != dest) { + psbs("can't delete the character"); + } + thd_atend: + end = end - hole_size; // adjust the new END + if (dest >= end) + dest = end - 1; // make sure dest in below end-1 + if (end <= text) + dest = end = text; // keep pointers valid + file_modified = TRUE; // has the file been modified + thd0: + return (dest); +} + +// copy text into register, then delete text. +// if dist <= 0, do not include, or go past, a NewLine +// +static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf) +{ + Byte *p; + + // make sure start <= stop + if (start > stop) { + // they are backwards, reverse them + p = start; + start = stop; + stop = p; + } + if (dist <= 0) { + // we can not cross NL boundaries + p = start; + if (*p == '\n') + return (p); + // dont go past a NewLine + for (; p + 1 <= stop; p++) { + if (p[1] == '\n') { + stop = p; // "stop" just before NewLine + break; + } + } + } + p = start; +#ifdef CONFIG_FEATURE_VI_YANKMARK + text_yank(start, stop, YDreg); +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + if (yf == YANKDEL) { + p = text_hole_delete(start, stop); + } // delete lines + return (p); +} + +static void show_help(void) +{ + puts("These features are available:" +#ifdef CONFIG_FEATURE_VI_SEARCH + "\n\tPattern searches with / and ?" +#endif /* CONFIG_FEATURE_VI_SEARCH */ +#ifdef CONFIG_FEATURE_VI_DOT_CMD + "\n\tLast command repeat with \'.\'" +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ +#ifdef CONFIG_FEATURE_VI_YANKMARK + "\n\tLine marking with 'x" + "\n\tNamed buffers with \"x" +#endif /* CONFIG_FEATURE_VI_YANKMARK */ +#ifdef CONFIG_FEATURE_VI_READONLY + "\n\tReadonly if vi is called as \"view\"" + "\n\tReadonly with -R command line arg" +#endif /* CONFIG_FEATURE_VI_READONLY */ +#ifdef CONFIG_FEATURE_VI_SET + "\n\tSome colon mode commands with \':\'" +#endif /* CONFIG_FEATURE_VI_SET */ +#ifdef CONFIG_FEATURE_VI_SETOPTS + "\n\tSettable options with \":set\"" +#endif /* CONFIG_FEATURE_VI_SETOPTS */ +#ifdef CONFIG_FEATURE_VI_USE_SIGNALS + "\n\tSignal catching- ^C" + "\n\tJob suspend and resume with ^Z" +#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ +#ifdef CONFIG_FEATURE_VI_WIN_RESIZE + "\n\tAdapt to window re-sizes" +#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */ + ); +} + +static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable +{ + Byte c, b[2]; + + b[1] = '\0'; + strcpy((char *) buf, ""); // init buf + if (strlen((char *) s) <= 0) + s = (Byte *) "(NULL)"; + for (; *s > '\0'; s++) { + int c_is_no_print; + + c = *s; + c_is_no_print = c > 127 && !Isprint(c); + if (c_is_no_print) { + strcat((char *) buf, SOn); + c = '.'; + } + if (c < ' ' || c == 127) { + strcat((char *) buf, "^"); + if(c == 127) + c = '?'; + else + c += '@'; + } + b[0] = c; + strcat((char *) buf, (char *) b); + if (c_is_no_print) + strcat((char *) buf, SOs); + if (*s == '\n') { + strcat((char *) buf, "$"); + } + } +} + +#ifdef CONFIG_FEATURE_VI_DOT_CMD +static void start_new_cmd_q(Byte c) +{ + // release old cmd + free(last_modifying_cmd); + // get buffer for new cmd + last_modifying_cmd = (Byte *) xmalloc(BUFSIZ); + memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue + // if there is a current cmd count put it in the buffer first + if (cmdcnt > 0) + sprintf((char *) last_modifying_cmd, "%d", cmdcnt); + // save char c onto queue + last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c; + adding2q = 1; + return; +} + +static void end_cmd_q(void) +{ +#ifdef CONFIG_FEATURE_VI_YANKMARK + YDreg = 26; // go back to default Yank/Delete reg +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + adding2q = 0; + return; +} +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ + +#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME) +static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p' +{ + int cnt, i; + + i = strlen((char *) s); + p = text_hole_make(p, i); + strncpy((char *) p, (char *) s, i); + for (cnt = 0; *s != '\0'; s++) { + if (*s == '\n') + cnt++; + } +#ifdef CONFIG_FEATURE_VI_YANKMARK + psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg()); +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + return (p); +} +#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */ + +#ifdef CONFIG_FEATURE_VI_YANKMARK +static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register +{ + Byte *t; + int cnt; + + if (q < p) { // they are backwards- reverse them + t = q; + q = p; + p = t; + } + cnt = q - p + 1; + t = reg[dest]; + free(t); // if already a yank register, free it + t = (Byte *) xmalloc(cnt + 1); // get a new register + memset(t, '\0', cnt + 1); // clear new text[] + strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer + reg[dest] = t; + return (p); +} + +static Byte what_reg(void) +{ + Byte c; + int i; + + i = 0; + c = 'D'; // default to D-reg + if (0 <= YDreg && YDreg <= 25) + c = 'a' + (Byte) YDreg; + if (YDreg == 26) + c = 'D'; + if (YDreg == 27) + c = 'U'; + return (c); +} + +static void check_context(Byte cmd) +{ + // A context is defined to be "modifying text" + // Any modifying command establishes a new context. + + if (dot < context_start || dot > context_end) { + if (strchr((char *) modifying_cmds, cmd) != NULL) { + // we are trying to modify text[]- make this the current context + mark[27] = mark[26]; // move cur to prev + mark[26] = dot; // move local to cur + context_start = prev_line(prev_line(dot)); + context_end = next_line(next_line(dot)); + //loiter= start_loiter= now; + } + } + return; +} + +static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context +{ + Byte *tmp; + + // the current context is in mark[26] + // the previous context is in mark[27] + // only swap context if other context is valid + if (text <= mark[27] && mark[27] <= end - 1) { + tmp = mark[27]; + mark[27] = mark[26]; + mark[26] = tmp; + p = mark[26]; // where we are going- previous context + context_start = prev_line(prev_line(prev_line(p))); + context_end = next_line(next_line(next_line(p))); + } + return (p); +} +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + +static int isblnk(Byte c) // is the char a blank or tab +{ + return (c == ' ' || c == '\t'); +} + +//----- Set terminal attributes -------------------------------- +static void rawmode(void) +{ + tcgetattr(0, &term_orig); + term_vi = term_orig; + term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's + term_vi.c_iflag &= (~IXON & ~ICRNL); + term_vi.c_oflag &= (~ONLCR); + term_vi.c_cc[VMIN] = 1; + term_vi.c_cc[VTIME] = 0; + erase_char = term_vi.c_cc[VERASE]; + tcsetattr(0, TCSANOW, &term_vi); +} + +static void cookmode(void) +{ + fflush(stdout); + tcsetattr(0, TCSANOW, &term_orig); +} + +//----- Come here when we get a window resize signal --------- +#ifdef CONFIG_FEATURE_VI_USE_SIGNALS +static void winch_sig(int sig) +{ + signal(SIGWINCH, winch_sig); +#ifdef CONFIG_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */ + new_screen(rows, columns); // get memory for virtual screen + redraw(TRUE); // re-draw the screen +} + +//----- Come here when we get a continue signal ------------------- +static void cont_sig(int sig) +{ + rawmode(); // terminal to "raw" + *status_buffer = '\0'; // clear the status buffer + redraw(TRUE); // re-draw the screen + + signal(SIGTSTP, suspend_sig); + signal(SIGCONT, SIG_DFL); + kill(my_pid, SIGCONT); +} + +//----- Come here when we get a Suspend signal ------------------- +static void suspend_sig(int sig) +{ + place_cursor(rows - 1, 0, FALSE); // go to bottom of screen + clear_to_eol(); // Erase to end of line + cookmode(); // terminal to "cooked" + + signal(SIGCONT, cont_sig); + signal(SIGTSTP, SIG_DFL); + kill(my_pid, SIGTSTP); +} + +//----- Come here when we get a signal --------------------------- +static void catch_sig(int sig) +{ + signal(SIGHUP, catch_sig); + signal(SIGINT, catch_sig); + signal(SIGTERM, catch_sig); + signal(SIGALRM, catch_sig); + if(sig) + longjmp(restart, sig); +} + +//----- Come here when we get a core dump signal ----------------- +static void core_sig(int sig) +{ + signal(SIGQUIT, core_sig); + signal(SIGILL, core_sig); + signal(SIGTRAP, core_sig); + signal(SIGIOT, core_sig); + signal(SIGABRT, core_sig); + signal(SIGFPE, core_sig); + signal(SIGBUS, core_sig); + signal(SIGSEGV, core_sig); +#ifdef SIGSYS + signal(SIGSYS, core_sig); +#endif + + if(sig) { // signaled + dot = bound_dot(dot); // make sure "dot" is valid + + longjmp(restart, sig); + } +} +#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ + +static int mysleep(int hund) // sleep for 'h' 1/100 seconds +{ + // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000 + fflush(stdout); + FD_ZERO(&rfds); + FD_SET(0, &rfds); + tv.tv_sec = 0; + tv.tv_usec = hund * 10000; + select(1, &rfds, NULL, NULL, &tv); + return (FD_ISSET(0, &rfds)); +} + +static Byte readbuffer[BUFSIZ]; +static int readed_for_parse; + +//----- IO Routines -------------------------------------------- +static Byte readit(void) // read (maybe cursor) key from stdin +{ + Byte c; + int n; + struct esc_cmds { + const char *seq; + Byte val; + }; + + static const struct esc_cmds esccmds[] = { + {"OA", (Byte) VI_K_UP}, // cursor key Up + {"OB", (Byte) VI_K_DOWN}, // cursor key Down + {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right + {"OD", (Byte) VI_K_LEFT}, // cursor key Left + {"OH", (Byte) VI_K_HOME}, // Cursor Key Home + {"OF", (Byte) VI_K_END}, // Cursor Key End + {"[A", (Byte) VI_K_UP}, // cursor key Up + {"[B", (Byte) VI_K_DOWN}, // cursor key Down + {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right + {"[D", (Byte) VI_K_LEFT}, // cursor key Left + {"[H", (Byte) VI_K_HOME}, // Cursor Key Home + {"[F", (Byte) VI_K_END}, // Cursor Key End + {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home + {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert + {"[4~", (Byte) VI_K_END}, // Cursor Key End + {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up + {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down + {"OP", (Byte) VI_K_FUN1}, // Function Key F1 + {"OQ", (Byte) VI_K_FUN2}, // Function Key F2 + {"OR", (Byte) VI_K_FUN3}, // Function Key F3 + {"OS", (Byte) VI_K_FUN4}, // Function Key F4 + {"[15~", (Byte) VI_K_FUN5}, // Function Key F5 + {"[17~", (Byte) VI_K_FUN6}, // Function Key F6 + {"[18~", (Byte) VI_K_FUN7}, // Function Key F7 + {"[19~", (Byte) VI_K_FUN8}, // Function Key F8 + {"[20~", (Byte) VI_K_FUN9}, // Function Key F9 + {"[21~", (Byte) VI_K_FUN10}, // Function Key F10 + {"[23~", (Byte) VI_K_FUN11}, // Function Key F11 + {"[24~", (Byte) VI_K_FUN12}, // Function Key F12 + {"[11~", (Byte) VI_K_FUN1}, // Function Key F1 + {"[12~", (Byte) VI_K_FUN2}, // Function Key F2 + {"[13~", (Byte) VI_K_FUN3}, // Function Key F3 + {"[14~", (Byte) VI_K_FUN4}, // Function Key F4 + }; + +#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds)) + + (void) alarm(0); // turn alarm OFF while we wait for input + fflush(stdout); + n = readed_for_parse; + // get input from User- are there already input chars in Q? + if (n <= 0) { + ri0: + // the Q is empty, wait for a typed char + n = read(0, readbuffer, BUFSIZ - 1); + if (n < 0) { + if (errno == EINTR) + goto ri0; // interrupted sys call + if (errno == EBADF) + editing = 0; + if (errno == EFAULT) + editing = 0; + if (errno == EINVAL) + editing = 0; + if (errno == EIO) + editing = 0; + errno = 0; + } + if(n <= 0) + return 0; // error + if (readbuffer[0] == 27) { + // This is an ESC char. Is this Esc sequence? + // Could be bare Esc key. See if there are any + // more chars to read after the ESC. This would + // be a Function or Cursor Key sequence. + FD_ZERO(&rfds); + FD_SET(0, &rfds); + tv.tv_sec = 0; + tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000 + + // keep reading while there are input chars and room in buffer + while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) { + // read the rest of the ESC string + int r = read(0, (void *) (readbuffer + n), BUFSIZ - n); + if (r > 0) { + n += r; + } + } + } + readed_for_parse = n; + } + c = readbuffer[0]; + if(c == 27 && n > 1) { + // Maybe cursor or function key? + const struct esc_cmds *eindex; + + for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) { + int cnt = strlen(eindex->seq); + + if(n <= cnt) + continue; + if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt)) + continue; + // is a Cursor key- put derived value back into Q + c = eindex->val; + // for squeeze out the ESC sequence + n = cnt + 1; + break; + } + if(eindex == &esccmds[ESCCMDS_COUNT]) { + /* defined ESC sequence not found, set only one ESC */ + n = 1; + } + } else { + n = 1; + } + // remove key sequence from Q + readed_for_parse -= n; + memmove(readbuffer, readbuffer + n, BUFSIZ - n); + (void) alarm(3); // we are done waiting for input, turn alarm ON + return (c); +} + +//----- IO Routines -------------------------------------------- +static Byte get_one_char() +{ + static Byte c; + +#ifdef CONFIG_FEATURE_VI_DOT_CMD + // ! adding2q && ioq == 0 read() + // ! adding2q && ioq != 0 *ioq + // adding2q *last_modifying_cmd= read() + if (!adding2q) { + // we are not adding to the q. + // but, we may be reading from a q + if (ioq == 0) { + // there is no current q, read from STDIN + c = readit(); // get the users input + } else { + // there is a queue to get chars from first + c = *ioq++; + if (c == '\0') { + // the end of the q, read from STDIN + free(ioq_start); + ioq_start = ioq = 0; + c = readit(); // get the users input + } + } + } else { + // adding STDIN chars to q + c = readit(); // get the users input + if (last_modifying_cmd != 0) { + int len = strlen((char *) last_modifying_cmd); + if (len + 1 >= BUFSIZ) { + psbs("last_modifying_cmd overrun"); + } else { + // add new char to q + last_modifying_cmd[len] = c; + } + } + } +#else /* CONFIG_FEATURE_VI_DOT_CMD */ + c = readit(); // get the users input +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ + return (c); // return the char, where ever it came from +} + +static Byte *get_input_line(Byte * prompt) // get input line- use "status line" +{ + Byte buf[BUFSIZ]; + Byte c; + int i; + static Byte *obufp = NULL; + + strcpy((char *) buf, (char *) prompt); + *status_buffer = '\0'; // clear the status buffer + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen + clear_to_eol(); // clear the line + write1(prompt); // write out the :, /, or ? prompt + + for (i = strlen((char *) buf); i < BUFSIZ;) { + c = get_one_char(); // read user input + if (c == '\n' || c == '\r' || c == 27) + break; // is this end of input + if (c == erase_char) { // user wants to erase prev char + i--; // backup to prev char + buf[i] = '\0'; // erase the char + buf[i + 1] = '\0'; // null terminate buffer + write1("\b \b"); // erase char on screen + if (i <= 0) { // user backs up before b-o-l, exit + break; + } + } else { + buf[i] = c; // save char in buffer + buf[i + 1] = '\0'; // make sure buffer is null terminated + putchar(c); // echo the char back to user + i++; + } + } + refresh(FALSE); + free(obufp); + obufp = (Byte *) bb_xstrdup((char *) buf); + return (obufp); +} + +static int file_size(const Byte * fn) // what is the byte size of "fn" +{ + struct stat st_buf; + int cnt, sr; + + if (fn == 0 || strlen(fn) <= 0) + return (-1); + cnt = -1; + sr = stat((char *) fn, &st_buf); // see if file exists + if (sr >= 0) { + cnt = (int) st_buf.st_size; + } + return (cnt); +} + +static int file_insert(Byte * fn, Byte * p, int size) +{ + int fd, cnt; + + cnt = -1; +#ifdef CONFIG_FEATURE_VI_READONLY + readonly = FALSE; +#endif /* CONFIG_FEATURE_VI_READONLY */ + if (fn == 0 || strlen((char*) fn) <= 0) { + psbs("No filename given"); + goto fi0; + } + if (size == 0) { + // OK- this is just a no-op + cnt = 0; + goto fi0; + } + if (size < 0) { + psbs("Trying to insert a negative number (%d) of characters", size); + goto fi0; + } + if (p < text || p > end) { + psbs("Trying to insert file outside of memory"); + goto fi0; + } + + // see if we can open the file +#ifdef CONFIG_FEATURE_VI_READONLY + if (vi_readonly) goto fi1; // do not try write-mode +#endif + fd = open((char *) fn, O_RDWR); // assume read & write + if (fd < 0) { + // could not open for writing- maybe file is read only +#ifdef CONFIG_FEATURE_VI_READONLY + fi1: +#endif + fd = open((char *) fn, O_RDONLY); // try read-only + if (fd < 0) { + psbs("\"%s\" %s", fn, "could not open file"); + goto fi0; + } +#ifdef CONFIG_FEATURE_VI_READONLY + // got the file- read-only + readonly = TRUE; +#endif /* CONFIG_FEATURE_VI_READONLY */ + } + p = text_hole_make(p, size); + cnt = read(fd, p, size); + close(fd); + if (cnt < 0) { + cnt = -1; + p = text_hole_delete(p, p + size - 1); // un-do buffer insert + psbs("could not read file \"%s\"", fn); + } else if (cnt < size) { + // There was a partial read, shrink unused space text[] + p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert + psbs("could not read all of file \"%s\"", fn); + } + if (cnt >= size) + file_modified = TRUE; + fi0: + return (cnt); +} + +static int file_write(Byte * fn, Byte * first, Byte * last) +{ + int fd, cnt, charcnt; + + if (fn == 0) { + psbs("No current filename"); + return (-1); + } + charcnt = 0; + // FIXIT- use the correct umask() + fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664); + if (fd < 0) + return (-1); + cnt = last - first + 1; + charcnt = write(fd, first, cnt); + if (charcnt == cnt) { + // good write + //file_modified= FALSE; // the file has not been modified + } else { + charcnt = 0; + } + close(fd); + return (charcnt); +} + +//----- Terminal Drawing --------------------------------------- +// The terminal is made up of 'rows' line of 'columns' columns. +// classically this would be 24 x 80. +// screen coordinates +// 0,0 ... 0,79 +// 1,0 ... 1,79 +// . ... . +// . ... . +// 22,0 ... 22,79 +// 23,0 ... 23,79 status line +// + +//----- Move the cursor to row x col (count from 0, not 1) ------- +static void place_cursor(int row, int col, int opti) +{ + char cm1[BUFSIZ]; + char *cm; +#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR + char cm2[BUFSIZ]; + Byte *screenp; + // char cm3[BUFSIZ]; + int Rrow= last_row; +#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */ + + memset(cm1, '\0', BUFSIZ - 1); // clear the buffer + + if (row < 0) row = 0; + if (row >= rows) row = rows - 1; + if (col < 0) col = 0; + if (col >= columns) col = columns - 1; + + //----- 1. Try the standard terminal ESC sequence + sprintf((char *) cm1, CMrc, row + 1, col + 1); + cm= cm1; + if (! opti) goto pc0; + +#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR + //----- find the minimum # of chars to move cursor ------------- + //----- 2. Try moving with discreet chars (Newline, [back]space, ...) + memset(cm2, '\0', BUFSIZ - 1); // clear the buffer + + // move to the correct row + while (row < Rrow) { + // the cursor has to move up + strcat(cm2, CMup); + Rrow--; + } + while (row > Rrow) { + // the cursor has to move down + strcat(cm2, CMdown); + Rrow++; + } + + // now move to the correct column + strcat(cm2, "\r"); // start at col 0 + // just send out orignal source char to get to correct place + screenp = &screen[row * columns]; // start of screen line + strncat(cm2, screenp, col); + + //----- 3. Try some other way of moving cursor + //--------------------------------------------- + + // pick the shortest cursor motion to send out + cm= cm1; + if (strlen(cm2) < strlen(cm)) { + cm= cm2; + } /* else if (strlen(cm3) < strlen(cm)) { + cm= cm3; + } */ +#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */ + pc0: + write1(cm); // move the cursor +} + +//----- Erase from cursor to end of line ----------------------- +static void clear_to_eol() +{ + write1(Ceol); // Erase from cursor to end of line +} + +//----- Erase from cursor to end of screen ----------------------- +static void clear_to_eos() +{ + write1(Ceos); // Erase from cursor to end of screen +} + +//----- Start standout mode ------------------------------------ +static void standout_start() // send "start reverse video" sequence +{ + write1(SOs); // Start reverse video mode +} + +//----- End standout mode -------------------------------------- +static void standout_end() // send "end reverse video" sequence +{ + write1(SOn); // End reverse video mode +} + +//----- Flash the screen -------------------------------------- +static void flash(int h) +{ + standout_start(); // send "start reverse video" sequence + redraw(TRUE); + (void) mysleep(h); + standout_end(); // send "end reverse video" sequence + redraw(TRUE); +} + +static void Indicate_Error(void) +{ +#ifdef CONFIG_FEATURE_VI_CRASHME + if (crashme > 0) + return; // generate a random command +#endif /* CONFIG_FEATURE_VI_CRASHME */ + if (!err_method) { + write1(bell); // send out a bell character + } else { + flash(10); + } +} + +//----- Screen[] Routines -------------------------------------- +//----- Erase the Screen[] memory ------------------------------ +static void screen_erase() +{ + memset(screen, ' ', screensize); // clear new screen +} + +//----- Draw the status line at bottom of the screen ------------- +static void show_status_line(void) +{ + static int last_cksum; + int l, cnt, cksum; + + edit_status(); + cnt = strlen((char *) status_buffer); + for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); } + // don't write the status line unless it changes + if (cnt > 0 && last_cksum != cksum) { + last_cksum= cksum; // remember if we have seen this line + place_cursor(rows - 1, 0, FALSE); // put cursor on status line + write1(status_buffer); + clear_to_eol(); + place_cursor(crow, ccol, FALSE); // put cursor back in correct place + } + fflush(stdout); +} + +//----- format the status buffer, the bottom line of screen ------ +// print status buffer, with STANDOUT mode +static void psbs(const char *format, ...) +{ + va_list args; + + va_start(args, format); + strcpy((char *) status_buffer, SOs); // Terminal standout mode on + vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, + args); + strcat((char *) status_buffer, SOn); // Terminal standout mode off + va_end(args); + + return; +} + +// print status buffer +static void psb(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vsprintf((char *) status_buffer, format, args); + va_end(args); + return; +} + +static void ni(Byte * s) // display messages +{ + Byte buf[BUFSIZ]; + + print_literal(buf, s); + psbs("\'%s\' is not implemented", buf); +} + +static void edit_status(void) // show file status on status line +{ + int cur, tot, percent; + + cur = count_lines(text, dot); + tot = count_lines(text, end - 1); + // current line percent + // ------------- ~~ ---------- + // total lines 100 + if (tot > 0) { + percent = (100 * cur) / tot; + } else { + cur = tot = 0; + percent = 100; + } + psb("\"%s\"" +#ifdef CONFIG_FEATURE_VI_READONLY + "%s" +#endif /* CONFIG_FEATURE_VI_READONLY */ + "%s line %d of %d --%d%%--", + (cfn != 0 ? (char *) cfn : "No file"), +#ifdef CONFIG_FEATURE_VI_READONLY + ((vi_readonly || readonly) ? " [Read only]" : ""), +#endif /* CONFIG_FEATURE_VI_READONLY */ + (file_modified ? " [modified]" : ""), + cur, tot, percent); +} + +//----- Force refresh of all Lines ----------------------------- +static void redraw(int full_screen) +{ + place_cursor(0, 0, FALSE); // put cursor in correct place + clear_to_eos(); // tel terminal to erase display + screen_erase(); // erase the internal screen buffer + refresh(full_screen); // this will redraw the entire display +} + +//----- Format a text[] line into a buffer --------------------- +static void format_line(Byte *dest, Byte *src, int li) +{ + int co; + Byte c; + + for (co= 0; co < MAX_SCR_COLS; co++) { + c= ' '; // assume blank + if (li > 0 && co == 0) { + c = '~'; // not first line, assume Tilde + } + // are there chars in text[] and have we gone past the end + if (text < end && src < end) { + c = *src++; + } + if (c == '\n') + break; + if (c > 127 && !Isprint(c)) { + c = '.'; + } + if (c < ' ' || c == 127) { + if (c == '\t') { + c = ' '; + // co % 8 != 7 + for (; (co % tabstop) != (tabstop - 1); co++) { + dest[co] = c; + } + } else { + dest[co++] = '^'; + if(c == 127) + c = '?'; + else + c += '@'; // make it visible + } + } + // the co++ is done here so that the column will + // not be overwritten when we blank-out the rest of line + dest[co] = c; + if (src >= end) + break; + } +} + +//----- Refresh the changed screen lines ----------------------- +// Copy the source line from text[] into the buffer and note +// if the current screenline is different from the new buffer. +// If they differ then that line needs redrawing on the terminal. +// +static void refresh(int full_screen) +{ + static int old_offset; + int li, changed; + Byte buf[MAX_SCR_COLS]; + Byte *tp, *sp; // pointer into text[] and screen[] +#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR + int last_li= -2; // last line that changed- for optimizing cursor movement +#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */ + +#ifdef CONFIG_FEATURE_VI_WIN_RESIZE + window_size_get(0); +#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */ + sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot") + tp = screenbegin; // index into text[] of top line + + // compare text[] to screen[] and mark screen[] lines that need updating + for (li = 0; li < rows - 1; li++) { + int cs, ce; // column start & end + memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer + buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer + // format current text line into buf + format_line(buf, tp, li); + + // skip to the end of the current text[] line + while (tp < end && *tp++ != '\n') /*no-op*/ ; + + // see if there are any changes between vitual screen and buf + changed = FALSE; // assume no change + cs= 0; + ce= columns-1; + sp = &screen[li * columns]; // start of screen line + if (full_screen) { + // force re-draw of every single column from 0 - columns-1 + goto re0; + } + // compare newly formatted buffer with virtual screen + // look forward for first difference between buf and screen + for ( ; cs <= ce; cs++) { + if (buf[cs + offset] != sp[cs]) { + changed = TRUE; // mark for redraw + break; + } + } + + // look backward for last difference between buf and screen + for ( ; ce >= cs; ce--) { + if (buf[ce + offset] != sp[ce]) { + changed = TRUE; // mark for redraw + break; + } + } + // now, cs is index of first diff, and ce is index of last diff + + // if horz offset has changed, force a redraw + if (offset != old_offset) { + re0: + changed = TRUE; + } + + // make a sanity check of columns indexes + if (cs < 0) cs= 0; + if (ce > columns-1) ce= columns-1; + if (cs > ce) { cs= 0; ce= columns-1; } + // is there a change between vitual screen and buf + if (changed) { + // copy changed part of buffer to virtual screen + memmove(sp+cs, buf+(cs+offset), ce-cs+1); + + // move cursor to column of first change + if (offset != old_offset) { + // opti_cur_move is still too stupid + // to handle offsets correctly + place_cursor(li, cs, FALSE); + } else { +#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR + // if this just the next line + // try to optimize cursor movement + // otherwise, use standard ESC sequence + place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE); + last_li= li; +#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */ + place_cursor(li, cs, FALSE); // use standard ESC sequence +#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */ + } + + // write line out to terminal + { + int nic = ce-cs+1; + char *out = sp+cs; + + while(nic-- > 0) { + putchar(*out); + out++; + } + } +#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR + last_row = li; +#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */ + } + } + +#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR + place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE); + last_row = crow; +#else + place_cursor(crow, ccol, FALSE); +#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */ + + if (offset != old_offset) + old_offset = offset; +} + +//--------------------------------------------------------------------- +//----- the Ascii Chart ----------------------------------------------- +// +// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel +// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si +// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb +// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us +// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 ' +// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f / +// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7 +// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ? +// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G +// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O +// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W +// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _ +// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g +// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o +// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w +// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del +//--------------------------------------------------------------------- + +//----- Execute a Vi Command ----------------------------------- +static void do_cmd(Byte c) +{ + Byte c1, *p, *q, *msg, buf[9], *save_dot; + int cnt, i, j, dir, yf; + + c1 = c; // quiet the compiler + cnt = yf = dir = 0; // quiet the compiler + p = q = save_dot = msg = buf; // quiet the compiler + memset(buf, '\0', 9); // clear buf + + /* if this is a cursor key, skip these checks */ + switch (c) { + case VI_K_UP: + case VI_K_DOWN: + case VI_K_LEFT: + case VI_K_RIGHT: + case VI_K_HOME: + case VI_K_END: + case VI_K_PAGEUP: + case VI_K_PAGEDOWN: + goto key_cmd_mode; + } + + if (cmd_mode == 2) { + // flip-flop Insert/Replace mode + if (c == VI_K_INSERT) goto dc_i; + // we are 'R'eplacing the current *dot with new char + if (*dot == '\n') { + // don't Replace past E-o-l + cmd_mode = 1; // convert to insert + } else { + if (1 <= c || Isprint(c)) { + if (c != 27) + dot = yank_delete(dot, dot, 0, YANKDEL); // delete char + dot = char_insert(dot, c); // insert new char + } + goto dc1; + } + } + if (cmd_mode == 1) { + // hitting "Insert" twice means "R" replace mode + if (c == VI_K_INSERT) goto dc5; + // insert the char c at "dot" + if (1 <= c || Isprint(c)) { + dot = char_insert(dot, c); + } + goto dc1; + } + +key_cmd_mode: + switch (c) { + //case 0x01: // soh + //case 0x09: // ht + //case 0x0b: // vt + //case 0x0e: // so + //case 0x0f: // si + //case 0x10: // dle + //case 0x11: // dc1 + //case 0x13: // dc3 +#ifdef CONFIG_FEATURE_VI_CRASHME + case 0x14: // dc4 ctrl-T + crashme = (crashme == 0) ? 1 : 0; + break; +#endif /* CONFIG_FEATURE_VI_CRASHME */ + //case 0x16: // syn + //case 0x17: // etb + //case 0x18: // can + //case 0x1c: // fs + //case 0x1d: // gs + //case 0x1e: // rs + //case 0x1f: // us + //case '!': // !- + //case '#': // #- + //case '&': // &- + //case '(': // (- + //case ')': // )- + //case '*': // *- + //case ',': // ,- + //case '=': // =- + //case '@': // @- + //case 'F': // F- + //case 'K': // K- + //case 'Q': // Q- + //case 'S': // S- + //case 'T': // T- + //case 'V': // V- + //case '[': // [- + //case '\\': // \- + //case ']': // ]- + //case '_': // _- + //case '`': // `- + //case 'g': // g- + //case 'u': // u- FIXME- there is no undo + //case 'v': // v- + default: // unrecognised command + buf[0] = c; + buf[1] = '\0'; + if (c < ' ') { + buf[0] = '^'; + buf[1] = c + '@'; + buf[2] = '\0'; + } + ni((Byte *) buf); + end_cmd_q(); // stop adding to q + case 0x00: // nul- ignore + break; + case 2: // ctrl-B scroll up full screen + case VI_K_PAGEUP: // Cursor Key Page Up + dot_scroll(rows - 2, -1); + break; +#ifdef CONFIG_FEATURE_VI_USE_SIGNALS + case 0x03: // ctrl-C interrupt + longjmp(restart, 1); + break; + case 26: // ctrl-Z suspend + suspend_sig(SIGTSTP); + break; +#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ + case 4: // ctrl-D scroll down half screen + dot_scroll((rows - 2) / 2, 1); + break; + case 5: // ctrl-E scroll down one line + dot_scroll(1, 1); + break; + case 6: // ctrl-F scroll down full screen + case VI_K_PAGEDOWN: // Cursor Key Page Down + dot_scroll(rows - 2, 1); + break; + case 7: // ctrl-G show current status + edit_status(); + break; + case 'h': // h- move left + case VI_K_LEFT: // cursor key Left + case 8: // ctrl-H- move left (This may be ERASE char) + case 127: // DEL- move left (This may be ERASE char) + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_left(); + break; + case 10: // Newline ^J + case 'j': // j- goto next line, same col + case VI_K_DOWN: // cursor key Down + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_next(); // go to next B-o-l + dot = move_to_col(dot, ccol + offset); // try stay in same col + break; + case 12: // ctrl-L force redraw whole screen + case 18: // ctrl-R force redraw + place_cursor(0, 0, FALSE); // put cursor in correct place + clear_to_eos(); // tel terminal to erase display + (void) mysleep(10); + screen_erase(); // erase the internal screen buffer + refresh(TRUE); // this will redraw the entire display + break; + case 13: // Carriage Return ^M + case '+': // +- goto next line + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_next(); + dot_skip_over_ws(); + break; + case 21: // ctrl-U scroll up half screen + dot_scroll((rows - 2) / 2, -1); + break; + case 25: // ctrl-Y scroll up one line + dot_scroll(1, -1); + break; + case 27: // esc + if (cmd_mode == 0) + indicate_error(c); + cmd_mode = 0; // stop insrting + end_cmd_q(); + *status_buffer = '\0'; // clear status buffer + break; + case ' ': // move right + case 'l': // move right + case VI_K_RIGHT: // Cursor Key Right + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_right(); + break; +#ifdef CONFIG_FEATURE_VI_YANKMARK + case '"': // "- name a register to use for Delete/Yank + c1 = get_one_char(); + c1 = tolower(c1); + if (islower(c1)) { + YDreg = c1 - 'a'; + } else { + indicate_error(c); + } + break; + case '\'': // '- goto a specific mark + c1 = get_one_char(); + c1 = tolower(c1); + if (islower(c1)) { + c1 = c1 - 'a'; + // get the b-o-l + q = mark[(int) c1]; + if (text <= q && q < end) { + dot = q; + dot_begin(); // go to B-o-l + dot_skip_over_ws(); + } + } else if (c1 == '\'') { // goto previous context + dot = swap_context(dot); // swap current and previous context + dot_begin(); // go to B-o-l + dot_skip_over_ws(); + } else { + indicate_error(c); + } + break; + case 'm': // m- Mark a line + // this is really stupid. If there are any inserts or deletes + // between text[0] and dot then this mark will not point to the + // correct location! It could be off by many lines! + // Well..., at least its quick and dirty. + c1 = get_one_char(); + c1 = tolower(c1); + if (islower(c1)) { + c1 = c1 - 'a'; + // remember the line + mark[(int) c1] = dot; + } else { + indicate_error(c); + } + break; + case 'P': // P- Put register before + case 'p': // p- put register after + p = reg[YDreg]; + if (p == 0) { + psbs("Nothing in register %c", what_reg()); + break; + } + // are we putting whole lines or strings + if (strchr((char *) p, '\n') != NULL) { + if (c == 'P') { + dot_begin(); // putting lines- Put above + } + if (c == 'p') { + // are we putting after very last line? + if (end_line(dot) == (end - 1)) { + dot = end; // force dot to end of text[] + } else { + dot_next(); // next line, then put before + } + } + } else { + if (c == 'p') + dot_right(); // move to right, can move to NL + } + dot = string_insert(dot, p); // insert the string + end_cmd_q(); // stop adding to q + break; + case 'U': // U- Undo; replace current line with original version + if (reg[Ureg] != 0) { + p = begin_line(dot); + q = end_line(dot); + p = text_hole_delete(p, q); // delete cur line + p = string_insert(p, reg[Ureg]); // insert orig line + dot = p; + dot_skip_over_ws(); + } + break; +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + case '$': // $- goto end of line + case VI_K_END: // Cursor Key End + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot = end_line(dot); + break; + case '%': // %- find matching char of pair () [] {} + for (q = dot; q < end && *q != '\n'; q++) { + if (strchr("()[]{}", *q) != NULL) { + // we found half of a pair + p = find_pair(q, *q); + if (p == NULL) { + indicate_error(c); + } else { + dot = p; + } + break; + } + } + if (*q == '\n') + indicate_error(c); + break; + case 'f': // f- forward to a user specified char + last_forward_char = get_one_char(); // get the search char + // + // dont separate these two commands. 'f' depends on ';' + // + //**** fall thru to ... 'i' + case ';': // ;- look at rest of line for last forward char + if (cmdcnt-- > 1) { + do_cmd(';'); + } // repeat cnt + if (last_forward_char == 0) break; + q = dot + 1; + while (q < end - 1 && *q != '\n' && *q != last_forward_char) { + q++; + } + if (*q == last_forward_char) + dot = q; + break; + case '-': // -- goto prev line + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_prev(); + dot_skip_over_ws(); + break; +#ifdef CONFIG_FEATURE_VI_DOT_CMD + case '.': // .- repeat the last modifying command + // Stuff the last_modifying_cmd back into stdin + // and let it be re-executed. + if (last_modifying_cmd != 0) { + ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd); + } + break; +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ +#ifdef CONFIG_FEATURE_VI_SEARCH + case '?': // /- search for a pattern + case '/': // /- search for a pattern + buf[0] = c; + buf[1] = '\0'; + q = get_input_line(buf); // get input line- use "status line" + if (strlen((char *) q) == 1) + goto dc3; // if no pat re-use old pat + if (strlen((char *) q) > 1) { // new pat- save it and find + // there is a new pat + free(last_search_pattern); + last_search_pattern = (Byte *) bb_xstrdup((char *) q); + goto dc3; // now find the pattern + } + // user changed mind and erased the "/"- do nothing + break; + case 'N': // N- backward search for last pattern + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dir = BACK; // assume BACKWARD search + p = dot - 1; + if (last_search_pattern[0] == '?') { + dir = FORWARD; + p = dot + 1; + } + goto dc4; // now search for pattern + break; + case 'n': // n- repeat search for last pattern + // search rest of text[] starting at next char + // if search fails return orignal "p" not the "p+1" address + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dc3: + if (last_search_pattern == 0) { + msg = (Byte *) "No previous regular expression"; + goto dc2; + } + if (last_search_pattern[0] == '/') { + dir = FORWARD; // assume FORWARD search + p = dot + 1; + } + if (last_search_pattern[0] == '?') { + dir = BACK; + p = dot - 1; + } + dc4: + q = char_search(p, last_search_pattern + 1, dir, FULL); + if (q != NULL) { + dot = q; // good search, update "dot" + msg = (Byte *) ""; + goto dc2; + } + // no pattern found between "dot" and "end"- continue at top + p = text; + if (dir == BACK) { + p = end - 1; + } + q = char_search(p, last_search_pattern + 1, dir, FULL); + if (q != NULL) { // found something + dot = q; // found new pattern- goto it + msg = (Byte *) "search hit BOTTOM, continuing at TOP"; + if (dir == BACK) { + msg = (Byte *) "search hit TOP, continuing at BOTTOM"; + } + } else { + msg = (Byte *) "Pattern not found"; + } + dc2: + psbs("%s", msg); + break; + case '{': // {- move backward paragraph + q = char_search(dot, (Byte *) "\n\n", BACK, FULL); + if (q != NULL) { // found blank line + dot = next_line(q); // move to next blank line + } + break; + case '}': // }- move forward paragraph + q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL); + if (q != NULL) { // found blank line + dot = next_line(q); // move to next blank line + } + break; +#endif /* CONFIG_FEATURE_VI_SEARCH */ + case '0': // 0- goto begining of line + case '1': // 1- + case '2': // 2- + case '3': // 3- + case '4': // 4- + case '5': // 5- + case '6': // 6- + case '7': // 7- + case '8': // 8- + case '9': // 9- + if (c == '0' && cmdcnt < 1) { + dot_begin(); // this was a standalone zero + } else { + cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number + } + break; + case ':': // :- the colon mode commands + p = get_input_line((Byte *) ":"); // get input line- use "status line" +#ifdef CONFIG_FEATURE_VI_COLON + colon(p); // execute the command +#else /* CONFIG_FEATURE_VI_COLON */ + if (*p == ':') + p++; // move past the ':' + cnt = strlen((char *) p); + if (cnt <= 0) + break; + if (strncasecmp((char *) p, "quit", cnt) == 0 || + strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines + if (file_modified && p[1] != '!') { + psbs("No write since last change (:quit! overrides)"); + } else { + editing = 0; + } + } else if (strncasecmp((char *) p, "write", cnt) == 0 || + strncasecmp((char *) p, "wq", cnt) == 0 || + strncasecmp((char *) p, "x", cnt) == 0) { + cnt = file_write(cfn, text, end - 1); + file_modified = FALSE; + psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt); + if (p[0] == 'x' || p[1] == 'q') { + editing = 0; + } + } else if (strncasecmp((char *) p, "file", cnt) == 0 ) { + edit_status(); // show current file status + } else if (sscanf((char *) p, "%d", &j) > 0) { + dot = find_line(j); // go to line # j + dot_skip_over_ws(); + } else { // unrecognised cmd + ni((Byte *) p); + } +#endif /* CONFIG_FEATURE_VI_COLON */ + break; + case '<': // <- Left shift something + case '>': // >- Right shift something + cnt = count_lines(text, dot); // remember what line we are on + c1 = get_one_char(); // get the type of thing to delete + find_range(&p, &q, c1); + (void) yank_delete(p, q, 1, YANKONLY); // save copy before change + p = begin_line(p); + q = end_line(q); + i = count_lines(p, q); // # of lines we are shifting + for ( ; i > 0; i--, p = next_line(p)) { + if (c == '<') { + // shift left- remove tab or 8 spaces + if (*p == '\t') { + // shrink buffer 1 char + (void) text_hole_delete(p, p); + } else if (*p == ' ') { + // we should be calculating columns, not just SPACE + for (j = 0; *p == ' ' && j < tabstop; j++) { + (void) text_hole_delete(p, p); + } + } + } else if (c == '>') { + // shift right -- add tab or 8 spaces + (void) char_insert(p, '\t'); + } + } + dot = find_line(cnt); // what line were we on + dot_skip_over_ws(); + end_cmd_q(); // stop adding to q + break; + case 'A': // A- append at e-o-l + dot_end(); // go to e-o-l + //**** fall thru to ... 'a' + case 'a': // a- append after current char + if (*dot != '\n') + dot++; + goto dc_i; + break; + case 'B': // B- back a blank-delimited Word + case 'E': // E- end of a blank-delimited word + case 'W': // W- forward a blank-delimited word + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dir = FORWARD; + if (c == 'B') + dir = BACK; + if (c == 'W' || isspace(dot[dir])) { + dot = skip_thing(dot, 1, dir, S_TO_WS); + dot = skip_thing(dot, 2, dir, S_OVER_WS); + } + if (c != 'W') + dot = skip_thing(dot, 1, dir, S_BEFORE_WS); + break; + case 'C': // C- Change to e-o-l + case 'D': // D- delete to e-o-l + save_dot = dot; + dot = dollar_line(dot); // move to before NL + // copy text into a register and delete + dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l + if (c == 'C') + goto dc_i; // start inserting +#ifdef CONFIG_FEATURE_VI_DOT_CMD + if (c == 'D') + end_cmd_q(); // stop adding to q +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ + break; + case 'G': // G- goto to a line number (default= E-O-F) + dot = end - 1; // assume E-O-F + if (cmdcnt > 0) { + dot = find_line(cmdcnt); // what line is #cmdcnt + } + dot_skip_over_ws(); + break; + case 'H': // H- goto top line on screen + dot = screenbegin; + if (cmdcnt > (rows - 1)) { + cmdcnt = (rows - 1); + } + if (cmdcnt-- > 1) { + do_cmd('+'); + } // repeat cnt + dot_skip_over_ws(); + break; + case 'I': // I- insert before first non-blank + dot_begin(); // 0 + dot_skip_over_ws(); + //**** fall thru to ... 'i' + case 'i': // i- insert before current char + case VI_K_INSERT: // Cursor Key Insert + dc_i: + cmd_mode = 1; // start insrting + psb("-- Insert --"); + break; + case 'J': // J- join current and next lines together + if (cmdcnt-- > 2) { + do_cmd(c); + } // repeat cnt + dot_end(); // move to NL + if (dot < end - 1) { // make sure not last char in text[] + *dot++ = ' '; // replace NL with space + while (isblnk(*dot)) { // delete leading WS + dot_delete(); + } + } + end_cmd_q(); // stop adding to q + break; + case 'L': // L- goto bottom line on screen + dot = end_screen(); + if (cmdcnt > (rows - 1)) { + cmdcnt = (rows - 1); + } + if (cmdcnt-- > 1) { + do_cmd('-'); + } // repeat cnt + dot_begin(); + dot_skip_over_ws(); + break; + case 'M': // M- goto middle line on screen + dot = screenbegin; + for (cnt = 0; cnt < (rows-1) / 2; cnt++) + dot = next_line(dot); + break; + case 'O': // O- open a empty line above + // 0i\n ESC -i + p = begin_line(dot); + if (p[-1] == '\n') { + dot_prev(); + case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..." + dot_end(); + dot = char_insert(dot, '\n'); + } else { + dot_begin(); // 0 + dot = char_insert(dot, '\n'); // i\n ESC + dot_prev(); // - + } + goto dc_i; + break; + case 'R': // R- continuous Replace char + dc5: + cmd_mode = 2; + psb("-- Replace --"); + break; + case 'X': // X- delete char before dot + case 'x': // x- delete the current char + case 's': // s- substitute the current char + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dir = 0; + if (c == 'X') + dir = -1; + if (dot[dir] != '\n') { + if (c == 'X') + dot--; // delete prev char + dot = yank_delete(dot, dot, 0, YANKDEL); // delete char + } + if (c == 's') + goto dc_i; // start insrting + end_cmd_q(); // stop adding to q + break; + case 'Z': // Z- if modified, {write}; exit + // ZZ means to save file (if necessary), then exit + c1 = get_one_char(); + if (c1 != 'Z') { + indicate_error(c); + break; + } + if (file_modified +#ifdef CONFIG_FEATURE_VI_READONLY + && ! vi_readonly + && ! readonly +#endif /* CONFIG_FEATURE_VI_READONLY */ + ) { + cnt = file_write(cfn, text, end - 1); + if (cnt == (end - 1 - text + 1)) { + editing = 0; + } + } else { + editing = 0; + } + break; + case '^': // ^- move to first non-blank on line + dot_begin(); + dot_skip_over_ws(); + break; + case 'b': // b- back a word + case 'e': // e- end of word + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dir = FORWARD; + if (c == 'b') + dir = BACK; + if ((dot + dir) < text || (dot + dir) > end - 1) + break; + dot += dir; + if (isspace(*dot)) { + dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS); + } + if (isalnum(*dot) || *dot == '_') { + dot = skip_thing(dot, 1, dir, S_END_ALNUM); + } else if (ispunct(*dot)) { + dot = skip_thing(dot, 1, dir, S_END_PUNCT); + } + break; + case 'c': // c- change something + case 'd': // d- delete something +#ifdef CONFIG_FEATURE_VI_YANKMARK + case 'y': // y- yank something + case 'Y': // Y- Yank a line +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + yf = YANKDEL; // assume either "c" or "d" +#ifdef CONFIG_FEATURE_VI_YANKMARK + if (c == 'y' || c == 'Y') + yf = YANKONLY; +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + c1 = 'y'; + if (c != 'Y') + c1 = get_one_char(); // get the type of thing to delete + find_range(&p, &q, c1); + if (c1 == 27) { // ESC- user changed mind and wants out + c = c1 = 27; // Escape- do nothing + } else if (strchr("wW", c1)) { + if (c == 'c') { + // don't include trailing WS as part of word + while (isblnk(*q)) { + if (q <= text || q[-1] == '\n') + break; + q--; + } + } + dot = yank_delete(p, q, 0, yf); // delete word + } else if (strchr("^0bBeEft$", c1)) { + // single line copy text into a register and delete + dot = yank_delete(p, q, 0, yf); // delete word + } else if (strchr("cdykjHL%+-{}\r\n", c1)) { + // multiple line copy text into a register and delete + dot = yank_delete(p, q, 1, yf); // delete lines + if (c == 'c') { + dot = char_insert(dot, '\n'); + // on the last line of file don't move to prev line + if (dot != (end-1)) { + dot_prev(); + } + } else if (c == 'd') { + dot_begin(); + dot_skip_over_ws(); + } + } else { + // could not recognize object + c = c1 = 27; // error- + indicate_error(c); + } + if (c1 != 27) { + // if CHANGING, not deleting, start inserting after the delete + if (c == 'c') { + strcpy((char *) buf, "Change"); + goto dc_i; // start inserting + } + if (c == 'd') { + strcpy((char *) buf, "Delete"); + } +#ifdef CONFIG_FEATURE_VI_YANKMARK + if (c == 'y' || c == 'Y') { + strcpy((char *) buf, "Yank"); + } + p = reg[YDreg]; + q = p + strlen((char *) p); + for (cnt = 0; p <= q; p++) { + if (*p == '\n') + cnt++; + } + psb("%s %d lines (%d chars) using [%c]", + buf, cnt, strlen((char *) reg[YDreg]), what_reg()); +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + end_cmd_q(); // stop adding to q + } + break; + case 'k': // k- goto prev line, same col + case VI_K_UP: // cursor key Up + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_prev(); + dot = move_to_col(dot, ccol + offset); // try stay in same col + break; + case 'r': // r- replace the current char with user input + c1 = get_one_char(); // get the replacement char + if (*dot != '\n') { + *dot = c1; + file_modified = TRUE; // has the file been modified + } + end_cmd_q(); // stop adding to q + break; + case 't': // t- move to char prior to next x + last_forward_char = get_one_char(); + do_cmd(';'); + if (*dot == last_forward_char) + dot_left(); + last_forward_char= 0; + break; + case 'w': // w- forward a word + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + if (isalnum(*dot) || *dot == '_') { // we are on ALNUM + dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM); + } else if (ispunct(*dot)) { // we are on PUNCT + dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT); + } + if (dot < end - 1) + dot++; // move over word + if (isspace(*dot)) { + dot = skip_thing(dot, 2, FORWARD, S_OVER_WS); + } + break; + case 'z': // z- + c1 = get_one_char(); // get the replacement char + cnt = 0; + if (c1 == '.') + cnt = (rows - 2) / 2; // put dot at center + if (c1 == '-') + cnt = rows - 2; // put dot at bottom + screenbegin = begin_line(dot); // start dot at top + dot_scroll(cnt, -1); + break; + case '|': // |- move to column "cmdcnt" + dot = move_to_col(dot, cmdcnt - 1); // try to move to column + break; + case '~': // ~- flip the case of letters a-z -> A-Z + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + if (islower(*dot)) { + *dot = toupper(*dot); + file_modified = TRUE; // has the file been modified + } else if (isupper(*dot)) { + *dot = tolower(*dot); + file_modified = TRUE; // has the file been modified + } + dot_right(); + end_cmd_q(); // stop adding to q + break; + //----- The Cursor and Function Keys ----------------------------- + case VI_K_HOME: // Cursor Key Home + dot_begin(); + break; + // The Fn keys could point to do_macro which could translate them + case VI_K_FUN1: // Function Key F1 + case VI_K_FUN2: // Function Key F2 + case VI_K_FUN3: // Function Key F3 + case VI_K_FUN4: // Function Key F4 + case VI_K_FUN5: // Function Key F5 + case VI_K_FUN6: // Function Key F6 + case VI_K_FUN7: // Function Key F7 + case VI_K_FUN8: // Function Key F8 + case VI_K_FUN9: // Function Key F9 + case VI_K_FUN10: // Function Key F10 + case VI_K_FUN11: // Function Key F11 + case VI_K_FUN12: // Function Key F12 + break; + } + + dc1: + // if text[] just became empty, add back an empty line + if (end == text) { + (void) char_insert(text, '\n'); // start empty buf with dummy line + dot = text; + } + // it is OK for dot to exactly equal to end, otherwise check dot validity + if (dot != end) { + dot = bound_dot(dot); // make sure "dot" is valid + } +#ifdef CONFIG_FEATURE_VI_YANKMARK + check_context(c); // update the current context +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + + if (!isdigit(c)) + cmdcnt = 0; // cmd was not a number, reset cmdcnt + cnt = dot - begin_line(dot); + // Try to stay off of the Newline + if (*dot == '\n' && cnt > 0 && cmd_mode == 0) + dot--; +} + +#ifdef CONFIG_FEATURE_VI_CRASHME +static int totalcmds = 0; +static int Mp = 85; // Movement command Probability +static int Np = 90; // Non-movement command Probability +static int Dp = 96; // Delete command Probability +static int Ip = 97; // Insert command Probability +static int Yp = 98; // Yank command Probability +static int Pp = 99; // Put command Probability +static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0; +char chars[20] = "\t012345 abcdABCD-=.$"; +char *words[20] = { "this", "is", "a", "test", + "broadcast", "the", "emergency", "of", + "system", "quick", "brown", "fox", + "jumped", "over", "lazy", "dogs", + "back", "January", "Febuary", "March" +}; +char *lines[20] = { + "You should have received a copy of the GNU General Public License\n", + "char c, cm, *cmd, *cmd1;\n", + "generate a command by percentages\n", + "Numbers may be typed as a prefix to some commands.\n", + "Quit, discarding changes!\n", + "Forced write, if permission originally not valid.\n", + "In general, any ex or ed command (such as substitute or delete).\n", + "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n", + "Please get w/ me and I will go over it with you.\n", + "The following is a list of scheduled, committed changes.\n", + "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n", + "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n", + "Any question about transactions please contact Sterling Huxley.\n", + "I will try to get back to you by Friday, December 31.\n", + "This Change will be implemented on Friday.\n", + "Let me know if you have problems accessing this;\n", + "Sterling Huxley recently added you to the access list.\n", + "Would you like to go to lunch?\n", + "The last command will be automatically run.\n", + "This is too much english for a computer geek.\n", +}; +char *multilines[20] = { + "You should have received a copy of the GNU General Public License\n", + "char c, cm, *cmd, *cmd1;\n", + "generate a command by percentages\n", + "Numbers may be typed as a prefix to some commands.\n", + "Quit, discarding changes!\n", + "Forced write, if permission originally not valid.\n", + "In general, any ex or ed command (such as substitute or delete).\n", + "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n", + "Please get w/ me and I will go over it with you.\n", + "The following is a list of scheduled, committed changes.\n", + "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n", + "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n", + "Any question about transactions please contact Sterling Huxley.\n", + "I will try to get back to you by Friday, December 31.\n", + "This Change will be implemented on Friday.\n", + "Let me know if you have problems accessing this;\n", + "Sterling Huxley recently added you to the access list.\n", + "Would you like to go to lunch?\n", + "The last command will be automatically run.\n", + "This is too much english for a computer geek.\n", +}; + +// create a random command to execute +static void crash_dummy() +{ + static int sleeptime; // how long to pause between commands + char c, cm, *cmd, *cmd1; + int i, cnt, thing, rbi, startrbi, percent; + + // "dot" movement commands + cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL"; + + // is there already a command running? + if (readed_for_parse > 0) + goto cd1; + cd0: + startrbi = rbi = 0; + sleeptime = 0; // how long to pause between commands + memset(readbuffer, '\0', BUFSIZ); // clear the read buffer + // generate a command by percentages + percent = (int) lrand48() % 100; // get a number from 0-99 + if (percent < Mp) { // Movement commands + // available commands + cmd = cmd1; + M++; + } else if (percent < Np) { // non-movement commands + cmd = "mz<>\'\""; // available commands + N++; + } else if (percent < Dp) { // Delete commands + cmd = "dx"; // available commands + D++; + } else if (percent < Ip) { // Inset commands + cmd = "iIaAsrJ"; // available commands + I++; + } else if (percent < Yp) { // Yank commands + cmd = "yY"; // available commands + Y++; + } else if (percent < Pp) { // Put commands + cmd = "pP"; // available commands + P++; + } else { + // We do not know how to handle this command, try again + U++; + goto cd0; + } + // randomly pick one of the available cmds from "cmd[]" + i = (int) lrand48() % strlen(cmd); + cm = cmd[i]; + if (strchr(":\024", cm)) + goto cd0; // dont allow colon or ctrl-T commands + readbuffer[rbi++] = cm; // put cmd into input buffer + + // now we have the command- + // there are 1, 2, and multi char commands + // find out which and generate the rest of command as necessary + if (strchr("dmryz<>\'\"", cm)) { // 2-char commands + cmd1 = " \n\r0$^-+wWeEbBhjklHL"; + if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[] + cmd1 = "abcdefghijklmnopqrstuvwxyz"; + } + thing = (int) lrand48() % strlen(cmd1); // pick a movement command + c = cmd1[thing]; + readbuffer[rbi++] = c; // add movement to input buffer + } + if (strchr("iIaAsc", cm)) { // multi-char commands + if (cm == 'c') { + // change some thing + thing = (int) lrand48() % strlen(cmd1); // pick a movement command + c = cmd1[thing]; + readbuffer[rbi++] = c; // add movement to input buffer + } + thing = (int) lrand48() % 4; // what thing to insert + cnt = (int) lrand48() % 10; // how many to insert + for (i = 0; i < cnt; i++) { + if (thing == 0) { // insert chars + readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))]; + } else if (thing == 1) { // insert words + strcat((char *) readbuffer, words[(int) lrand48() % 20]); + strcat((char *) readbuffer, " "); + sleeptime = 0; // how fast to type + } else if (thing == 2) { // insert lines + strcat((char *) readbuffer, lines[(int) lrand48() % 20]); + sleeptime = 0; // how fast to type + } else { // insert multi-lines + strcat((char *) readbuffer, multilines[(int) lrand48() % 20]); + sleeptime = 0; // how fast to type + } + } + strcat((char *) readbuffer, "\033"); + } + readed_for_parse = strlen(readbuffer); + cd1: + totalcmds++; + if (sleeptime > 0) + (void) mysleep(sleeptime); // sleep 1/100 sec +} + +// test to see if there are any errors +static void crash_test() +{ + static time_t oldtim; + time_t tim; + char d[2], msg[BUFSIZ]; + + msg[0] = '\0'; + if (end < text) { + strcat((char *) msg, "end 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); + printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s", + totalcmds, last_input_char, msg, SOs, SOn); + fflush(stdout); + while (read(0, d, 1) > 0) { + if (d[0] == '\n' || d[0] == '\r') + break; + } + alarm(3); + } + tim = (time_t) time((time_t *) 0); + if (tim >= (oldtim + 3)) { + sprintf((char *) status_buffer, + "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d", + totalcmds, M, N, I, D, Y, P, U, end - text + 1); + oldtim = tim; + } + return; +} +#endif /* CONFIG_FEATURE_VI_CRASHME */ diff --git a/busybox/examples/bootfloppy/bootfloppy.txt b/busybox/examples/bootfloppy/bootfloppy.txt new file mode 100644 index 000000000..16f2c130f --- /dev/null +++ b/busybox/examples/bootfloppy/bootfloppy.txt @@ -0,0 +1,180 @@ +Building a Busybox Boot Floppy +============================== + +This document describes how to buid a boot floppy using the following +components: + + - Linux Kernel (http://www.kernel.org) + - uClibc: C library (http://cvs.uclinux.org/uClibc.html) + - Busybox: Unix utilities (http://busybox.net/) + - Syslinux: bootloader (http://syslinux.zytor.com) + +It is based heavily on a paper presented by Erik Andersen at the 2001 Embedded +Systems Conference. + + + +Building The Software Components +-------------------------------- + +Detailed instructions on how to build Busybox, uClibc, or a working Linux +kernel are beyond the scope of this document. The following guidelines will +help though: + + - Stock Busybox from CVS or a tarball will work with no modifications to + any files. Just extract and go. + - Ditto uClibc. + - Your Linux kernel must include support for initrd or else the floppy + won't be able to mount it's root file system. + +If you require further information on building Busybox uClibc or Linux, please +refer to the web pages and documentation for those individual programs. + + + +Making a Root File System +------------------------- + +The following steps will create a root file system. + + - Create an empty file that you can format as a filesystem: + + dd if=/dev/zero of=rootfs bs=1k count=4000 + + - Set up the rootfs file we just created to be used as a loop device (may not + be necessary) + + losetup /dev/loop0 rootfs + + - Format the rootfs file with a filesystem: + + mkfs.ext2 -F -i 2000 rootfs + + - Mount the file on a mountpoint so we can place files in it: + + mkdir loop + mount -o loop rootfs loop/ + + (you will probably need to be root to do this) + + - Copy on the C library, the dynamic linking library, and other necessary + libraries. For this example, we copy the following files from the uClibc + tree: + + mkdir loop/lib + (chdir to uClibc directory) + cp -a libc.so* uClibc*.so \ + ld.so-1/d-link/ld-linux-uclibc.so* \ + ld.so-1/libdl/libdl.so* \ + crypt/libcrypt.so* \ + (path to)loop/lib + + - Install the Busybox binary and accompanying symlinks: + + (chdir to busybox directory) + make PREFIX=(path to)loop/ install + + - Make device files in /dev: + + This can be done by running the 'mkdevs.sh' script. If you want the gory + details, you can read the script. + + - Make necessary files in /etc: + + For this, just cp -a the etc/ directory onto rootfs. Again, if you want + all the details, you can just look at the files in the dir. + + - Unmount the rootfs from the mountpoint: + + umount loop + + - Compress it: + + gzip -9 rootfs + + +Making a SYSLINUX boot floppy +----------------------------- + +The following steps will create the boot floppy. + +Note: You will need to have the mtools package installed beforehand. + + - Insert a floppy in the drive and format it with an MSDOS filesystem: + + mformat a: + + (if the system doesn't know what device 'a:' is, look at /etc/mtools.conf) + + - Run syslinux on the floppy: + + syslinux -s /dev/fd0 + + (the -s stands for "safe, slow, and stupid" and should work better with + buggy BIOSes; it can be omitted) + + - Put on a syslinux.cfg file: + + mcopy syslinux.cfg a: + + (more on syslinux.cfg below) + + - Copy the root file system you made onto the MSDOS formatted floppy + + mcopy rootfs.gz a: + + - Build a linux kernel and copy it onto the disk with the filename 'linux' + + mcopy bzImage a:linux + + +Sample syslinux.cfg +~~~~~~~~~~~~~~~~~~~ + +The following simple syslinux.cfg file should work. You can tweak it if you +like. + +----begin-syslinux.cfg--------------- +DEFAULT linux +APPEND initrd=rootfs.gz root=/dev/ram0 +TIMEOUT 10 +PROMPT 1 +----end-syslinux.cfg--------------- + +Some changes you could make to syslinux.cfg: + + - This value is the number seconds it will wait before booting. You can set + the timeout to 0 (or omit) to boot instantly, or you can set it as high as + 10 to wait awhile. + + - PROMPT can be set to 0 to disable the 'boot:' prompt. + + - you can add this line to display the contents of a file as a welcome + message: + + DISPLAY display.txt + + + +Additional Resources +-------------------- + +Other useful information on making a Linux bootfloppy is available at the +following URLs: + +http://www.linuxdoc.org/HOWTO/Bootdisk-HOWTO/index.html +http://www.linux-embedded.com/howto/Embedded-Linux-Howto.html +http://linux-embedded.org/howto/LFS-HOWTO.html +http://linux-embedded.org/pmhowto.html +http://recycle.lbl.gov/~ldoolitt/embedded/ (Larry Doolittle's stuff) + + + +Possible TODOs +-------------- + +The following features that we might want to add later: + + - support for additional filesystems besides ext2, i.e. minix + - different libc, static vs dynamic loading + - maybe using an alternate bootloader diff --git a/busybox/examples/bootfloppy/display.txt b/busybox/examples/bootfloppy/display.txt new file mode 100644 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..8a7c77d78 --- /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..e79ed418e --- /dev/null +++ b/busybox/examples/bootfloppy/mkrootfs.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# +# mkrootfs.sh - creates a root file system +# + +# TODO: need to add checks here to verify that busybox, uClibc and bzImage +# exist + + +# command-line settable variables +BUSYBOX_DIR=.. +UCLIBC_DIR=../../uClibc +TARGET_DIR=./loop +FSSIZE=4000 +CLEANUP=1 +MKFS='mkfs.ext2 -F' + +# don't-touch variables +BASE_DIR=`pwd` + + +while getopts 'b:u:s:t:Cm' opt +do + case $opt in + b) BUSYBOX_DIR=$OPTARG ;; + u) UCLIBC_DIR=$OPTARG ;; + t) TARGET_DIR=$OPTARG ;; + s) FSSIZE=$OPTARG ;; + C) CLEANUP=0 ;; + m) MKFS='mkfs.minix' ;; + *) + echo "usage: `basename $0` [-bu]" + echo " -b DIR path to busybox direcory (default ..)" + echo " -u DIR path to uClibc direcory (default ../../uClibc)" + echo " -t DIR path to target direcory (default ./loop)" + echo " -s SIZE size of root filesystem in Kbytes (default 4000)" + echo " -C don't perform cleanup (umount target dir, gzip rootfs, etc.)" + echo " (this allows you to 'chroot loop/ /bin/sh' to test it)" + echo " -m use minix filesystem (default is ext2)" + exit 1 + ;; + esac +done + + + + +# clean up from any previous work +mount | grep -q loop +[ $? -eq 0 ] && umount $TARGET_DIR +[ -d $TARGET_DIR ] && rm -rf $TARGET_DIR/ +[ -f rootfs ] && rm -f rootfs +[ -f rootfs.gz ] && rm -f rootfs.gz + + +# prepare root file system and mount as loopback +dd if=/dev/zero of=rootfs bs=1k count=$FSSIZE +$MKFS -i 2000 rootfs +mkdir $TARGET_DIR +mount -o loop,exec rootfs $TARGET_DIR # must be root + + +# install uClibc +mkdir -p $TARGET_DIR/lib +cd $UCLIBC_DIR +make INSTALL_DIR= +cp -a libc.so* $BASE_DIR/$TARGET_DIR/lib +cp -a uClibc*.so $BASE_DIR/$TARGET_DIR/lib +cp -a ld.so-1/d-link/ld-linux-uclibc.so* $BASE_DIR/$TARGET_DIR/lib +cp -a ld.so-1/libdl/libdl.so* $BASE_DIR/$TARGET_DIR/lib +cp -a crypt/libcrypt.so* $BASE_DIR/$TARGET_DIR/lib +cd $BASE_DIR + + +# install busybox and components +cd $BUSYBOX_DIR +make distclean +make CC=$BASE_DIR/$UCLIBC_DIR/extra/gcc-uClibc/i386-uclibc-gcc +make PREFIX=$BASE_DIR/$TARGET_DIR install +cd $BASE_DIR + + +# make files in /dev +mkdir $TARGET_DIR/dev +./mkdevs.sh $TARGET_DIR/dev + + +# make files in /etc +cp -a etc $TARGET_DIR +ln -s /proc/mounts $TARGET_DIR/etc/mtab + + +# other miscellaneous setup +mkdir $TARGET_DIR/initrd +mkdir $TARGET_DIR/proc + + +# Done. Maybe do cleanup. +if [ $CLEANUP -eq 1 ] +then + umount $TARGET_DIR + rmdir $TARGET_DIR + gzip -9 rootfs +fi + diff --git a/busybox/examples/bootfloppy/mksyslinux.sh b/busybox/examples/bootfloppy/mksyslinux.sh new file mode 100755 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..fa2677ca8 --- /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..3986436c1 --- /dev/null +++ b/busybox/examples/busybox.spec @@ -0,0 +1,44 @@ +%define name busybox +%define epoch 0 +%define version 0.61.pre +%define release %(date -I | sed -e 's/-/_/g') +%define serial 1 + +Name: %{name} +#Epoch: %{epoch} +Version: %{version} +Release: %{release} +Serial: %{serial} +Copyright: GPL +Group: System/Utilities +Summary: BusyBox is a tiny suite of Unix utilities in a multi-call binary. +URL: http://busybox.net/ +Source: ftp://busybox.net/busybox/%{name}-%{version}.tar.gz +Buildroot: /var/tmp/%{name}-%{version} +Packager : Erik Andersen + +%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..9af192208 --- /dev/null +++ b/busybox/examples/depmod.pl @@ -0,0 +1,237 @@ +#!/usr/bin/perl -w +# vi: set ts=4: +# Copyright (c) 2001 David Schleef +# Copyright (c) 2001 Erik Andersen +# Copyright (c) 2001 Stuart Hughes +# Copyright (c) 2002 Steven J. Hill +# This program is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. + +# TODO -- use strict mode... +#use strict; + +use Getopt::Long; +use File::Find; + + +# Set up some default values + +my $basedir=""; +my $kernel; +my $kernelsyms; +my $stdout=0; +my $verbose=0; + + +# get command-line options + +my %opt; + +GetOptions( + \%opt, + "help|h", + "basedir|b=s" => \$basedir, + "kernel|k=s" => \$kernel, + "kernelsyms|F=s" => \$kernelsyms, + "stdout|n" => \$stdout, + "verbose|v" => \$verbose, +); + +if (defined $opt{help}) { + print + " $0 [OPTION]... [basedir]\n", + " -h --help\t\tShow this help screen\n", + " -b --basedir\tModules base directory (defaults to /lib/modules)\n", + " -k --kernel\tKernel binary for the target\n", + " -F --kernelsyms\tKernel symbol file\n", + " -n --stdout\tWrite to stdout instead of /modules.dep\n", + " -v --verbose\tPrint out lots of debugging stuff\n", + ; + exit 1; +} + +if($basedir !~ m-/lib/modules-) { + warn "WARNING: base directory does not match ..../lib/modules\n"; +} + +# Find the list of .o files living under $basedir +#if ($verbose) { printf "Locating all modules\n"; } +my($ofile) = ""; +my($file) = ""; +my(@liblist) = (); +find sub { + if ( -f $_ && ! -d $_ ) { + $file = $File::Find::name; + if ( $file =~ /.o$/ ) { + push(@liblist, $file); + if ($verbose) { printf "$file\n"; } + } + } +}, $basedir; +if ($verbose) { printf "Finished locating modules\n"; } + +foreach $obj ( @liblist, $kernel ){ + # turn the input file name into a target tag name + # vmlinux is a special that is only used to resolve symbols + if($obj =~ /vmlinux/) { + $tgtname = "vmlinux"; + } else { + ($tgtname) = $obj =~ m-(/lib/modules/.*)$-; + } + + warn "MODULE = $tgtname\n" if $verbose; + + # get a list of symbols + @output=`nm $obj`; + $ksymtab=grep m/ __ksymtab/, @output; + + # gather the exported symbols + if($ksymtab){ + # explicitly exported + foreach ( @output ) { + / __ksymtab_(.*)$/ and do { + warn "sym = $1\n" if $verbose; + $exp->{$1} = $tgtname; + }; + } + } else { + # exporting all symbols + foreach ( @output) { + / [ABCDGRST] (.*)$/ and do { + warn "syma = $1\n" if $verbose; + $exp->{$1} = $tgtname; + }; + } + } + # gather the unresolved symbols + foreach ( @output ) { + !/ __this_module/ && / U (.*)$/ and do { + warn "und = $1\n" if $verbose; + push @{$dep->{$tgtname}}, $1; + }; + } +} + + +# reduce dependancies: remove unresolvable and resolved from vmlinux +# remove duplicates +foreach $module (keys %$dep) { + $mod->{$module} = {}; + foreach (@{$dep->{$module}}) { + if( $exp->{$_} ) { + warn "resolved symbol $_ in file $exp->{$_}\n" if $verbose; + next if $exp->{$_} =~ /vmlinux/; + $mod->{$module}{$exp->{$_}} = 1; + } else { + warn "unresolved symbol $_ in file $module\n"; + } + } +} + +# resolve the dependancies for each module +if ($stdout == 1) { + foreach $module ( keys %$mod ) { + print "$module:\t"; + @sorted = sort bydep keys %{$mod->{$module}}; + print join(" \\\n\t",@sorted); + print "\n\n"; + } +} else { + open(OFILE, ">$basedir/modules.dep"); + foreach $module ( keys %$mod ) { + print OFILE "$module:\t"; + @sorted = sort bydep keys %{$mod->{$module}}; + print OFILE join(" \\\n\t",@sorted); + print OFILE "\n\n"; + } +} + + +sub bydep +{ + foreach my $f ( keys %{$mod->{$b}} ) { + if($f eq $a) { + return 1; + } + } + return -1; +} + + + +__END__ + +=head1 NAME + +depmod.pl - a cross platform script to generate kernel module dependency + lists which can then be used by modprobe. + +=head1 SYNOPSIS + +depmod.pl [OPTION]... [basedir]... + +Example: + + depmod.pl -F linux/System.map target/lib/modules + +=head1 DESCRIPTION + +The purpose of this script is to automagically generate a list of of kernel +module dependancies. This script produces dependancy lists that should be +identical to the depmod program from the modutils package. Unlike the depmod +binary, however, depmod.pl is designed to be run on your host system, not +on your target system. + +This script was written by David Schleef 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.4 2004/03/15 08:28:33 andersen Exp $ + diff --git a/busybox/examples/devfsd.conf b/busybox/examples/devfsd.conf new file mode 100644 index 000000000..e90e7102b --- /dev/null +++ b/busybox/examples/devfsd.conf @@ -0,0 +1,133 @@ +# Sample /etc/devfsd.conf configuration file. +# Richard Gooch 17-FEB-2002 +# +# adapted for busybox devfsd implementation by Tito +# +# Enable full compatibility mode for old device names. You may comment these +# out if you don't use the old device names. Make sure you know what you're +# doing! +REGISTER .* MKOLDCOMPAT +UNREGISTER .* RMOLDCOMPAT + +# You may comment out the above and uncomment the following if you've +# configured your system to use the original "new" devfs names or the really +# new names +#REGISTER ^vc/ MKOLDCOMPAT +#UNREGISTER ^vc/ RMOLDCOMPAT +#REGISTER ^pty/ MKOLDCOMPAT +#UNREGISTER ^pty/ RMOLDCOMPAT +#REGISTER ^misc/ MKOLDCOMPAT +#UNREGISTER ^misc/ RMOLDCOMPAT + +# You may comment these out if you don't use the original "new" names +REGISTER .* MKNEWCOMPAT +UNREGISTER .* RMNEWCOMPAT + +# Enable module autoloading. You may comment this out if you don't use +# autoloading +# Supported by busybox when CONFIG_DEVFSD_MODLOAD is set. +# This actually doesn't work with busybox modutils but needs +# the real modutils' modprobe +LOOKUP .* MODLOAD + +# Uncomment the following if you want to set the group to "tty" for the +# pseudo-tty devices. This is necessary so that mesg(1) can later be used to +# enable/disable talk requests and wall(1) messages. +REGISTER ^pty/s.* PERMISSIONS -1.tty 0600 +#REGISTER ^pts/.* PERMISSIONS -1.tty 0600 + +# Restoring /dev/log on startup would trigger the minilogd/initlog deadlock +# (minilogd falsely assuming syslogd has been started). +REGISTER ^log$ IGNORE +CREATE ^log$ IGNORE +CHANGE ^log$ IGNORE +DELETE ^log$ IGNORE + +# +# Uncomment this if you want permissions to be saved and restored +# Do not do this for pseudo-terminal devices +REGISTER ^pt[sy] IGNORE +CREATE ^pt[sy] IGNORE +CHANGE ^pt[sy] IGNORE +DELETE ^pt[sy] IGNORE +REGISTER .* COPY /lib/dev-state/$devname $devpath +CREATE .* COPY $devpath /lib/dev-state/$devname +CHANGE .* COPY $devpath /lib/dev-state/$devname +#DELETE .* CFUNCTION GLOBAL unlink /lib/dev-state/$devname +# Busybox +DELETE .* EXECUTE /bin/rm -f /lib/dev-state/$devname + +RESTORE /lib/dev-state + +# +# Uncomment this if you want the old /dev/cdrom symlink +#REGISTER ^cdroms/cdrom0$ CFUNCTION GLOBAL mksymlink $devname cdrom +#UNREGISTER ^cdroms/cdrom0$ CFUNCTION GLOBAL unlink cdrom +# busybox +REGISTER ^cdroms/cdrom0$ EXECUTE /bin/ln -sf $devname cdrom +UNREGISTER ^cdroms/cdrom0$ EXECUTE /bin/rm -f cdrom + +#REGISTER ^v4l/video0$ CFUNCTION GLOBAL mksymlink v4l/video0 video +#UNREGISTER ^v4l/video0$ CFUNCTION GLOBAL unlink video +#REGISTER ^radio0$ CFUNCTION GLOBAL mksymlink radio0 radio +#UNREGISTER ^radio0$ CFUNCTION GLOBAL unlink radio +# Busybox +REGISTER ^v4l/video0$ EXECUTE /bin/ln -sf v4l/video0 video +UNREGISTER ^v4l/video0$ EXECUTE /bin/rm -f video +REGISTER ^radio0$ EXECUTE /bin/ln -sf radio0 radio +UNREGISTER ^radio0$ EXECUTE /bin/rm -f radio + +# ALSA stuff +#LOOKUP snd MODLOAD ACTION snd + +# Uncomment this to let PAM manage devfs +# Not supported by busybox +#REGISTER .* CFUNCTION /lib/security/pam_console_apply_devfsd.so pam_console_apply_single $devpath + +# Uncomment this to manage USB mouse +# Not supported by busybox +#REGISTER ^input/mouse0$ CFUNCTION GLOBAL mksymlink $devname usbmouse +#UNREGISTER ^input/mouse0$ CFUNCTION GLOBAL unlink usbmouse +# Busybox +#REGISTER ^input/mouse0$ EXECUTE /bin/ln -sf $devname usbmouse +#UNREGISTER ^input/mouse0$ EXECUTE /bin/rm -f usbmouse +# Not supported by busybox +#REGISTER ^input/mice$ CFUNCTION GLOBAL mksymlink $devname usbmouse +#UNREGISTER ^input/mice$ CFUNCTION GLOBAL unlink usbmouse +# Busybox +REGISTER ^input/mice$ EXECUTE /bin/ln -sf $devname usbmouse +UNREGISTER ^input/mice$ EXECUTE /bin/rm -f usbmouse + +# If you have removable media and want to force media revalidation when looking +# up new or old compatibility names, uncomment the following lines +# SCSI NEWCOMPAT /dev/sd/* names +LOOKUP ^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 +# SCSI OLDCOMPAT /dev/sd?? names +LOOKUP ^(sd[a-z]+)[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 +# IDE NEWCOMPAT /dev/ide/hd/* names +LOOKUP ^(ide/hd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 +# IDE OLDCOMPAT /dev/hd?? names +LOOKUP ^(hd[a-z])[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 +# IDE-SCSI NEWCOMPAT /dev/sd/* names +#LOOKUP ^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 +#SCSI OLDCOMPAT /dev/scd? names +LOOKUP ^(scd+)[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1 + + +REGISTER ^dvb/card[0-9]+/[^/]+$ PERMISSIONS root.video 0660 +# Not supported by busybox +#REGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ CFUNCTION GLOBAL mksymlink /dev/$devname ost/\2\1 +#UNREGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ CFUNCTION GLOBAL unlink ost/\2\1 +# Busybox +REGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ EXECUTE /bin/ln -sf /dev/$devname ost/\2\1 +UNREGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ EXECUTE /bin/rm -f ost/\2\1 + +# Include package-generated files from /etc/devfs/conf.d +# Supported by busybox +# INCLUDE /etc/devfs/conf.d/ +INCLUDE /etc/devfs/busybox/ +# Busybox: just for testing +#INCLUDE /etc/devfs/nothing/ +#INCLUDE /etc/devfs/nothing/nothing +#OPTIONAL_INCLUDE /etc/devfs/nothing/ +#OPTIONAL_INCLUDE /etc/devfs/nothing/nothing diff --git a/busybox/examples/inetd.conf b/busybox/examples/inetd.conf new file mode 100644 index 000000000..ca7e3d8e1 --- /dev/null +++ b/busybox/examples/inetd.conf @@ -0,0 +1,73 @@ +# /etc/inetd.conf: see inetd(8) for further informations. +# +# Internet server configuration database +# +# +# If you want to disable an entry so it isn't touched during +# package updates just comment it out with a single '#' character. +# +# If you make changes to this file, either reboot your machine or +# send the inetd process a HUP signal: +# Do a "ps x" as root and look up the pid of inetd. Then do a +# kill -HUP +# inetd will re-read this file whenever it gets that signal. +# +# +#:INTERNAL: Internal services +# It is generally considered safer to keep these off. +echo stream tcp nowait root internal +echo dgram udp wait root internal +#discard stream tcp nowait root internal +#discard dgram udp wait root internal +daytime stream tcp nowait root internal +daytime dgram udp wait root internal +#chargen stream tcp nowait root internal +#chargen dgram udp wait root internal +time stream tcp nowait root internal +time dgram udp wait root internal + +# These are standard services. +# +#ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd +#telnet stream tcp nowait root /sbin/telnetd /sbin/telnetd +#nntp stream tcp nowait root tcpd in.nntpd +#smtp stream tcp nowait root tcpd sendmail -v +# +# Shell, login, exec and talk are BSD protocols. +# +# If you run an ntalk daemon (such as netkit-ntalk) on the old talk +# port, that is, "talk" as opposed to "ntalk", it won't work and may +# cause certain broken talk clients to malfunction. +# +# The talkd from netkit-ntalk 0.12 and higher, however, can speak the +# old talk protocol and can be used safely. +# +#shell stream tcp nowait root /usr/sbin/tcpd in.rshd -L +#login stream tcp nowait root /usr/sbin/tcpd in.rlogind -L +#exec stream tcp nowait root /usr/sbin/tcpd in.rexecd +#talk dgram udp wait root /usr/sbin/tcpd in.talkd +#ntalk dgram udp wait root /usr/sbin/tcpd in.talkd +# +# Pop et al +# Leave these off unless you're using them. +#pop2 stream tcp nowait root /usr/sbin/tcpd in.pop2d +#pop3 stream tcp nowait root /usr/sbin/tcpd in.pop3d +# +# The Internet UUCP service. +# uucp stream tcp nowait uucp /usr/sbin/tcpd /usr/lib/uucp/uucico -l +# +# Tftp service is provided primarily for booting. Most sites +# run this only on machines acting as "boot servers." If you don't +# need it, don't use it. +# +#tftp dgram udp wait nobody /usr/sbin/tcpd in.tftpd +#bootps dgram udp wait root /usr/sbin/in.bootpd in.bootpd +# +# Finger, systat and netstat give out user information which may be +# valuable to potential "system crackers." Many sites choose to disable +# some or all of these services to improve security. +# +#finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd -w +#systat stream tcp nowait nobody /usr/sbin/tcpd /bin/ps -auwwx +#netstat stream tcp nowait root /bin/netstat /bin/netstat -a +#ident stream tcp nowait root /usr/sbin/in.identd in.identd diff --git a/busybox/examples/inittab b/busybox/examples/inittab new file mode 100644 index 000000000..ce711ac6c --- /dev/null +++ b/busybox/examples/inittab @@ -0,0 +1,90 @@ +# /etc/inittab init(8) configuration for BusyBox +# +# Copyright (C) 1999-2004 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, +# restart, ctrlaltdel, and shutdown. +# +# Note: askfirst acts just like respawn, but before running the specified +# process it displays the line "Please press Enter to activate this +# console." and then waits for the user to press enter before starting +# the specified process. +# +# Note: unrecognised actions (like initdefault) will cause init to emit +# an error message, and then go along with its business. +# +# : Specifies the process to be executed and it's command line. +# +# Note: BusyBox init works just fine without an inittab. If no inittab is +# found, it has the following default behavior: +# ::sysinit:/etc/init.d/rcS +# ::askfirst:/bin/sh +# ::ctrlaltdel:/sbin/reboot +# ::shutdown:/sbin/swapoff -a +# ::shutdown:/bin/umount -a -r +# ::restart:/sbin/init +# +# if it detects that /dev/console is _not_ a serial console, it will +# also run: +# tty2::askfirst:/bin/sh +# tty3::askfirst:/bin/sh +# tty4::askfirst:/bin/sh +# +# Boot-time system configuration/initialization script. +# This is run first except when booting in single-user mode. +# +::sysinit:/etc/init.d/rcS + +# /bin/sh invocations on selected ttys +# +# Note below that we prefix the shell commands with a "-" to indicate to the +# shell that it is supposed to be a login shell. Normally this is handled by +# login, but since we are bypassing login in this case, BusyBox lets you do +# this yourself... +# +# Start an "askfirst" shell on the console (whatever that may be) +::askfirst:-/bin/sh +# Start an "askfirst" shell on /dev/tty2-4 +tty2::askfirst:-/bin/sh +tty3::askfirst:-/bin/sh +tty4::askfirst:-/bin/sh + +# /sbin/getty invocations for selected ttys +tty4::respawn:/sbin/getty 38400 tty5 +tty5::respawn:/sbin/getty 38400 tty6 + +# Example of how to put a getty on a serial line (for a terminal) +#::respawn:/sbin/getty -L ttyS0 9600 vt100 +#::respawn:/sbin/getty -L ttyS1 9600 vt100 +# +# Example how to put a getty on a modem line. +#::respawn:/sbin/getty 57600 ttyS2 + +# Stuff to do when restarting the init process +::restart:/sbin/init + +# Stuff to do before rebooting +::ctrlaltdel:/sbin/reboot +::shutdown:/bin/umount -a -r +::shutdown:/sbin/swapoff -a + diff --git a/busybox/examples/mk2knr.pl b/busybox/examples/mk2knr.pl new file mode 100755 index 000000000..1259b8436 --- /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 'examples/mk2knr.pl files-to-convert' +# - Review the 'convertme.pl' script generated and remove / edit any of the +# substitutions in there (please especially check for false positives) +# - Type './convertme.pl same-files-as-before' +# - Compile and see if it works +# +# BUGS: This script does not ignore strings inside comments or strings inside +# quotes (it probably should). + +# set this to something else if you want +$convertme = 'convertme.pl'; + +# internal-use variables (don't touch) +$convert = 0; +%converted = (); + +# if no files were specified, print usage +die "usage: $0 file.c | file.h\n" if scalar(@ARGV) == 0; + +# prepare the "convert me" file +open(CM, ">$convertme") or die "convertme.pl $!"; +print CM "#!/usr/bin/perl -p -i\n\n"; + +# process each file passed on the cmd line +while (<>) { + + # if the line says "getopt" in it anywhere, we don't want to muck with it + # because option lists tend to include strings like "cxtzvOf:" which get + # matched by the "check for mixed case" regexps below + next if /getopt/; + + # tokenize the string into just the variables + while (/([a-zA-Z_][a-zA-Z0-9_]*)/g) { + $var = $1; + + # ignore the word "BusyBox" + next if ($var =~ /BusyBox/); + + # this checks for javaStyle or szHungarianNotation + $convert++ if ($var =~ /^[a-z]+[A-Z][a-z]+/); + + # this checks for PascalStyle + $convert++ if ($var =~ /^[A-Z][a-z]+[A-Z][a-z]+/); + + # if we want to add more checks, we can add 'em here, but the above + # checks catch "just enough" and not too much, so prolly not. + + if ($convert) { + $convert = 0; + + # skip ahead if we've already dealt with this one + next if ($converted{$var}); + + # record that we've dealt with this var + $converted{$var} = 1; + + print CM "s/\\b$var\\b/"; # more to come in just a minute + + # change the first letter to lower-case + $var = lcfirst($var); + + # put underscores before all remaining upper-case letters + $var =~ s/([A-Z])/_$1/g; + + # now change the remaining characters to lower-case + $var = lc($var); + + print CM "$var/g;\n"; + } + } +} + +# tidy up and make the $convertme script executable +close(CM); +chmod 0755, $convertme; + +# print a helpful help message +print "Done. Scheduled name changes are in $convertme.\n"; +print "Please review/modify it and then type ./$convertme to do the search & replace.\n"; diff --git a/busybox/examples/udhcp/sample.bound b/busybox/examples/udhcp/sample.bound new file mode 100755 index 000000000..2a95d8b7d --- /dev/null +++ b/busybox/examples/udhcp/sample.bound @@ -0,0 +1,31 @@ +#!/bin/sh +# Sample udhcpc renew script + +RESOLV_CONF="/etc/udhcpc/resolv.conf" + +[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" +[ -n "$subnet" ] && NETMASK="netmask $subnet" + +/sbin/ifconfig $interface $ip $BROADCAST $NETMASK + +if [ -n "$router" ] +then + echo "deleting routers" + while /sbin/route del default gw 0.0.0.0 dev $interface + do : + done + + metric=0 + for i in $router + do + /sbin/route add default gw $i dev $interface metric $((metric++)) + done +fi + +echo -n > $RESOLV_CONF +[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF +for i in $dns +do + echo adding dns $i + echo nameserver $i >> $RESOLV_CONF +done \ No newline at end of file diff --git a/busybox/examples/udhcp/sample.deconfig b/busybox/examples/udhcp/sample.deconfig new file mode 100755 index 000000000..b221bcf12 --- /dev/null +++ b/busybox/examples/udhcp/sample.deconfig @@ -0,0 +1,4 @@ +#!/bin/sh +# Sample udhcpc deconfig script + +/sbin/ifconfig $interface 0.0.0.0 diff --git a/busybox/examples/udhcp/sample.nak b/busybox/examples/udhcp/sample.nak new file mode 100755 index 000000000..f4d08e669 --- /dev/null +++ b/busybox/examples/udhcp/sample.nak @@ -0,0 +1,4 @@ +#!/bin/sh +# Sample udhcpc nak script + +echo Received a NAK: $message diff --git a/busybox/examples/udhcp/sample.renew b/busybox/examples/udhcp/sample.renew new file mode 100755 index 000000000..842bafe91 --- /dev/null +++ b/busybox/examples/udhcp/sample.renew @@ -0,0 +1,31 @@ +#!/bin/sh +# Sample udhcpc bound script + +RESOLV_CONF="/etc/udhcpc/resolv.conf" + +[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" +[ -n "$subnet" ] && NETMASK="netmask $subnet" + +/sbin/ifconfig $interface $ip $BROADCAST $NETMASK + +if [ -n "$router" ] +then + echo "deleting routers" + while /sbin/route del default gw 0.0.0.0 dev $interface + do : + done + + metric=0 + for i in $router + do + /sbin/route add default gw $i dev $interface metric $((metric++)) + done +fi + +echo -n > $RESOLV_CONF +[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF +for i in $dns +do + echo adding dns $i + echo nameserver $i >> $RESOLV_CONF +done \ No newline at end of file diff --git a/busybox/examples/udhcp/sample.script b/busybox/examples/udhcp/sample.script new file mode 100644 index 000000000..9b717ac3c --- /dev/null +++ b/busybox/examples/udhcp/sample.script @@ -0,0 +1,7 @@ +#!/bin/sh +# Currently, we only dispatch according to command. However, a more +# elaborate system might dispatch by command and interface or do some +# common initialization first, especially if more dhcp event notifications +# are added. + +exec /usr/share/udhcpc/sample.$1 diff --git a/busybox/examples/udhcp/simple.script b/busybox/examples/udhcp/simple.script new file mode 100644 index 000000000..98ebc159f --- /dev/null +++ b/busybox/examples/udhcp/simple.script @@ -0,0 +1,40 @@ +#!/bin/sh + +# udhcpc script edited by Tim Riker + +[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1 + +RESOLV_CONF="/etc/resolv.conf" +[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" +[ -n "$subnet" ] && NETMASK="netmask $subnet" + +case "$1" in + deconfig) + /sbin/ifconfig $interface 0.0.0.0 + ;; + + renew|bound) + /sbin/ifconfig $interface $ip $BROADCAST $NETMASK + + if [ -n "$router" ] ; then + echo "deleting routers" + while route del default gw 0.0.0.0 dev $interface ; do + : + done + + metric=0 + for i in $router ; do + route add default gw $i dev $interface metric $((metric++)) + done + fi + + echo -n > $RESOLV_CONF + [ -n "$domain" ] && echo search $domain >> $RESOLV_CONF + for i in $dns ; do + echo adding dns $i + echo nameserver $i >> $RESOLV_CONF + done + ;; +esac + +exit 0 diff --git a/busybox/examples/udhcp/udhcpd.conf b/busybox/examples/udhcp/udhcpd.conf new file mode 100644 index 000000000..f91fddebf --- /dev/null +++ b/busybox/examples/udhcp/udhcpd.conf @@ -0,0 +1,123 @@ +# Sample udhcpd configuration file (/etc/udhcpd.conf) + +# The start and end of the IP lease block + +start 192.168.0.20 #default: 192.168.0.20 +end 192.168.0.254 #default: 192.168.0.254 + + +# The interface that udhcpd will use + +interface eth0 #default: eth0 + + +# The maximim number of leases (includes addressesd reserved +# by OFFER's, DECLINE's, and ARP conficts + +#max_leases 254 #default: 254 + + +# If remaining is true (default), udhcpd will store the time +# remaining for each lease in the udhcpd leases file. This is +# for embedded systems that cannot keep time between reboots. +# If you set remaining to no, the absolute time that the lease +# expires at will be stored in the dhcpd.leases file. + +#remaining yes #default: yes + + +# The time period at which udhcpd will write out a dhcpd.leases +# file. If this is 0, udhcpd will never automatically write a +# lease file. (specified in seconds) + +#auto_time 7200 #default: 7200 (2 hours) + + +# The amount of time that an IP will be reserved (leased) for if a +# DHCP decline message is received (seconds). + +#decline_time 3600 #default: 3600 (1 hour) + + +# The amount of time that an IP will be reserved (leased) for if an +# ARP conflct occurs. (seconds + +#conflict_time 3600 #default: 3600 (1 hour) + + +# How long an offered address is reserved (leased) in seconds + +#offer_time 60 #default: 60 (1 minute) + +# If a lease to be given is below this value, the full lease time is +# instead used (seconds). + +#min_lease 60 #defult: 60 + + +# The location of the leases file + +#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases + +# The location of the pid file +#pidfile /var/run/udhcpd.pid #default: /var/run/udhcpd.pid + +# Everytime udhcpd writes a leases file, the below script will be called. +# Useful for writing the lease file to flash every few hours. + +#notify_file #default: (no script) + +#notify_file dumpleases # <--- usefull for debugging + +# The following are bootp specific options, setable by udhcpd. + +#siaddr 192.168.0.22 #default: 0.0.0.0 + +#sname zorak #default: (none) + +#boot_file /var/nfs_root #default: (none) + +# The remainer of options are DHCP options and can be specifed with the +# keyword 'opt' or 'option'. If an option can take multiple items, such +# as the dns option, they can be listed on the same line, or multiple +# lines. The only option with a default is 'lease'. + +#Examles +opt dns 192.168.10.2 192.168.10.10 +option subnet 255.255.255.0 +opt router 192.168.10.2 +opt wins 192.168.10.10 +option dns 129.219.13.81 # appened to above DNS servers for a total of 3 +option domain local +option lease 864000 # 10 days of seconds + + +# Currently supported options, for more info, see options.c +#opt subnet +#opt timezone +#opt router +#opt timesvr +#opt namesvr +#opt dns +#opt logsvr +#opt cookiesvr +#opt lprsvr +#opt bootsize +#opt domain +#opt swapsvr +#opt rootpath +#opt ipttl +#opt mtu +#opt broadcast +#opt wins +#opt lease +#opt ntpsrv +#opt tftp +#opt bootfile + + +# Static leases map +#static_lease 00:60:08:11:CE:4E 192.168.0.54 +#static_lease 00:60:08:11:CE:3E 192.168.0.44 + + diff --git a/busybox/examples/undeb b/busybox/examples/undeb new file mode 100644 index 000000000..37104e9d8 --- /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..7fd3676f6 --- /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/findutils/Config.in b/busybox/findutils/Config.in new file mode 100644 index 000000000..3143bd438 --- /dev/null +++ b/busybox/findutils/Config.in @@ -0,0 +1,133 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Finding Utilities" + +config CONFIG_FIND + bool "find" + default n + help + find is used to search your system to find specified files. + +config CONFIG_FEATURE_FIND_MTIME + bool " Enable modified time matching (-mtime) option" + default y + depends on CONFIG_FIND + help + Allow searching based on the modification time of + files. + +config CONFIG_FEATURE_FIND_PERM + bool " Enable permissions matching (-perm) option" + default y + depends on CONFIG_FIND + help + Enable searching based on file permissions. + +config CONFIG_FEATURE_FIND_TYPE + bool " Enable filetype matching (-type) option" + default y + depends on CONFIG_FIND + help + Enable searching based on file type (file, + directory, socket, device, etc.). + +config CONFIG_FEATURE_FIND_XDEV + bool " Enable stay in filesystem (-xdev) option" + default y + depends on CONFIG_FIND + help + This option will allow find to restrict searches to a single + filesystem. + +config CONFIG_FEATURE_FIND_NEWER + bool " Enable -newer option for comparing file mtimes" + default y + depends on CONFIG_FIND + help + Support the 'find -newer' option for finding any files which have + a modified time that is more recent than the specified FILE. + +config CONFIG_FEATURE_FIND_INUM + bool " Enable inode number matching (-inum) option" + default y + depends on CONFIG_FIND + help + Support the 'find -inum' option for searching by inode number. + +config CONFIG_GREP + bool "grep" + default n + help + grep is used to search files for a specified pattern. + +config CONFIG_FEATURE_GREP_EGREP_ALIAS + bool " Support extended regular expressions (egrep & grep -E)" + default y + depends on CONFIG_GREP + help + Enabled support for extended regular expressions. Extended + regular expressions allow for alternation (foo|bar), grouping, + and various repetition operators. + +config CONFIG_FEATURE_GREP_FGREP_ALIAS + bool " Alias fgrep to grep -f" + default y + depends on CONFIG_GREP + help + fgrep sees the search pattern as a normal string rather than + regular expressions. + grep -f is always builtin, this just creates the fgrep alias. + +config CONFIG_FEATURE_GREP_CONTEXT + bool " Enable before and after context flags (-A, -B and -C)" + default y + depends on CONFIG_GREP + help + Print the specified number of leading (-B) and/or trailing (-A) + context surrounding our matching lines. + Print the specified number of context lines (-C). + +config CONFIG_XARGS + bool "xargs" + default n + help + xargs is used to execute a specified command on + every item from standard input. + +config CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION + bool " Enable prompt and confirmation option -p" + default n + depends on CONFIG_XARGS + help + Support prompt the user about whether to run each command + line and read a line from the terminal. + +config CONFIG_FEATURE_XARGS_SUPPORT_QUOTES + bool " Enable support single and double quotes and backslash" + default n + depends on CONFIG_XARGS + help + Default xargs unsupport single and double quotes + and backslash for can use aruments with spaces. + +config CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT + bool " Enable support options -x" + default n + depends on CONFIG_XARGS + help + Enable support exit if the size (see the -s or -n option) + is exceeded. + +config CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM + bool " Enable options -0" + default n + depends on CONFIG_XARGS + help + Enable input filenames are terminated by a null character + instead of by whitespace, and the quotes and backslash + are not special. + +endmenu diff --git a/busybox/findutils/Makefile b/busybox/findutils/Makefile new file mode 100644 index 000000000..f3f8bb872 --- /dev/null +++ b/busybox/findutils/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/findutils +FINDUTILS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/findutils/Makefile.in b/busybox/findutils/Makefile.in new file mode 100644 index 000000000..ae71070d9 --- /dev/null +++ b/busybox/findutils/Makefile.in @@ -0,0 +1,38 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +FINDUTILS_AR:=findutils.a +ifndef $(FINDUTILS_DIR) +FINDUTILS_DIR:=$(top_builddir)/findutils/ +endif +srcdir=$(top_srcdir)/findutils + +FINDUTILS-y:= +FINDUTILS-$(CONFIG_FIND) += find.o +FINDUTILS-$(CONFIG_GREP) += grep.o +FINDUTILS-$(CONFIG_XARGS) += xargs.o + +libraries-y+=$(FINDUTILS_DIR)$(FINDUTILS_AR) + +$(FINDUTILS_DIR)$(FINDUTILS_AR): $(patsubst %,$(FINDUTILS_DIR)%, $(FINDUTILS-y)) + $(AR) -ro $@ $(patsubst %,$(FINDUTILS_DIR)%, $(FINDUTILS-y)) + +$(FINDUTILS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/findutils/find.c b/busybox/findutils/find.c new file mode 100644 index 000000000..11a838e9f --- /dev/null +++ b/busybox/findutils/find.c @@ -0,0 +1,280 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini find implementation for busybox + * + * Copyright (C) 1999-2004 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" + +//XXX just found out about libbb/messages.c . maybe move stuff there ? - ghoz +const char msg_req_arg[] = "option `%s' requires an argument"; +const char msg_invalid_arg[] = "invalid argument `%s' to `%s'"; + +static char *pattern; + +#ifdef CONFIG_FEATURE_FIND_TYPE +static int type_mask = 0; +#endif + +#ifdef CONFIG_FEATURE_FIND_PERM +static char perm_char = 0; +static int perm_mask = 0; +#endif + +#ifdef CONFIG_FEATURE_FIND_MTIME +static char mtime_char; +static int mtime_days; +#endif + +#ifdef CONFIG_FEATURE_FIND_XDEV +static dev_t *xdev_dev; +static int xdev_count = 0; +#endif + +#ifdef CONFIG_FEATURE_FIND_NEWER +time_t newer_mtime; +#endif + +#ifdef CONFIG_FEATURE_FIND_INUM +static ino_t inode_num; +#endif + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +{ + if (pattern != NULL) { + const char *tmp = strrchr(fileName, '/'); + + if (tmp == NULL) + tmp = fileName; + else + tmp++; + if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0)) + goto no_match; + } +#ifdef CONFIG_FEATURE_FIND_TYPE + if (type_mask != 0) { + if (!((statbuf->st_mode & S_IFMT) == type_mask)) + goto no_match; + } +#endif +#ifdef CONFIG_FEATURE_FIND_PERM + if (perm_mask != 0) { + if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) || + (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) || + (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0))) + goto no_match; + } +#endif +#ifdef CONFIG_FEATURE_FIND_MTIME + if (mtime_char != 0) { + time_t file_age = time(NULL) - statbuf->st_mtime; + time_t mtime_secs = mtime_days * 24 * 60 * 60; + if (!((isdigit(mtime_char) && file_age >= mtime_secs && + file_age < mtime_secs + 24 * 60 * 60) || + (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) || + (mtime_char == '-' && file_age < mtime_secs))) + goto no_match; + } +#endif +#ifdef CONFIG_FEATURE_FIND_XDEV + if (xdev_count) { + int i; + for (i=0; i st_dev) + break; + } + if (i == xdev_count) { + if(S_ISDIR(statbuf->st_mode)) + return SKIP; + else + goto no_match; + } + } +#endif +#ifdef CONFIG_FEATURE_FIND_NEWER + if (newer_mtime != 0) { + time_t file_age = newer_mtime - statbuf->st_mtime; + if (file_age >= 0) + goto no_match; + } +#endif +#ifdef CONFIG_FEATURE_FIND_INUM + if (inode_num != 0) { + if (!(statbuf->st_ino == inode_num)) + goto no_match; + } +#endif + puts(fileName); +no_match: + return (TRUE); +} + +#ifdef CONFIG_FEATURE_FIND_TYPE +static int find_type(char *type) +{ + int mask = 0; + + switch (type[0]) { + case 'b': + mask = S_IFBLK; + break; + case 'c': + mask = S_IFCHR; + break; + case 'd': + mask = S_IFDIR; + break; + case 'p': + mask = S_IFIFO; + break; + case 'f': + mask = S_IFREG; + break; + case 'l': + mask = S_IFLNK; + break; + case 's': + mask = S_IFSOCK; + break; + } + + if (mask == 0 || type[1] != '\0') + bb_error_msg_and_die(msg_invalid_arg, type, "-type"); + + return mask; +} +#endif + +int find_main(int argc, char **argv) +{ + int dereference = FALSE; + int i, firstopt, status = EXIT_SUCCESS; + + for (firstopt = 1; firstopt < argc; firstopt++) { + if (argv[firstopt][0] == '-') + break; + } + + /* Parse any options */ + for (i = firstopt; i < argc; i++) { + if (strcmp(argv[i], "-follow") == 0) + dereference = TRUE; + else if (strcmp(argv[i], "-print") == 0) { + ; + } + else if (strcmp(argv[i], "-name") == 0) { + if (++i == argc) + bb_error_msg_and_die(msg_req_arg, "-name"); + pattern = argv[i]; +#ifdef CONFIG_FEATURE_FIND_TYPE + } else if (strcmp(argv[i], "-type") == 0) { + if (++i == argc) + bb_error_msg_and_die(msg_req_arg, "-type"); + type_mask = find_type(argv[i]); +#endif +#ifdef CONFIG_FEATURE_FIND_PERM + } else if (strcmp(argv[i], "-perm") == 0) { + char *end; + if (++i == argc) + bb_error_msg_and_die(msg_req_arg, "-perm"); + perm_mask = strtol(argv[i], &end, 8); + if ((end[0] != '\0') || (perm_mask > 07777)) + bb_error_msg_and_die(msg_invalid_arg, argv[i], "-perm"); + if ((perm_char = argv[i][0]) == '-') + perm_mask = -perm_mask; +#endif +#ifdef CONFIG_FEATURE_FIND_MTIME + } else if (strcmp(argv[i], "-mtime") == 0) { + char *end; + if (++i == argc) + bb_error_msg_and_die(msg_req_arg, "-mtime"); + mtime_days = strtol(argv[i], &end, 10); + if (end[0] != '\0') + bb_error_msg_and_die(msg_invalid_arg, argv[i], "-mtime"); + if ((mtime_char = argv[i][0]) == '-') + mtime_days = -mtime_days; +#endif +#ifdef CONFIG_FEATURE_FIND_XDEV + } else if (strcmp(argv[i], "-xdev") == 0) { + struct stat stbuf; + + xdev_count = ( firstopt - 1 ) ? ( firstopt - 1 ) : 1; + xdev_dev = xmalloc ( xdev_count * sizeof( dev_t )); + + if ( firstopt == 1 ) { + if ( stat ( ".", &stbuf ) < 0 ) + bb_error_msg_and_die("could not stat '.'" ); + xdev_dev [0] = stbuf. st_dev; + } + else { + + for (i = 1; i < firstopt; i++) { + if ( stat ( argv [i], &stbuf ) < 0 ) + bb_error_msg_and_die("could not stat '%s'", argv [i] ); + xdev_dev [i-1] = stbuf. st_dev; + } + } +#endif +#ifdef CONFIG_FEATURE_FIND_NEWER + } else if (strcmp(argv[i], "-newer") == 0) { + struct stat stat_newer; + if (++i == argc) + bb_error_msg_and_die(msg_req_arg, "-newer"); + if (stat (argv[i], &stat_newer) != 0) + bb_error_msg_and_die("file %s not found", argv[i]); + newer_mtime = stat_newer.st_mtime; +#endif +#ifdef CONFIG_FEATURE_FIND_INUM + } else if (strcmp(argv[i], "-inum") == 0) { + char *end; + if (++i == argc) + bb_error_msg_and_die(msg_req_arg, "-inum"); + inode_num = strtol(argv[i], &end, 10); + if (end[0] != '\0') + bb_error_msg_and_die(msg_invalid_arg, argv[i], "-inum"); +#endif + } else + bb_show_usage(); + } + + if (firstopt == 1) { + if (! recursive_action(".", TRUE, dereference, FALSE, fileAction, + fileAction, NULL)) + status = EXIT_FAILURE; + } else { + for (i = 1; i < firstopt; i++) { + if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction, + fileAction, NULL)) + status = EXIT_FAILURE; + } + } + + return status; +} diff --git a/busybox/findutils/grep.c b/busybox/findutils/grep.c new file mode 100644 index 000000000..29f4ecd4f --- /dev/null +++ b/busybox/findutils/grep.c @@ -0,0 +1,397 @@ +/* + * Mini grep implementation for busybox using libc regex. + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley + * Copyright (C) 1999,2000,2001 by Mark Whitley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * Apr 2004 by Vladimir Oleynik - + * correction "-e pattern1 -e pattern2" logic and more optimizations. +*/ + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +/* options */ +#define GREP_OPTS "lnqvscFiHhe:f:L" +#define GREP_OPT_l (1<<0) +static char print_files_with_matches; +#define GREP_OPT_n (1<<1) +static char print_line_num; +#define GREP_OPT_q (1<<2) +static char be_quiet; +#define GREP_OPT_v (1<<3) +typedef char invert_search_t; +static invert_search_t invert_search; +#define GREP_OPT_s (1<<4) +static char suppress_err_msgs; +#define GREP_OPT_c (1<<5) +static char print_match_counts; +#define GREP_OPT_F (1<<6) +static char fgrep_flag; +#define GREP_OPT_i (1<<7) +#define GREP_OPT_H (1<<8) +#define GREP_OPT_h (1<<9) +#define GREP_OPT_e (1<<10) +#define GREP_OPT_f (1<<11) +#define GREP_OPT_L (1<<12) +static char print_files_without_matches; +#ifdef CONFIG_FEATURE_GREP_CONTEXT +#define GREP_OPT_CONTEXT "A:B:C" +#define GREP_OPT_A (1<<13) +#define GREP_OPT_B (1<<14) +#define GREP_OPT_C (1<<15) +#define GREP_OPT_E (1<<16) +#else +#define GREP_OPT_CONTEXT "" +#define GREP_OPT_E (1<<13) +#endif +#ifdef CONFIG_FEATURE_GREP_EGREP_ALIAS +# define OPT_EGREP "E" +#else +# define OPT_EGREP "" +#endif + +static int reflags; +static int print_filename; + +#ifdef CONFIG_FEATURE_GREP_CONTEXT +static int lines_before; +static int lines_after; +static char **before_buf; +static int last_line_printed; +#endif /* CONFIG_FEATURE_GREP_CONTEXT */ + +/* globals used internally */ +static llist_t *pattern_head; /* growable list of patterns to match */ +static char *cur_file; /* the current file we are reading */ + + +static void print_line(const char *line, int linenum, char decoration) +{ +#ifdef CONFIG_FEATURE_GREP_CONTEXT + /* possibly print the little '--' separator */ + if ((lines_before || lines_after) && last_line_printed && + last_line_printed < linenum - 1) { + puts("--"); + } + last_line_printed = linenum; +#endif + if (print_filename) + printf("%s%c", cur_file, decoration); + if (print_line_num) + printf("%i%c", linenum, decoration); + puts(line); +} + +extern void xregcomp(regex_t *preg, const char *regex, int cflags); + + +static int grep_file(FILE *file) +{ + char *line; + invert_search_t ret; + int linenum = 0; + int nmatches = 0; +#ifdef CONFIG_FEATURE_GREP_CONTEXT + int print_n_lines_after = 0; + int curpos = 0; /* track where we are in the circular 'before' buffer */ + int idx = 0; /* used for iteration through the circular buffer */ +#endif /* CONFIG_FEATURE_GREP_CONTEXT */ + + while ((line = bb_get_chomped_line_from_file(file)) != NULL) { + llist_t *pattern_ptr = pattern_head; + + linenum++; + ret = 0; + while (pattern_ptr) { + if (fgrep_flag) { + ret = strstr(line, pattern_ptr->data) != NULL; + } else { + /* + * test for a postitive-assertion match (regexec returns success (0) + * and the user did not specify invert search), or a negative-assertion + * match (regexec returns failure (REG_NOMATCH) and the user specified + * invert search) + */ + regex_t regex; + xregcomp(®ex, pattern_ptr->data, reflags); + ret |= regexec(®ex, line, 0, NULL, 0) == 0; + regfree(®ex); + } + pattern_ptr = pattern_ptr->link; + } /* while (pattern_ptr) */ + + if ((ret ^ invert_search)) { + + if (print_files_with_matches || be_quiet) + free(line); + + /* if we found a match but were told to be quiet, stop here */ + if (be_quiet || print_files_without_matches) + return -1; + + /* keep track of matches */ + nmatches++; + + /* if we're just printing filenames, we stop after the first match */ + if (print_files_with_matches) + break; + + /* print the matched line */ + if (print_match_counts == 0) { +#ifdef CONFIG_FEATURE_GREP_CONTEXT + int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1; + + /* if we were told to print 'before' lines and there is at least + * one line in the circular buffer, print them */ + if (lines_before && before_buf[prevpos] != NULL) { + int first_buf_entry_line_num = linenum - lines_before; + + /* advance to the first entry in the circular buffer, and + * figure out the line number is of the first line in the + * buffer */ + idx = curpos; + while (before_buf[idx] == NULL) { + idx = (idx + 1) % lines_before; + first_buf_entry_line_num++; + } + + /* now print each line in the buffer, clearing them as we go */ + while (before_buf[idx] != NULL) { + print_line(before_buf[idx], first_buf_entry_line_num, '-'); + free(before_buf[idx]); + before_buf[idx] = NULL; + idx = (idx + 1) % lines_before; + first_buf_entry_line_num++; + } + } + + /* make a note that we need to print 'after' lines */ + print_n_lines_after = lines_after; +#endif /* CONFIG_FEATURE_GREP_CONTEXT */ + print_line(line, linenum, ':'); + } + } +#ifdef CONFIG_FEATURE_GREP_CONTEXT + else { /* no match */ + /* Add the line to the circular 'before' buffer */ + if(lines_before) { + free(before_buf[curpos]); + before_buf[curpos] = bb_xstrdup(line); + curpos = (curpos + 1) % lines_before; + } + } + + /* if we need to print some context lines after the last match, do so */ + if (print_n_lines_after && (last_line_printed != linenum)) { + print_line(line, linenum, '-'); + print_n_lines_after--; + } +#endif /* CONFIG_FEATURE_GREP_CONTEXT */ + free(line); + } + + + /* special-case file post-processing for options where we don't print line + * matches, just filenames and possibly match counts */ + + /* grep -c: print [filename:]count, even if count is zero */ + if (print_match_counts) { + if (print_filename) + printf("%s:", cur_file); + printf("%d\n", nmatches); + } + + /* grep -l: print just the filename, but only if we grepped the line in the file */ + if (print_files_with_matches && nmatches > 0) { + puts(cur_file); + } + + /* grep -L: print just the filename, but only if we didn't grep the line in the file */ + if (print_files_without_matches && nmatches == 0) { + puts(cur_file); + } + + return nmatches; +} + +static void load_regexes_from_file(llist_t *fopt) +{ + char *line; + FILE *f; + + while(fopt) { + llist_t *cur = fopt; + char *ffile = cur->data; + + fopt = cur->link; + free(cur); + f = bb_xfopen(ffile, "r"); + while ((line = bb_get_chomped_line_from_file(f)) != NULL) { + pattern_head = llist_add_to(pattern_head, line); + } + } +} + + +extern int grep_main(int argc, char **argv) +{ + FILE *file; + int matched; + unsigned long opt; + llist_t *fopt = NULL; + + /* do normal option parsing */ +#ifdef CONFIG_FEATURE_GREP_CONTEXT + { + char *junk; + char *slines_after; + char *slines_before; + char *Copt; + + bb_opt_complementaly = "H-h:e*:f*:C-AB"; + opt = bb_getopt_ulflags(argc, argv, + GREP_OPTS GREP_OPT_CONTEXT OPT_EGREP, + &pattern_head, &fopt, + &slines_after, &slines_before, &Copt); + + if(opt & GREP_OPT_C) { + /* C option unseted A and B options, but next -A or -B + may be ovewrite own option */ + if(!(opt & GREP_OPT_A)) /* not overwtited */ + slines_after = Copt; + if(!(opt & GREP_OPT_B)) /* not overwtited */ + slines_before = Copt; + opt |= GREP_OPT_A|GREP_OPT_B; /* set for parse now */ + } + if(opt & GREP_OPT_A) { + lines_after = strtoul(slines_after, &junk, 10); + if(*junk != '\0') + bb_error_msg_and_die("invalid context length argument"); + } + if(opt & GREP_OPT_B) { + lines_before = strtoul(slines_before, &junk, 10); + if(*junk != '\0') + bb_error_msg_and_die("invalid context length argument"); + } + /* sanity checks after parse may be invalid numbers ;-) */ + if ((opt & (GREP_OPT_c|GREP_OPT_q|GREP_OPT_l|GREP_OPT_L))) { + opt &= ~GREP_OPT_n; + lines_before = 0; + lines_after = 0; + } else if(lines_before > 0) + before_buf = (char **)xcalloc(lines_before, sizeof(char *)); + } +#else + /* with auto sanity checks */ + bb_opt_complementaly = "H-h:e*:f*:c-n:q-n:l-n"; + opt = bb_getopt_ulflags(argc, argv, GREP_OPTS OPT_EGREP, + &pattern_head, &fopt); + +#endif + print_files_with_matches = opt & GREP_OPT_l; + print_files_without_matches = (opt & GREP_OPT_L) != 0; + print_line_num = opt & GREP_OPT_n; + be_quiet = opt & GREP_OPT_q; + invert_search = (opt & GREP_OPT_v) != 0; /* 0 | 1 */ + suppress_err_msgs = opt & GREP_OPT_s; + print_match_counts = opt & GREP_OPT_c; + fgrep_flag = opt & GREP_OPT_F; + if(opt & GREP_OPT_H) + print_filename++; + if(opt & GREP_OPT_h) + print_filename--; + if(opt & GREP_OPT_f) + load_regexes_from_file(fopt); + +#ifdef CONFIG_FEATURE_GREP_EGREP_ALIAS + if(bb_applet_name[0] == 'e' || (opt & GREP_OPT_E)) + reflags = REG_EXTENDED | REG_NOSUB; + else +#endif + reflags = REG_NOSUB; + + if(opt & GREP_OPT_i) + reflags |= REG_ICASE; + + argv += optind; + argc -= optind; + + /* if we didn't get a pattern from a -e and no command file was specified, + * argv[optind] should be the pattern. no pattern, no worky */ + if (pattern_head == NULL) { + if (*argv == NULL) + bb_show_usage(); + else { + pattern_head = llist_add_to(pattern_head, *argv++); + argc--; + } + } + + /* argv[(optind)..(argc-1)] should be names of file to grep through. If + * there is more than one file to grep, we will print the filenames */ + if (argc > 1) { + print_filename++; + + /* If no files were specified, or '-' was specified, take input from + * stdin. Otherwise, we grep through all the files specified. */ + } else if (argc == 0) { + argc++; + } + matched = 0; + while (argc--) { + cur_file = *argv++; + if(!cur_file || (*cur_file == '-' && !cur_file[1])) { + cur_file = "-"; + file = stdin; + } else { + file = fopen(cur_file, "r"); + } + if (file == NULL) { + if (!suppress_err_msgs) + bb_perror_msg("%s", cur_file); + } else { + matched += grep_file(file); + if(matched < 0) { + /* we found a match but were told to be quiet, stop here and + * return success */ + break; + } + fclose(file); + } + } + +#ifdef CONFIG_FEATURE_CLEAN_UP + /* destroy all the elments in the pattern list */ + while (pattern_head) { + llist_t *pattern_head_ptr = pattern_head; + + pattern_head = pattern_head->link; + free(pattern_head_ptr); + } +#endif + + return !matched; /* invert return value 0 = success, 1 = failed */ +} diff --git a/busybox/findutils/xargs.c b/busybox/findutils/xargs.c new file mode 100644 index 000000000..1a4347828 --- /dev/null +++ b/busybox/findutils/xargs.c @@ -0,0 +1,586 @@ +/* + * Mini xargs implementation for busybox + * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]" + * + * (C) 2002,2003 by Vladimir Oleynik + * + * Special thanks + * - Mark Whitley and Glenn McGrath for stimulus to rewrite :) + * - Mike Rendell + * and David MacKenzie . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * xargs is described in the Single Unix Specification v3 at + * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* COMPAT: SYSV version defaults size (and has a max value of) to 470. + We try to make it as large as possible. */ +#if !defined(ARG_MAX) && defined(_SC_ARG_MAX) +#define ARG_MAX sysconf (_SC_ARG_MAX) +#endif +#ifndef ARG_MAX +#define ARG_MAX 470 +#endif + + +#ifdef TEST +# ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +# define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +# endif +# ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES +# define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES +# endif +# ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT +# define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT +# endif +# ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM +# define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM +# endif +#endif + +/* + This function have special algorithm. + Don`t use fork and include to main! +*/ +static int xargs_exec(char *const *args) +{ + pid_t p; + volatile int exec_errno = 0; /* shared vfork stack */ + + if ((p = vfork()) >= 0) { + if (p == 0) { + /* vfork -- child */ + execvp(args[0], args); + exec_errno = errno; /* set error to shared stack */ + _exit(1); + } else { + /* vfork -- parent */ + int status; + + while (wait(&status) == (pid_t) - 1) + if (errno != EINTR) + break; + if (exec_errno) { + errno = exec_errno; + bb_perror_msg("%s", args[0]); + return exec_errno == ENOENT ? 127 : 126; + } else { + if (WEXITSTATUS(status) == 255) { + bb_error_msg("%s: exited with status 255; aborting", args[0]); + return 124; + } + if (WIFSTOPPED(status)) { + bb_error_msg("%s: stopped by signal %d", + args[0], WSTOPSIG(status)); + return 125; + } + if (WIFSIGNALED(status)) { + bb_error_msg("%s: terminated by signal %d", + args[0], WTERMSIG(status)); + return 125; + } + if (WEXITSTATUS(status) != 0) + return 123; + return 0; + } + } + } else { + bb_perror_msg_and_die("vfork"); + } +} + + +typedef struct xlist_s { + char *data; + size_t lenght; + struct xlist_s *link; +} xlist_t; + +static int eof_stdin_detected; + +#define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \ + || (c) == '\f' || (c) == '\v') + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES +static xlist_t *process_stdin(xlist_t * list_arg, + const char *eof_str, size_t mc, char *buf) +{ +#define NORM 0 +#define QUOTE 1 +#define BACKSLASH 2 +#define SPACE 4 + + char *s = NULL; /* start word */ + char *p = NULL; /* pointer to end word */ + char q = 0; /* quote char */ + char state = NORM; + char eof_str_detected = 0; + size_t line_l = 0; /* size loaded args line */ + int c; /* current char */ + xlist_t *cur; + xlist_t *prev; + + for (prev = cur = list_arg; cur; cur = cur->link) { + line_l += cur->lenght; /* previous allocated */ + if (prev != cur) + prev = prev->link; + } + + while (!eof_stdin_detected) { + c = getchar(); + if (c == EOF) { + eof_stdin_detected++; + if (s) + goto unexpected_eof; + break; + } + if (eof_str_detected) + continue; + if (state == BACKSLASH) { + state = NORM; + goto set; + } else if (state == QUOTE) { + if (c == q) { + q = 0; + state = NORM; + } else { + goto set; + } + } else { /* if(state == NORM) */ + + if (ISSPACE(c)) { + if (s) { +unexpected_eof: + state = SPACE; + c = 0; + goto set; + } + } else { + if (s == NULL) + s = p = buf; + if (c == '\\') { + state = BACKSLASH; + } else if (c == '\'' || c == '"') { + q = c; + state = QUOTE; + } else { +set: + if ((p - buf) >= mc) + bb_error_msg_and_die("argument line too long"); + *p++ = c; + } + } + } + if (state == SPACE) { /* word's delimiter or EOF detected */ + if (q) { + bb_error_msg_and_die("unmatched %s quote", + q == '\'' ? "single" : "double"); + } + /* word loaded */ + if (eof_str) { + eof_str_detected = strcmp(s, eof_str) == 0; + } + if (!eof_str_detected) { + size_t lenght = (p - buf); + + cur = xmalloc(sizeof(xlist_t) + lenght); + cur->data = memcpy(cur + 1, s, lenght); + cur->lenght = lenght; + cur->link = NULL; + if (prev == NULL) { + list_arg = cur; + } else { + prev->link = cur; + } + prev = cur; + line_l += lenght; + if (line_l > mc) { + /* stop memory usage :-) */ + break; + } + } + s = NULL; + state = NORM; + } + } + return list_arg; +} +#else +/* The variant does not support single quotes, double quotes or backslash */ +static xlist_t *process_stdin(xlist_t * list_arg, + const char *eof_str, size_t mc, char *buf) +{ + + int c; /* current char */ + int eof_str_detected = 0; + char *s = NULL; /* start word */ + char *p = NULL; /* pointer to end word */ + size_t line_l = 0; /* size loaded args line */ + xlist_t *cur; + xlist_t *prev; + + for (prev = cur = list_arg; cur; cur = cur->link) { + line_l += cur->lenght; /* previous allocated */ + if (prev != cur) + prev = prev->link; + } + + while (!eof_stdin_detected) { + c = getchar(); + if (c == EOF) { + eof_stdin_detected++; + } + if (eof_str_detected) + continue; + if (c == EOF || ISSPACE(c)) { + if (s == NULL) + continue; + c = EOF; + } + if (s == NULL) + s = p = buf; + if ((p - buf) >= mc) + bb_error_msg_and_die("argument line too long"); + *p++ = c == EOF ? 0 : c; + if (c == EOF) { /* word's delimiter or EOF detected */ + /* word loaded */ + if (eof_str) { + eof_str_detected = strcmp(s, eof_str) == 0; + } + if (!eof_str_detected) { + size_t lenght = (p - buf); + + cur = xmalloc(sizeof(xlist_t) + lenght); + cur->data = memcpy(cur + 1, s, lenght); + cur->lenght = lenght; + cur->link = NULL; + if (prev == NULL) { + list_arg = cur; + } else { + prev->link = cur; + } + prev = cur; + line_l += lenght; + if (line_l > mc) { + /* stop memory usage :-) */ + break; + } + s = NULL; + } + } + } + return list_arg; +} +#endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */ + + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +/* Prompt the user for a response, and + if the user responds affirmatively, return true; + otherwise, return false. Used "/dev/tty", not stdin. */ +static int xargs_ask_confirmation(void) +{ + static FILE *tty_stream; + int c, savec; + + if (!tty_stream) { + tty_stream = fopen("/dev/tty", "r"); + if (!tty_stream) + bb_perror_msg_and_die("/dev/tty"); + /* pranoidal security by vodz */ + fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC); + } + fputs(" ?...", stderr); + fflush(stderr); + c = savec = getc(tty_stream); + while (c != EOF && c != '\n') + c = getc(tty_stream); + if (savec == 'y' || savec == 'Y') + return 1; + return 0; +} + +# define OPT_INC_P 1 +#else +# define OPT_INC_P 0 +# define xargs_ask_confirmation() 1 +#endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */ + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT +# define OPT_INC_X 1 +#else +# define OPT_INC_X 0 +#endif + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM +static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str, + size_t mc, char *buf) +{ + int c; /* current char */ + char *s = NULL; /* start word */ + char *p = NULL; /* pointer to end word */ + size_t line_l = 0; /* size loaded args line */ + xlist_t *cur; + xlist_t *prev; + + for (prev = cur = list_arg; cur; cur = cur->link) { + line_l += cur->lenght; /* previous allocated */ + if (prev != cur) + prev = prev->link; + } + + while (!eof_stdin_detected) { + c = getchar(); + if (c == EOF) { + eof_stdin_detected++; + if (s == NULL) + break; + c = 0; + } + if (s == NULL) + s = p = buf; + if ((p - buf) >= mc) + bb_error_msg_and_die("argument line too long"); + *p++ = c; + if (c == 0) { /* word's delimiter or EOF detected */ + /* word loaded */ + size_t lenght = (p - buf); + + cur = xmalloc(sizeof(xlist_t) + lenght); + cur->data = memcpy(cur + 1, s, lenght); + cur->lenght = lenght; + cur->link = NULL; + if (prev == NULL) { + list_arg = cur; + } else { + prev->link = cur; + } + prev = cur; + line_l += lenght; + if (line_l > mc) { + /* stop memory usage :-) */ + break; + } + s = NULL; + } + } + return list_arg; +} + +# define READ_ARGS(l, e, nmc, mc) (*read_args)(l, e, nmc, mc) +# define OPT_INC_0 1 /* future use */ +#else +# define OPT_INC_0 0 /* future use */ +# define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc) +#endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */ + + +#define OPT_VERBOSE (1<<0) +#define OPT_NO_EMPTY (1<<1) +#define OPT_UPTO_NUMBER (1<<2) +#define OPT_UPTO_SIZE (1<<3) +#define OPT_EOF_STRING (1<<4) +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +#define OPT_INTERACTIVE (1<<5) +#else +#define OPT_INTERACTIVE (0) /* require for algorithm &| */ +#endif +#define OPT_TERMINATE (1<<(5+OPT_INC_P)) +#define OPT_ZEROTERM (1<<(5+OPT_INC_P+OPT_INC_X)) +/* next future +#define OPT_NEXT_OTHER (1<<(5+OPT_INC_P+OPT_INC_X+OPT_INC_0)) +*/ + +int xargs_main(int argc, char **argv) +{ + char **args; + int i, a, n; + xlist_t *list = NULL; + xlist_t *cur; + int child_error = 0; + char *max_args, *max_chars; + int n_max_arg; + size_t n_chars = 0; + long orig_arg_max; + const char *eof_str = "_"; + unsigned long opt; + size_t n_max_chars; + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM + xlist_t *(*read_args) (xlist_t *, const char *, size_t, char *) = process_stdin; +#endif + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION + bb_opt_complementaly = "pt"; +#endif + + opt = bb_getopt_ulflags(argc, argv, "+trn:s:e::" +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION + "p" +#endif +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT + "x" +#endif +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM + "0" +#endif + ,&max_args, &max_chars, &eof_str); + + a = argc - optind; + argv += optind; + if (a == 0) { + /* default behavior is to echo all the filenames */ + *argv = "echo"; + a++; + } + + orig_arg_max = ARG_MAX; + if (orig_arg_max == -1) + orig_arg_max = LONG_MAX; + orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048. */ + if ((opt & OPT_UPTO_SIZE)) { + n_max_chars = bb_xgetularg10_bnd(max_chars, 1, orig_arg_max); + for (i = 0; i < a; i++) { + n_chars += strlen(*argv) + 1; + } + if (n_max_chars < n_chars) { + bb_error_msg_and_die("can not fit single argument within argument list size limit"); + } + n_max_chars -= n_chars; + } else { + /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which + have it at 1 meg). Things will work fine with a large ARG_MAX but it + will probably hurt the system more than it needs to; an array of this + size is allocated. */ + if (orig_arg_max > 20 * 1024) + orig_arg_max = 20 * 1024; + n_max_chars = orig_arg_max; + } + max_chars = xmalloc(n_max_chars); + + if ((opt & OPT_UPTO_NUMBER)) { + n_max_arg = bb_xgetularg10_bnd(max_args, 1, INT_MAX); + } else { + n_max_arg = n_max_chars; + } + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM + if (opt & OPT_ZEROTERM) + read_args = process0_stdin; +#endif + + while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL || + (opt & OPT_NO_EMPTY) == 0) + { + opt |= OPT_NO_EMPTY; + n = 0; + n_chars = 0; +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT + for (cur = list; cur;) { + n_chars += cur->lenght; + n++; + cur = cur->link; + if (n_chars > n_max_chars || (n == n_max_arg && cur)) { + if (opt & OPT_TERMINATE) + bb_error_msg_and_die("argument list too long"); + break; + } + } +#else + for (cur = list; cur; cur = cur->link) { + n_chars += cur->lenght; + n++; + if (n_chars > n_max_chars || n == n_max_arg) { + break; + } + } +#endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */ + + /* allocating pointers for execvp: + a*arg, n*arg from stdin, NULL */ + args = xcalloc(n + a + 1, sizeof(char *)); + + /* Store the command to be executed + (taken from the command line) */ + for (i = 0; i < a; i++) + args[i] = argv[i]; + /* (taken from stdin) */ + for (cur = list; n; cur = cur->link) { + args[i++] = cur->data; + n--; + } + + if ((opt & (OPT_INTERACTIVE | OPT_VERBOSE))) { + for (i = 0; args[i]; i++) { + if (i) + fputc(' ', stderr); + fputs(args[i], stderr); + } + if ((opt & OPT_INTERACTIVE) == 0) + fputc('\n', stderr); + } + if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) { + child_error = xargs_exec(args); + } + + /* clean up */ + for (i = a; args[i]; i++) { + cur = list; + list = list->link; + free(cur); + } + free(args); + if (child_error > 0 && child_error != 123) { + break; + } + } +#ifdef CONFIG_FEATURE_CLEAN_UP + free(max_chars); +#endif + return child_error; +} + + +#ifdef TEST + +const char *bb_applet_name = "debug stuff usage"; + +void bb_show_usage(void) +{ + fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n", + bb_applet_name); + exit(1); +} + +int main(int argc, char **argv) +{ + return xargs_main(argc, argv); +} +#endif /* TEST */ diff --git a/busybox/include/.cvsignore b/busybox/include/.cvsignore new file mode 100644 index 000000000..9c68c9576 --- /dev/null +++ b/busybox/include/.cvsignore @@ -0,0 +1,2 @@ +config +config.h diff --git a/busybox/include/applets.h b/busybox/include/applets.h new file mode 100644 index 000000000..90d4195cc --- /dev/null +++ b/busybox/include/applets.h @@ -0,0 +1,678 @@ +/* + * applets.h - a listing of all busybox applets. + * + * If you write a new applet, you need to add an entry to this list to make + * busybox aware of it. + * + * It is CRUCIAL that this listing be kept in ascii order, otherwise the binary + * search lookup contributed by Gaute B Strokkenes stops working. If you value + * your kneecaps, you'll be sure to *make sure* that any changes made to this + * file result in the listing remaining in ascii order. You have been warned. + */ + +#undef APPLET +#undef APPLET_ODDNAME +#undef APPLET_NOUSAGE + + +#if defined(PROTOTYPES) + #define APPLET(a,b,c,d) extern int b(int argc, char **argv); + #define APPLET_NOUSAGE(a,b,c,d) extern int b(int argc, char **argv); + #define APPLET_ODDNAME(a,b,c,d,e) extern int b(int argc, char **argv); + extern const char usage_messages[]; +#elif defined(MAKE_USAGE) + #ifdef CONFIG_FEATURE_VERBOSE_USAGE + #define APPLET(a,b,c,d) a##_trivial_usage "\n\n" a##_full_usage "\0" + #define APPLET_NOUSAGE(a,b,c,d) "\b\0" + #define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\n\n" e##_full_usage "\0" + #else + #define APPLET(a,b,c,d) a##_trivial_usage "\0" + #define APPLET_NOUSAGE(a,b,c,d) "\b\0" + #define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\0" + #endif +#elif defined(MAKE_LINKS) +# define APPLET(a,b,c,d) LINK c a +# define APPLET_NOUSAGE(a,b,c,d) LINK c a +# define APPLET_ODDNAME(a,b,c,d,e) LINK c a +#else + const struct BB_applet applets[] = { + #define APPLET(a,b,c,d) {#a,b,c,d}, + #define APPLET_NOUSAGE(a,b,c,d) {a,b,c,d}, + #define APPLET_ODDNAME(a,b,c,d,e) {a,b,c,d}, +#endif + +#ifdef CONFIG_INSTALL_NO_USR +#define _BB_DIR_USR_BIN _BB_DIR_BIN +#define _BB_DIR_USR_SBIN _BB_DIR_SBIN +#endif + + + +#ifdef CONFIG_TEST + APPLET_NOUSAGE("[", test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ADDGROUP + APPLET(addgroup, addgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ADDUSER + APPLET(adduser, adduser_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ADJTIMEX + APPLET(adjtimex, adjtimex_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_AR + APPLET(ar, ar_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ARPING + APPLET(arping, arping_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ASH + APPLET_NOUSAGE("ash", ash_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_AWK + APPLET(awk, awk_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_BASENAME + APPLET(basename, basename_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_BUNZIP2 + APPLET(bunzip2, bunzip2_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif + APPLET_NOUSAGE("busybox", busybox_main, _BB_DIR_BIN, _BB_SUID_MAYBE) +#ifdef CONFIG_BUNZIP2 + APPLET(bzcat, bunzip2_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CAL + APPLET(cal, cal_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CAT + APPLET(cat, cat_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CHGRP + APPLET(chgrp, chgrp_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CHMOD + APPLET(chmod, chmod_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CHOWN + APPLET(chown, chown_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CHROOT + APPLET(chroot, chroot_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CHVT + APPLET(chvt, chvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CLEAR + APPLET(clear, clear_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CMP + APPLET(cmp, cmp_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CP + APPLET(cp, cp_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CPIO + APPLET(cpio, cpio_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CROND + APPLET(crond, crond_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_CRONTAB + APPLET(crontab, crontab_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS) +#endif +#ifdef CONFIG_CUT + APPLET(cut, cut_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DATE + APPLET(date, date_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DC + APPLET(dc, dc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DD + APPLET(dd, dd_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DEALLOCVT + APPLET(deallocvt, deallocvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DELGROUP + APPLET(delgroup, delgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DELUSER + APPLET(deluser, deluser_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DEVFSD + APPLET(devfsd, devfsd_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DF + APPLET(df, df_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DIRNAME + APPLET(dirname, dirname_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DMESG + APPLET(dmesg, dmesg_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DOS2UNIX + APPLET(dos2unix, dos2unix_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DPKG + APPLET(dpkg, dpkg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DPKG_DEB + APPLET_ODDNAME("dpkg-deb", dpkg_deb_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER, dpkg_deb) +#endif +#ifdef CONFIG_DU + APPLET(du, du_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DUMPKMAP + APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_DUMPLEASES + APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ECHO + APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS) + APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ENV + APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_EXPR + APPLET(expr, expr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FALSE + APPLET(false, false_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FBSET + APPLET(fbset, fbset_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FDFLUSH + APPLET(fdflush, fdflush_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FDFORMAT + APPLET(fdformat, fdformat_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FDISK + APPLET(fdisk, fdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#if defined(CONFIG_FEATURE_GREP_FGREP_ALIAS) + APPLET_NOUSAGE("fgrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FIND + APPLET(find, find_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FOLD + APPLET(fold, fold_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FREE + APPLET(free, free_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FREERAMDISK + APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FSCK_MINIX + APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix) +#endif +#ifdef CONFIG_FTPGET + APPLET(ftpget, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FTPPUT + APPLET(ftpput, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_GETOPT + APPLET(getopt, getopt_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_GETTY + APPLET(getty, getty_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_GREP + APPLET(grep, grep_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_GUNZIP + APPLET(gunzip, gunzip_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_GZIP + APPLET(gzip, gzip_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HALT + APPLET(halt, halt_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HDPARM + APPLET(hdparm, hdparm_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HEAD + APPLET(head, head_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HEXDUMP + APPLET(hexdump, hexdump_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HOSTID + APPLET(hostid, hostid_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HOSTNAME + APPLET(hostname, hostname_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HTTPD + APPLET(httpd, httpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HUSH + APPLET_NOUSAGE("hush", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_HWCLOCK + APPLET(hwclock, hwclock_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ID + APPLET(id, id_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IFCONFIG + APPLET(ifconfig, ifconfig_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IFUPDOWN + APPLET(ifdown, ifupdown_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IFUPDOWN + APPLET(ifup, ifupdown_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_INETD + APPLET(inetd, inetd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_INIT + APPLET(init, init_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_INSMOD + APPLET(insmod, insmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_INSTALL + APPLET(install, install_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IP + APPLET(ip, ip_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IPADDR + APPLET(ipaddr, ipaddr_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IPCALC + APPLET(ipcalc, ipcalc_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IPLINK + APPLET(iplink, iplink_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IPROUTE + APPLET(iproute, iproute_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_IPTUNNEL + APPLET(iptunnel, iptunnel_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_KILL + APPLET(kill, kill_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_KILLALL + APPLET(killall, kill_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_KLOGD + APPLET(klogd, klogd_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LASH + APPLET(lash, lash_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LAST + APPLET(last, last_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LENGTH + APPLET(length, length_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_FEATURE_INITRD + APPLET_NOUSAGE("linuxrc", init_main, _BB_DIR_ROOT, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LN + APPLET(ln, ln_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LOADFONT + APPLET(loadfont, loadfont_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LOADKMAP + APPLET(loadkmap, loadkmap_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LOGGER + APPLET(logger, logger_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LOGIN + APPLET(login, login_main, _BB_DIR_BIN, _BB_SUID_ALWAYS) +#endif +#ifdef CONFIG_LOGNAME + APPLET(logname, logname_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LOGREAD + APPLET(logread, logread_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LOSETUP + APPLET(losetup, losetup_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LS + APPLET(ls, ls_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_LSMOD + APPLET(lsmod, lsmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MAKEDEVS + APPLET(makedevs, makedevs_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MD5SUM + APPLET(md5sum, md5sum_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MESG + APPLET(mesg, mesg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MKDIR + APPLET(mkdir, mkdir_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MKFIFO + APPLET(mkfifo, mkfifo_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MKFS_MINIX + APPLET_ODDNAME("mkfs.minix", mkfs_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, mkfs_minix) +#endif +#ifdef CONFIG_MKNOD + APPLET(mknod, mknod_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MKSWAP + APPLET(mkswap, mkswap_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MKTEMP + APPLET(mktemp, mktemp_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MODPROBE + APPLET(modprobe, modprobe_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MORE + APPLET(more, more_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MOUNT + APPLET(mount, mount_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MSH + APPLET_NOUSAGE("msh", msh_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MT + APPLET(mt, mt_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_MV + APPLET(mv, mv_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_NAMEIF + APPLET(nameif, nameif_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_NC + APPLET(nc, nc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_NETSTAT + APPLET(netstat, netstat_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_NSLOOKUP + APPLET(nslookup, nslookup_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_OD + APPLET(od, od_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_OPENVT + APPLET(openvt, openvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_PASSWD + APPLET(passwd, passwd_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS) +#endif +#ifdef CONFIG_PATCH + APPLET(patch, patch_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_PIDOF + APPLET(pidof, pidof_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_PING + APPLET(ping, ping_main, _BB_DIR_BIN, _BB_SUID_MAYBE) +#endif +#ifdef CONFIG_PING6 + APPLET(ping6, ping6_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_PIPE_PROGRESS + APPLET_NOUSAGE("pipe_progress", pipe_progress_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_PIVOT_ROOT + APPLET(pivot_root, pivot_root_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_POWEROFF + APPLET(poweroff, poweroff_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_PRINTF + APPLET(printf, printf_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_PS + APPLET(ps, ps_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_PWD + APPLET(pwd, pwd_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RDATE + APPLET(rdate, rdate_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_READLINK + APPLET(readlink, readlink_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_REALPATH + APPLET(realpath, realpath_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_REBOOT + APPLET(reboot, reboot_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RENICE + APPLET(renice, renice_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RESET + APPLET(reset, reset_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RM + APPLET(rm, rm_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RMDIR + APPLET(rmdir, rmdir_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RMMOD + APPLET(rmmod, rmmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_ROUTE + APPLET(route, route_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RPM + APPLET(rpm, rpm_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RPM2CPIO + APPLET(rpm2cpio, rpm2cpio_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_RUN_PARTS + APPLET_ODDNAME("run-parts", run_parts_main, _BB_DIR_BIN, _BB_SUID_NEVER, run_parts) +#endif +#ifdef CONFIG_RX + APPLET(rx, rx_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SED + APPLET(sed, sed_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SEQ + APPLET(seq, seq_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SETKEYCODES + APPLET(setkeycodes, setkeycodes_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#if defined(CONFIG_FEATURE_SH_IS_ASH) && defined(CONFIG_ASH) + APPLET_NOUSAGE("sh", ash_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#elif defined(CONFIG_FEATURE_SH_IS_HUSH) && defined(CONFIG_HUSH) + APPLET_NOUSAGE("sh", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#elif defined(CONFIG_FEATURE_SH_IS_LASH) && defined(CONFIG_LASH) + APPLET_NOUSAGE("sh", lash_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#elif defined(CONFIG_FEATURE_SH_IS_MSH) && defined(CONFIG_MSH) + APPLET_NOUSAGE("sh", msh_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SHA1SUM + APPLET(sha1sum, sha1sum_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SLEEP + APPLET(sleep, sleep_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SORT + APPLET(sort, sort_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_START_STOP_DAEMON + APPLET_ODDNAME("start-stop-daemon", start_stop_daemon_main, _BB_DIR_SBIN, _BB_SUID_NEVER, start_stop_daemon) +#endif +#ifdef CONFIG_STRINGS + APPLET(strings, strings_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_STTY + APPLET(stty, stty_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SU + APPLET(su, su_main, _BB_DIR_BIN, _BB_SUID_ALWAYS) +#endif +#ifdef CONFIG_SULOGIN + APPLET(sulogin, sulogin_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SWAPONOFF + APPLET(swapoff, swap_on_off_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SWAPONOFF + APPLET(swapon, swap_on_off_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SYNC + APPLET(sync, sync_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SYSCTL + APPLET(sysctl, sysctl_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_SYSLOGD + APPLET(syslogd, syslogd_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TAIL + APPLET(tail, tail_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TAR + APPLET(tar, tar_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TEE + APPLET(tee, tee_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TELNET + APPLET(telnet, telnet_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TELNETD + APPLET(telnetd, telnetd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TEST + APPLET(test, test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TFTP + APPLET(tftp, tftp_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TIME + APPLET(time, time_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TOP + APPLET(top, top_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TOUCH + APPLET(touch, touch_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TR + APPLET(tr, tr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TRACEROUTE + APPLET(traceroute, traceroute_main, _BB_DIR_USR_BIN, _BB_SUID_MAYBE) +#endif +#ifdef CONFIG_TRUE + APPLET(true, true_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_TTY + APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UDHCPC + APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UDHCPD + APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UMOUNT + APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UNAME + APPLET(uname, uname_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UNCOMPRESS + APPLET(uncompress, uncompress_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UNIQ + APPLET(uniq, uniq_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UNIX2DOS + APPLET(unix2dos, dos2unix_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UNZIP + APPLET(unzip, unzip_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UPTIME + APPLET(uptime, uptime_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_USLEEP + APPLET(usleep, usleep_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UUDECODE + APPLET(uudecode, uudecode_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_UUENCODE + APPLET(uuencode, uuencode_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_VCONFIG + APPLET(vconfig, vconfig_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_VI + APPLET(vi, vi_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_VLOCK + APPLET(vlock, vlock_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS) +#endif +#ifdef CONFIG_WATCH + APPLET(watch, watch_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_WATCHDOG + APPLET(watchdog, watchdog_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_WC + APPLET(wc, wc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_WGET + APPLET(wget, wget_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_WHICH + APPLET(which, which_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_WHO + APPLET(who, who_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_WHOAMI + APPLET(whoami, whoami_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_XARGS + APPLET(xargs, xargs_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_YES + APPLET(yes, yes_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) +#endif +#ifdef CONFIG_GUNZIP + APPLET(zcat, gunzip_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif + +#if !defined(PROTOTYPES) && !defined(MAKE_USAGE) + { 0,NULL,0 } +}; + +#endif diff --git a/busybox/include/busybox.h b/busybox/include/busybox.h new file mode 100644 index 000000000..f6f575957 --- /dev/null +++ b/busybox/include/busybox.h @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +/* + * Busybox main internal header file + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + */ +#ifndef _BB_INTERNAL_H_ +#define _BB_INTERNAL_H_ 1 + +#include "config.h" + +#include +#include +#include +#include +#include + +#if __GNU_LIBRARY__ < 5 +#ifndef __dietlibc__ +#error "Sorry, libc5 is not supported" +#endif +#endif + +#ifndef BB_EXTRA_VERSION +#define BB_BANNER "BusyBox v" BB_VER " (" BB_BT ")" +#else +#define BB_BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")" +#endif + +#ifdef DMALLOC +#include +#endif + +#include + +/* Pull in the utility routines from libbb */ +#include "libbb.h" + +enum Location { + _BB_DIR_ROOT = 0, + _BB_DIR_BIN, + _BB_DIR_SBIN, + _BB_DIR_USR_BIN, + _BB_DIR_USR_SBIN +}; + +enum SUIDRoot { + _BB_SUID_NEVER = 0, + _BB_SUID_MAYBE, + _BB_SUID_ALWAYS +}; + +struct BB_applet { + const char *name; + int (*main) (int argc, char **argv); + enum Location location:4; + enum SUIDRoot need_suid:4; +}; + +/* From busybox.c */ +extern const struct BB_applet applets[]; + +/* Automagically pull in all the applet function prototypes and + * applet usage strings. These are all of the form: + * extern int foo_main(int argc, char **argv); + * extern const char foo_usage[]; + * These are all autogenerated from the set of currently defined applets. + */ +#define PROTOTYPES +#include "applets.h" +#undef PROTOTYPES + +#ifdef CONFIG_FEATURE_BUFFERS_GO_ON_STACK +#define RESERVE_CONFIG_BUFFER(buffer,len) char buffer[len] +#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char buffer[len] +#define RELEASE_CONFIG_BUFFER(buffer) ((void)0) +#else +#ifdef CONFIG_FEATURE_BUFFERS_GO_IN_BSS +#define RESERVE_CONFIG_BUFFER(buffer,len) static char buffer[len] +#define RESERVE_CONFIG_UBUFFER(buffer,len) static unsigned char buffer[len] +#define RELEASE_CONFIG_BUFFER(buffer) ((void)0) +#else +#define RESERVE_CONFIG_BUFFER(buffer,len) char *buffer=xmalloc(len) +#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char *buffer=xmalloc(len) +#define RELEASE_CONFIG_BUFFER(buffer) free (buffer) +#endif +#endif + + +#ifndef RB_POWER_OFF +/* Stop system and switch power off if possible. */ +#define RB_POWER_OFF 0x4321fedc +#endif + +/* Try to pull in PATH_MAX */ +#include + +/* for PATH_MAX on systems that don't have it in limits.h */ +#include +#ifndef PATH_MAX +#define PATH_MAX 256 +#endif + +#endif /* _BB_INTERNAL_H_ */ diff --git a/busybox/include/dump.h b/busybox/include/dump.h new file mode 100644 index 000000000..3f4b480b2 --- /dev/null +++ b/busybox/include/dump.h @@ -0,0 +1,49 @@ +#define F_IGNORE 0x01 /* %_A */ +#define F_SETREP 0x02 /* rep count set, not default */ +#define F_ADDRESS 0x001 /* print offset */ +#define F_BPAD 0x002 /* blank pad */ +#define F_C 0x004 /* %_c */ +#define F_CHAR 0x008 /* %c */ +#define F_DBL 0x010 /* %[EefGf] */ +#define F_INT 0x020 /* %[di] */ +#define F_P 0x040 /* %_p */ +#define F_STR 0x080 /* %s */ +#define F_U 0x100 /* %_u */ +#define F_UINT 0x200 /* %[ouXx] */ +#define F_TEXT 0x400 /* no conversions */ + +enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */ + +typedef struct _pr { + struct _pr *nextpr; /* next print unit */ + unsigned int flags; /* flag values */ + int bcnt; /* byte count */ + char *cchar; /* conversion character */ + char *fmt; /* printf format */ + char *nospace; /* no whitespace version */ +} PR; + +typedef struct _fu { + struct _fu *nextfu; /* next format unit */ + struct _pr *nextpr; /* next print unit */ + unsigned int flags; /* flag values */ + int reps; /* repetition count */ + int bcnt; /* byte count */ + char *fmt; /* format string */ +} FU; + +typedef struct _fs { /* format strings */ + struct _fs *nextfs; /* linked list of format strings */ + struct _fu *nextfu; /* linked list of format units */ + int bcnt; +} FS; + +extern void bb_dump_add(const char *fmt); +extern int bb_dump_dump (char **argv); +extern int bb_dump_size(FS * fs); + +extern FS *bb_dump_fshead; /* head of format strings */ +extern int bb_dump_blocksize; /* data block size */ +extern int bb_dump_length; /* max bytes to read */ +extern enum _vflag bb_dump_vflag; +extern off_t bb_dump_skip; /* bytes to skip */ diff --git a/busybox/include/grp_.h b/busybox/include/grp_.h new file mode 100644 index 000000000..b212b0b4a --- /dev/null +++ b/busybox/include/grp_.h @@ -0,0 +1,116 @@ +/* Copyright (C) 1991,92,95,96,97,98,99,2000,01 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * POSIX Standard: 9.2.1 Group Database Access + */ + + +#if !defined CONFIG_USE_BB_PWD_GRP +#include + +#else + +#ifndef _GRP_H +#define _GRP_H 1 + + +#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. */ +}; + + +/* Rewind the group-file stream. */ +extern void setgrent (void); + +/* Close the group-file stream. */ +extern void endgrent (void); + +/* Read an entry from the group-file stream, opening it if necessary. */ +extern struct group *getgrent (void); + +/* Read a group entry from STREAM. */ +extern struct group *fgetgrent (FILE *__stream); + +/* Write the given entry onto the given stream. */ +extern int putgrent (__const struct group *__restrict __p, + FILE *__restrict __f); + +/* Search for an entry with a matching group ID. */ +extern struct group *getgrgid (gid_t __gid); + +/* Search for an entry with a matching group name. */ +extern struct group *getgrnam (__const char *__name); + +/* Reentrant versions of some of the functions above. + + PLEASE NOTE: the `getgrent_r' function is not (yet) standardized. + The interface may change in later versions of this library. But + the interface is designed following the principals used for the + other reentrant functions so the chances are good this is what the + POSIX people would choose. */ + +extern int getgrent_r (struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result); + +/* Search for an entry with a matching group ID. */ +extern int getgrgid_r (gid_t __gid, struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result); + +/* Search for an entry with a matching group name. */ +extern int getgrnam_r (__const char *__restrict __name, + struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result); + +/* Read a group entry from STREAM. This function is not standardized + an probably never will. */ +extern int fgetgrent_r (FILE *__restrict __stream, + struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result); + +/* Set the group set for the current user to GROUPS (N of them). */ +extern int setgroups (size_t __n, __const gid_t *__groups); + +/* Store at most *NGROUPS members of the group set for USER into + *GROUPS. Also include GROUP. The actual number of groups found is + returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */ +extern int getgrouplist (__const char *__user, gid_t __group, + gid_t *__groups, int *__ngroups); + +/* Initialize the group set for the current user + by reading the group database and using all groups + of which USER is a member. Also include GROUP. */ +extern int initgroups (__const char *__user, gid_t __group); + + +#endif /* grp.h */ +#endif diff --git a/busybox/include/inet_common.h b/busybox/include/inet_common.h new file mode 100644 index 000000000..afea5deaa --- /dev/null +++ b/busybox/include/inet_common.h @@ -0,0 +1,33 @@ +/* + * stolen from net-tools-1.59 and stripped down for busybox by + * Erik Andersen + * + * Heavily modified by Manuel Novoa III Mar 12, 2001 + * + * Version: $Id: inet_common.h,v 1.4 2004/03/10 07:42:37 mjn3 Exp $ + * + */ + +#include +#include +#include +#include + + +extern const char bb_INET_default[]; /* = "default" */ + +/* hostfirst!=0 If we expect this to be a hostname, + try hostname database first + */ +extern int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst); + + +/* numeric: & 0x8000: default instead of *, + * & 0x4000: host instead of net, + * & 0x0fff: don't resolve + */ +extern int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in, + int numeric, unsigned int netmask); + +extern int INET6_resolve(const char *name, struct sockaddr_in6 *sin6); +extern int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, int numeric); diff --git a/busybox/include/libbb.h b/busybox/include/libbb.h new file mode 100644 index 000000000..93ab5375c --- /dev/null +++ b/busybox/include/libbb.h @@ -0,0 +1,487 @@ +/* vi: set sw=4 ts=4: */ +/* + * Busybox main internal header file + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + */ +#ifndef __LIBCONFIG_H__ +#define __LIBCONFIG_H__ 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef DMALLOC +#include +#endif + +#include + +#include "config.h" +#ifdef CONFIG_SELINUX +#include +#endif + +#include "pwd_.h" +#include "grp_.h" +#ifdef CONFIG_FEATURE_SHADOWPASSWDS +#include "shadow_.h" +#endif +#ifdef CONFIG_FEATURE_SHA1_PASSWORDS +# include "sha1.h" +#endif + +/* Convenience macros to test the version of gcc. */ +#if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +# define __GNUC_PREREQ(maj, min) 0 +#endif + +/* __restrict is known in EGCS 1.2 and above. */ +#if !__GNUC_PREREQ (2,92) +# define __restrict /* Ignore */ +#endif + +/* Some useful definitions */ +#define FALSE ((int) 0) +#define TRUE ((int) 1) +#define SKIP ((int) 2) + +/* for mtab.c */ +#define MTAB_GETMOUNTPT '1' +#define MTAB_GETDEVICE '2' + +#define BUF_SIZE 8192 +#define EXPAND_ALLOC 1024 + +/* Macros for min/max. */ +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +extern void bb_show_usage(void) __attribute__ ((noreturn)); +extern void bb_error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); +extern void bb_error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))); +extern void bb_perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); +extern void bb_perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))); +extern void bb_vherror_msg(const char *s, va_list p); +extern void bb_herror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); +extern void bb_herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))); + +extern void bb_perror_nomsg_and_die(void) __attribute__ ((noreturn)); +extern void bb_perror_nomsg(void); + +/* These two are used internally -- you shouldn't need to use them */ +extern void bb_verror_msg(const char *s, va_list p) __attribute__ ((format (printf, 1, 0))); +extern void bb_vperror_msg(const char *s, va_list p) __attribute__ ((format (printf, 1, 0))); + +extern const char *bb_mode_string(int mode); +extern int is_directory(const char *name, int followLinks, struct stat *statBuf); + +extern int remove_file(const char *path, int flags); +extern int copy_file(const char *source, const char *dest, int flags); +extern ssize_t safe_read(int fd, void *buf, size_t count); +extern ssize_t bb_full_read(int fd, void *buf, size_t len); +extern ssize_t safe_write(int fd, const void *buf, size_t count); +extern ssize_t bb_full_write(int fd, const void *buf, size_t len); +extern int recursive_action(const char *fileName, int recurse, + int followLinks, int depthFirst, + int (*fileAction) (const char *fileName, struct stat* statbuf, void* userData), + int (*dirAction) (const char *fileName, struct stat* statbuf, void* userData), + void* userData); + +extern int bb_parse_mode( const char* s, mode_t* theMode); +extern long bb_xgetlarg(const char *arg, int base, long lower, long upper); + +extern unsigned long bb_baud_to_value(speed_t speed); +extern speed_t bb_value_to_baud(unsigned long value); + +extern int get_kernel_revision(void); + +extern int get_console_fd(void); +extern struct mntent *find_mount_point(const char *name, const char *table); +extern void write_mtab(char* blockDevice, char* directory, + char* filesystemType, long flags, char* string_flags); +extern void erase_mtab(const char * name); +extern long *find_pid_by_name( const char* pidName); +extern char *find_real_root_device_name(void); +extern char *bb_get_line_from_file(FILE *file); +extern char *bb_get_chomped_line_from_file(FILE *file); +extern int bb_copyfd_size(int fd1, int fd2, const off_t size); +extern int bb_copyfd_eof(int fd1, int fd2); +extern void bb_xprint_and_close_file(FILE *file); +extern int bb_xprint_file_by_name(const char *filename); +extern char bb_process_escape_sequence(const char **ptr); +extern char *bb_get_last_path_component(char *path); +extern FILE *bb_wfopen(const char *path, const char *mode); +extern FILE *bb_xfopen(const char *path, const char *mode); + +//#warning rename? +extern int bb_fclose_nonstdin(FILE *f); +extern void bb_fflush_stdout_and_exit(int retval) __attribute__ ((noreturn)); + +extern const char *bb_opt_complementaly; +extern const struct option *bb_applet_long_options; +extern unsigned long bb_getopt_ulflags(int argc, char **argv, const char *applet_opts, ...); + +//#warning rename? +extern FILE *bb_wfopen_input(const char *filename); + +extern int bb_vfprintf(FILE * __restrict stream, const char * __restrict format, + va_list arg) __attribute__ ((format (printf, 2, 0))); +extern int bb_vprintf(const char * __restrict format, va_list arg) + __attribute__ ((format (printf, 1, 0))); +extern int bb_fprintf(FILE * __restrict stream, const char * __restrict format, ...) + __attribute__ ((format (printf, 2, 3))); +extern int bb_printf(const char * __restrict format, ...) + __attribute__ ((format (printf, 1, 2))); + +//#warning rename to xferror_filename? +extern void bb_xferror(FILE *fp, const char *fn); +extern void bb_xferror_stdout(void); +extern void bb_xfflush_stdout(void); + +extern void bb_warn_ignoring_args(int n); + +extern void chomp(char *s); +extern void trim(char *s); +extern const char *bb_skip_whitespace(const char *); + +extern struct BB_applet *find_applet_by_name(const char *name); +void run_applet_by_name(const char *name, int argc, char **argv); + +//#warning is this needed anymore? +#ifndef DMALLOC +extern void *xmalloc (size_t size); +extern void *xrealloc(void *old, size_t size); +extern void *xcalloc(size_t nmemb, size_t size); +#endif +extern char *bb_xstrdup (const char *s); +extern char *bb_xstrndup (const char *s, int n); +extern char *safe_strncpy(char *dst, const char *src, size_t size); +extern int safe_strtoi(char *arg, int* value); +extern int safe_strtod(char *arg, double* value); +extern int safe_strtol(char *arg, long* value); +extern int safe_strtoul(char *arg, unsigned long* value); + +struct suffix_mult { + const char *suffix; + unsigned int mult; +}; + +extern unsigned long bb_xgetularg_bnd_sfx(const char *arg, int base, + unsigned long lower, + unsigned long upper, + const struct suffix_mult *suffixes); +extern unsigned long bb_xgetularg_bnd(const char *arg, int base, + unsigned long lower, + unsigned long upper); +extern unsigned long bb_xgetularg10_bnd(const char *arg, + unsigned long lower, + unsigned long upper); +extern unsigned long bb_xgetularg10(const char *arg); + +extern long bb_xgetlarg_bnd_sfx(const char *arg, int base, + long lower, + long upper, + const struct suffix_mult *suffixes); +extern long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes); + + +//#warning pitchable now? +extern unsigned long bb_xparse_number(const char *numstr, + const struct suffix_mult *suffixes); + + +//#warning change names? + +/* These parse entries in /etc/passwd and /etc/group. This is desirable + * for BusyBox since we want to avoid using the glibc NSS stuff, which + * increases target size and is often not needed embedded systems. */ +extern long my_getpwnam(const char *name); +extern long my_getgrnam(const char *name); +extern char * my_getug(char *buffer, char *idname, long id, int bufsize, char prefix); +extern char * my_getpwuid(char *name, long uid, int bufsize); +extern char * my_getgrgid(char *group, long gid, int bufsize); +extern char *bb_askpass(int timeout, const char * prompt); + +extern int device_open(const char *device, int mode); + +extern int del_loop(const char *device); +extern int set_loop(const char *device, const char *file, int offset, int *loopro); +extern char *find_unused_loop_device (void); + + +#if (__GLIBC__ < 2) +extern int vdprintf(int d, const char *format, va_list ap); +#endif + +int nfsmount(const char *spec, const char *node, int *flags, + char **extra_opts, char **mount_opts, int running_bg); + +/* Include our own copy of struct sysinfo to avoid binary compatability + * problems with Linux 2.4, which changed things. Grumble, grumble. */ +struct sysinfo { + long uptime; /* Seconds since boot */ + unsigned long loads[3]; /* 1, 5, and 15 minute load averages */ + unsigned long totalram; /* Total usable main memory size */ + unsigned long freeram; /* Available memory size */ + unsigned long sharedram; /* Amount of shared memory */ + unsigned long bufferram; /* Memory used by buffers */ + unsigned long totalswap; /* Total swap space size */ + unsigned long freeswap; /* swap space still available */ + unsigned short procs; /* Number of current processes */ + unsigned short pad; /* Padding needed for m68k */ + unsigned long totalhigh; /* Total high memory size */ + unsigned long freehigh; /* Available high memory size */ + unsigned int mem_unit; /* Memory unit size in bytes */ + char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */ +}; +extern int sysinfo (struct sysinfo* info); + +enum { + KILOBYTE = 1024, + MEGABYTE = (KILOBYTE*1024), + GIGABYTE = (MEGABYTE*1024) +}; +const char *make_human_readable_str(unsigned long long size, + unsigned long block_size, unsigned long display_unit); + +int bb_ask_confirmation(void); +int klogctl(int type, char * b, int len); + +char *xgetcwd(char *cwd); +char *xreadlink(const char *path); +char *concat_path_file(const char *path, const char *filename); +char *concat_subpath_file(const char *path, const char *filename); +char *last_char_is(const char *s, int c); + +int read_package_field(const char *package_buffer, char **field_name, char **field_value); +//#warning yuk! +char *fgets_str(FILE *file, const char *terminating_string); + +extern int uncompress(int fd_in, int fd_out); +extern int inflate(int in, int out); + +extern struct hostent *xgethostbyname(const char *name); +extern struct hostent *xgethostbyname2(const char *name, int af); +extern int create_icmp_socket(void); +extern int create_icmp6_socket(void); +extern int xconnect(struct sockaddr_in *s_addr); +extern unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port); +extern void bb_lookup_host(struct sockaddr_in *s_in, const char *host); + +//#warning wrap this? +char *dirname (char *path); + +int bb_make_directory (char *path, long mode, int flags); + +const char *u_signal_names(const char *str_sig, int *signo, int startnum); +char *bb_simplify_path(const char *path); + +enum { /* DO NOT CHANGE THESE VALUES! cp.c depends on them. */ + FILEUTILS_PRESERVE_STATUS = 1, + FILEUTILS_DEREFERENCE = 2, + FILEUTILS_RECUR = 4, + FILEUTILS_FORCE = 8, + FILEUTILS_INTERACTIVE = 16 +}; + +extern const char *bb_applet_name; + +extern const char * const bb_msg_full_version; +extern const char * const bb_msg_memory_exhausted; +extern const char * const bb_msg_invalid_date; +extern const char * const bb_msg_io_error; +extern const char * const bb_msg_write_error; +extern const char * const bb_msg_name_longer_than_foo; +extern const char * const bb_msg_unknown; +extern const char * const bb_msg_can_not_create_raw_socket; +extern const char * const bb_msg_perm_denied_are_you_root; +extern const char * const bb_msg_standard_input; +extern const char * const bb_msg_standard_output; + +extern const char * const bb_path_nologin_file; +extern const char * const bb_path_passwd_file; +extern const char * const bb_path_shadow_file; +extern const char * const bb_path_gshadow_file; +extern const char * const bb_path_group_file; +extern const char * const bb_path_securetty_file; +extern const char * const bb_path_motd_file; + +/* + * You can change LIBBB_DEFAULT_LOGIN_SHELL, but don`t use, + * use bb_default_login_shell and next defines, + * if you LIBBB_DEFAULT_LOGIN_SHELL change, + * don`t lose change increment constant! + */ +#define LIBBB_DEFAULT_LOGIN_SHELL "-/bin/sh" + +extern const char * const bb_default_login_shell; +/* "/bin/sh" */ +#define DEFAULT_SHELL (bb_default_login_shell+1) +/* "sh" */ +#define DEFAULT_SHELL_SHORT_NAME (bb_default_login_shell+6) + + +extern const char bb_path_mtab_file[]; + +extern int bb_default_error_retval; + +#ifdef CONFIG_FEATURE_DEVFS +# define CURRENT_VC "/dev/vc/0" +# define VC_1 "/dev/vc/1" +# define VC_2 "/dev/vc/2" +# define VC_3 "/dev/vc/3" +# define VC_4 "/dev/vc/4" +# define VC_5 "/dev/vc/5" +#if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__) +/* Yes, this sucks, but both SH (including sh64) and H8 have a SCI(F) for their + respective serial ports .. as such, we can't use the common device paths for + these. -- PFM */ +# define SC_0 "/dev/ttsc/0" +# define SC_1 "/dev/ttsc/1" +# define SC_FORMAT "/dev/ttsc/%d" +#else +# define SC_0 "/dev/tts/0" +# define SC_1 "/dev/tts/1" +# define SC_FORMAT "/dev/tts/%d" +#endif +# define VC_FORMAT "/dev/vc/%d" +# define LOOP_FORMAT "/dev/loop/%d" +# define FB_0 "/dev/fb/0" +#else +# define CURRENT_VC "/dev/tty0" +# define VC_1 "/dev/tty1" +# define VC_2 "/dev/tty2" +# define VC_3 "/dev/tty3" +# define VC_4 "/dev/tty4" +# define VC_5 "/dev/tty5" +#if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__) +# define SC_0 "/dev/ttySC0" +# define SC_1 "/dev/ttySC1" +# define SC_FORMAT "/dev/ttySC%d" +#else +# define SC_0 "/dev/ttyS0" +# define SC_1 "/dev/ttyS1" +# define SC_FORMAT "/dev/ttyS%d" +#endif +# define VC_FORMAT "/dev/tty%d" +# define LOOP_FORMAT "/dev/loop%d" +# define FB_0 "/dev/fb0" +#endif + +//#warning put these in .o files + +/* The following devices are the same on devfs and non-devfs systems. */ +#define CURRENT_TTY "/dev/tty" +#define CONSOLE_DEV "/dev/console" + +int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name); +void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name); +void reset_ino_dev_hashtable(void); + +/* Stupid gcc always includes its own builtin strlen()... */ +extern size_t bb_strlen(const char *string); +#define strlen(x) bb_strlen(x) + +void bb_xasprintf(char **string_ptr, const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + +#define FAIL_DELAY 3 +extern void change_identity ( const struct passwd *pw ); +extern const char *change_identity_e2str ( const struct passwd *pw ); +extern void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args +#ifdef CONFIG_SELINUX + , security_id_t sid +#endif +); +extern int run_parts(char **args, const unsigned char test_mode, char **env); +extern int restricted_shell ( const char *shell ); +extern void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ); +extern int correct_password ( const struct passwd *pw ); +extern char *pw_encrypt(const char *clear, const char *salt); +extern struct spwd *pwd_to_spwd(const struct passwd *pw); +extern int obscure(const char *old, const char *newval, const struct passwd *pwdp); + +extern int bb_xopen(const char *pathname, int flags); +extern ssize_t bb_xread(int fd, void *buf, size_t count); +extern void bb_xread_all(int fd, void *buf, size_t count); +extern unsigned char bb_xread_char(int fd); + +typedef struct { + int pid; + char user[9]; + char state[4]; + unsigned long rss; + int ppid; +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + unsigned pcpu; + unsigned long stime, utime; +#endif + char *cmd; + + /* basename of executable file in call to exec(2), + size from kernel headers */ + char short_cmd[16]; +} procps_status_t; + +extern procps_status_t * procps_scan(int save_user_arg0 +#ifdef CONFIG_SELINUX + , int use_selinux, security_id_t *sid +#endif +); +extern unsigned short compare_string_array(const char *string_array[], const char *key); + +extern int my_query_module(const char *name, int which, void **buf, size_t *bufsize, size_t *ret); + +typedef struct llist_s { + char *data; + struct llist_s *link; +} llist_t; +extern llist_t *llist_add_to(llist_t *old_head, char *new_item); + +extern void print_login_issue(const char *issue_file, const char *tty); +extern void print_login_prompt(void); + +extern void vfork_daemon_rexec(int nochdir, int noclose, + int argc, char **argv, char *foreground_opt); +extern void get_terminal_width_height(int fd, int *width, int *height); +extern unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *)); +extern void xregcomp(regex_t *preg, const char *regex, int cflags); + +#define HASH_SHA1 1 +#define HASH_MD5 2 +extern int hash_fd(int fd, const size_t size, const uint8_t hash_algo, uint8_t *hashval); + +#endif /* __LIBCONFIG_H__ */ diff --git a/busybox/include/pwd_.h b/busybox/include/pwd_.h new file mode 100644 index 000000000..72151203e --- /dev/null +++ b/busybox/include/pwd_.h @@ -0,0 +1,106 @@ +/* Copyright (C) 1991,92,95,96,97,98,99,2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * POSIX Standard: 9.2.2 User Database Access + */ + +#if !defined CONFIG_USE_BB_PWD_GRP +#include + +#else + +#ifndef _PWD_H +#define _PWD_H 1 + +#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. */ +}; + + +/* Rewind the password-file stream. */ +extern void setpwent (void); + +/* Close the password-file stream. */ +extern void endpwent (void); + +/* Read an entry from the password-file stream, opening it if necessary. */ +extern struct passwd *getpwent (void); + +/* Read an entry from STREAM. */ +extern struct passwd *fgetpwent (FILE *__stream); + +/* Write the given entry onto the given stream. */ +extern int putpwent (__const struct passwd *__restrict __p, + FILE *__restrict __f); + +/* Search for an entry with a matching user ID. */ +extern struct passwd *getpwuid (uid_t __uid); + +/* Search for an entry with a matching username. */ +extern struct passwd *getpwnam (__const char *__name); + +/* Reentrant versions of some of the functions above. + + PLEASE NOTE: the `getpwent_r' function is not (yet) standardized. + The interface may change in later versions of this library. But + the interface is designed following the principals used for the + other reentrant functions so the chances are good this is what the + POSIX people would choose. */ + +extern int getpwent_r (struct passwd *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct passwd **__restrict __result); + +extern int getpwuid_r (uid_t __uid, + struct passwd *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct passwd **__restrict __result); + +extern int getpwnam_r (__const char *__restrict __name, + struct passwd *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct passwd **__restrict __result); + + +/* Read an entry from STREAM. This function is not standardized and + probably never will. */ +extern int fgetpwent_r (FILE *__restrict __stream, + struct passwd *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct passwd **__restrict __result); + +/* Re-construct the password-file line for the given uid + in the given buffer. This knows the format that the caller + will expect, but this need not be the format of the password file. */ +extern int getpw (uid_t __uid, char *__buffer); + +#endif /* pwd.h */ +#endif diff --git a/busybox/include/shadow_.h b/busybox/include/shadow_.h new file mode 100644 index 000000000..1b14f0a7b --- /dev/null +++ b/busybox/include/shadow_.h @@ -0,0 +1,98 @@ +/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Declaration of types and functions for shadow password suite. */ + +#if !defined CONFIG_USE_BB_SHADOW +#include +#else + +#ifndef _SHADOW_H +#define _SHADOW_H 1 + +#include + +/* Paths to the user database files. */ +#ifndef _PATH_SHADOW +#define _PATH_SHADOW "/etc/shadow" +#endif +#define SHADOW _PATH_SHADOW + + +/* Structure of the password file. */ +struct spwd +{ + char *sp_namp; /* Login name. */ + char *sp_pwdp; /* Encrypted password. */ + long int sp_lstchg; /* Date of last change. */ + long int sp_min; /* Minimum number of days between changes. */ + long int sp_max; /* Maximum number of days between changes. */ + long int sp_warn; /* Number of days to warn user to change + the password. */ + long int sp_inact; /* Number of days the account may be + inactive. */ + long int sp_expire; /* Number of days since 1970-01-01 until + account expires. */ + unsigned long int sp_flag; /* Reserved. */ +}; + + +/* Open database for reading. */ +extern void setspent (void); + +/* Close database. */ +extern void endspent (void); + +/* Get next entry from database, perhaps after opening the file. */ +extern struct spwd *getspent (void); + +/* Get shadow entry matching NAME. */ +extern struct spwd *getspnam (__const char *__name); + +/* Read shadow entry from STRING. */ +extern struct spwd *sgetspent (__const char *__string); + +/* Read next shadow entry from STREAM. */ +extern struct spwd *fgetspent (FILE *__stream); + +/* Write line containing shadow password entry to stream. */ +extern int putspent (__const struct spwd *__p, FILE *__stream); + +/* Reentrant versions of some of the functions above. */ +extern int getspent_r (struct spwd *__result_buf, char *__buffer, + size_t __buflen, struct spwd **__result); + +extern int getspnam_r (__const char *__name, struct spwd *__result_buf, + char *__buffer, size_t __buflen, + struct spwd **__result)__THROW; + +extern int sgetspent_r (__const char *__string, struct spwd *__result_buf, + char *__buffer, size_t __buflen, + struct spwd **__result); + +extern int fgetspent_r (FILE *__stream, struct spwd *__result_buf, + char *__buffer, size_t __buflen, + struct spwd **__result); +/* Protect password file against multi writers. */ +extern int lckpwdf (void); + +/* Unlock password file. */ +extern int ulckpwdf (void); + +#endif /* shadow.h */ +#endif diff --git a/busybox/include/unarchive.h b/busybox/include/unarchive.h new file mode 100644 index 000000000..1679b73ab --- /dev/null +++ b/busybox/include/unarchive.h @@ -0,0 +1,107 @@ +#ifndef __UNARCHIVE_H__ +#define __UNARCHIVE_H__ + +#define ARCHIVE_PRESERVE_DATE 1 +#define ARCHIVE_CREATE_LEADING_DIRS 2 +#define ARCHIVE_EXTRACT_UNCONDITIONAL 4 +#define ARCHIVE_EXTRACT_QUIET 8 +#define ARCHIVE_EXTRACT_NEWER 16 + +#include +#include +#include "libbb.h" + +typedef struct file_headers_s { + char *name; + char *link_name; + off_t size; + uid_t uid; + gid_t gid; + mode_t mode; + time_t mtime; + dev_t device; +} file_header_t; + +typedef struct archive_handle_s { + /* define if the header and data compenent should processed */ + char (*filter)(struct archive_handle_s *); + llist_t *accept; + llist_t *reject; + llist_t *passed; /* List of files that have successfully been worked on */ + + /* Contains the processed header entry */ + file_header_t *file_header; + + /* process the header component, e.g. tar -t */ + void (*action_header)(const file_header_t *); + + /* process the data component, e.g. extract to filesystem */ + void (*action_data)(struct archive_handle_s *); + + /* How to process any sub archive, e.g. get_header_tar_gz */ + char (*action_data_subarchive)(struct archive_handle_s *); + + /* Contains the handle to a sub archive */ + struct archive_handle_s *sub_archive; + + /* The raw stream as read from disk or stdin */ + int src_fd; + + /* Count the number of bytes processed */ + off_t offset; + + /* Function that skips data: read_by_char or read_by_skip */ + void (*seek)(const struct archive_handle_s *archive_handle, const unsigned int amount); + + /* Temporary storage */ + char *buffer; + + /* Misc. stuff */ + unsigned char flags; + +} archive_handle_t; + +extern archive_handle_t *init_handle(void); + +extern char filter_accept_all(archive_handle_t *archive_handle); +extern char filter_accept_list(archive_handle_t *archive_handle); +extern char filter_accept_list_reassign(archive_handle_t *archive_handle); +extern char filter_accept_reject_list(archive_handle_t *archive_handle); + +extern void unpack_ar_archive(archive_handle_t *ar_archive); + +extern void data_skip(archive_handle_t *archive_handle); +extern void data_extract_all(archive_handle_t *archive_handle); +extern void data_extract_to_stdout(archive_handle_t *archive_handle); +extern void data_extract_to_buffer(archive_handle_t *archive_handle); + +extern void header_skip(const file_header_t *file_header); +extern void header_list(const file_header_t *file_header); +extern void header_verbose_list(const file_header_t *file_header); + +extern void check_header_gzip(int src_fd); + +extern char get_header_ar(archive_handle_t *archive_handle); +extern char get_header_cpio(archive_handle_t *archive_handle); +extern char get_header_tar(archive_handle_t *archive_handle); +extern char get_header_tar_bz2(archive_handle_t *archive_handle); +extern char get_header_tar_gz(archive_handle_t *archive_handle); + +extern void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount); +extern void seek_by_char(const archive_handle_t *archive_handle, const unsigned int amount); + +extern void archive_xread_all(const archive_handle_t *archive_handle, void *buf, const size_t count); +extern ssize_t archive_xread_all_eof(archive_handle_t *archive_handle, unsigned char *buf, size_t count); + +extern void data_align(archive_handle_t *archive_handle, const unsigned short boundary); +extern const llist_t *find_list_entry(const llist_t *list, const char *filename); + +extern int uncompressStream(int src_fd, int dst_fd); +extern void inflate_init(unsigned int bufsize); +extern int inflate_unzip(int in, int out); +extern int inflate_gunzip(int in, int out); + +extern int open_transformer(int src_fd, int (*transformer)(int src_fd, int dst_fd)); + + +#endif diff --git a/busybox/include/usage.h b/busybox/include/usage.h new file mode 100644 index 000000000..377eb10e7 --- /dev/null +++ b/busybox/include/usage.h @@ -0,0 +1,2885 @@ +#ifndef __BB_USAGE_H__ +#define __BB_USAGE_H__ + +#define addgroup_trivial_usage \ + "[-g GID] group_name [user_name]" +#define addgroup_full_usage \ + "Adds a group to the system\n\n" \ + "Options:\n" \ + "\t-g GID\t\tspecify gid" + +#define adduser_trivial_usage \ + "[OPTIONS] user_name" +#define adduser_full_usage \ + "Adds a user to the system\n\n" \ + "Options:\n" \ + "\t-h DIR\t\tAssign home directory DIR\n" \ + "\t-g GECOS\tAssign gecos field GECOS\n" \ + "\t-s SHELL\tAssign login shell SHELL\n" \ + "\t-G\t\tAdd the user to existing group GROUP\n" \ + "\t-S\t\tcreate a system user (ignored)\n" \ + "\t-D\t\tDo not assign a password (logins still possible via ssh)\n" \ + "\t-H\t\tDo not create the home directory" + +#define adjtimex_trivial_usage \ + "[-q] [-o offset] [-f frequency] [-p timeconstant] [-t tick]" +#define adjtimex_full_usage \ + "Reads and optionally sets system timebase parameters.\n" \ + "See adjtimex(2).\n\n" \ + "Options:\n" \ + "\t-q\t\tquiet mode - do not print\n" \ + "\t-o offset\ttime offset, microseconds\n" \ + "\t-f frequency\tfrequency adjust, integer kernel units (65536 is 1ppm)\n" \ + "\t\t\t(positive values make the system clock run fast)\n" \ + "\t-t tick\t\tmicroseconds per tick, usually 10000\n" \ + "\t-p timeconstant" + +#define ar_trivial_usage \ + "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES" +#define ar_full_usage \ + "Extract or list FILES from an ar archive.\n\n" \ + "Options:\n" \ + "\t-o\t\tpreserve original dates\n" \ + "\t-p\t\textract to stdout\n" \ + "\t-t\t\tlist\n" \ + "\t-x\t\textract\n" \ + "\t-v\t\tverbosely list files processed" + +#define arping_trivial_usage \ + "[-fqbDUA] [-c count] [-w timeout] [-I device] [-s sender] target\n" +#define arping_full_usage \ + "Ping hosts by ARP requests/replies.\n\n" \ + "Options:\n" \ + "\t-f\t\tQuit on first ARP reply\n" \ + "\t-q\t\tBe quiet\n" \ + "\t-b\t\tKeep broadcasting, don't go unicast\n" \ + "\t-D\t\tDuplicated address detection mode\n" \ + "\t-U\t\tUnsolicited ARP mode, update your neighbours\n" \ + "\t-A\t\tARP answer mode, update your neighbours\n" \ + "\t-c count\tStop after sending count ARP request packets\n" \ + "\t-w timeout\tTime to wait for ARP reply, in seconds\n" \ + "\t-I device\tOutgoing interface name, default is eth0\n" \ + "\t-s sender\tSet specific sender IP address\n" \ + "\ttarget\t\tTarget IP address of ARP request" + +#define ash_trivial_usage \ + "[FILE]...\n" \ + "or: ash -c command [args]...\n" +#define ash_full_usage \ + "The ash shell (command interpreter)" + +#define awk_trivial_usage \ + "[OPTION]... [program-text] [FILE ...]" +#define awk_full_usage \ + "Options:\n" \ + "\t-v var=val\t\tassign value 'val' to variable 'var'\n" \ + "\t-F sep\t\tuse 'sep' as field separator\n" \ + "\t-f progname\t\tread program source from file 'progname'" + +#define basename_trivial_usage \ + "FILE [SUFFIX]" +#define basename_full_usage \ + "Strips directory path and suffixes from FILE.\n" \ + "If specified, also removes any trailing SUFFIX." +#define basename_example_usage \ + "$ basename /usr/local/bin/foo\n" \ + "foo\n" \ + "$ basename /usr/local/bin/\n" \ + "bin\n" \ + "$ basename /foo/bar.txt .txt\n" \ + "bar" + +#define bunzip2_trivial_usage \ + "[OPTION]... [FILE]" +#define bunzip2_full_usage \ + "Uncompress FILE (or standard input if FILE is '-' or omitted).\n\n" \ + "Options:\n" \ + "\t-c\tWrite output to standard output\n" \ + "\t-f\tForce" + +#define bzcat_trivial_usage \ + "FILE" +#define bzcat_full_usage \ + "Uncompress to stdout." + +#define cal_trivial_usage \ + "[-jy] [[month] year]" +#define cal_full_usage \ + "Display a calendar.\n" \ + "\nOptions:\n" \ + "\t-j\tUse julian dates.\n" \ + "\t-y\tDisplay the entire year." + +#define cat_trivial_usage \ + "[-u] [FILE]..." +#define cat_full_usage \ + "Concatenates FILE(s) and prints them to stdout.\n\n" \ + "Options:\n" \ + "\t-u\tignored since unbuffered i/o is always used" +#define cat_example_usage \ + "$ cat /proc/uptime\n" \ + "110716.72 17.67" + +#define chgrp_trivial_usage \ + "[OPTION]... GROUP FILE..." +#define chgrp_full_usage \ + "Change the group membership of each FILE to GROUP.\n" \ + "\nOptions:\n" \ + "\t-R\tChanges files and directories recursively." +#define chgrp_example_usage \ + "$ ls -l /tmp/foo\n" \ + "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" \ + "$ chgrp root /tmp/foo\n" \ + "$ ls -l /tmp/foo\n" \ + "-r--r--r-- 1 andersen root 0 Apr 12 18:25 /tmp/foo\n" + +#define chmod_trivial_usage \ + "[-R] MODE[,MODE]... FILE..." +#define chmod_full_usage \ + "Each MODE is one or more of the letters ugoa, one of the\n" \ + "symbols +-= and one or more of the letters rwxst.\n\n" \ + "Options:\n" \ + "\t-R\tChanges files and directories recursively." +#define chmod_example_usage \ + "$ ls -l /tmp/foo\n" \ + "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" \ + "$ chmod u+x /tmp/foo\n" \ + "$ ls -l /tmp/foo\n" \ + "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n" \ + "$ chmod 444 /tmp/foo\n" \ + "$ ls -l /tmp/foo\n" \ + "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" + +#define chown_trivial_usage \ + "[ -Rh ]... OWNER[<.|:>[GROUP]] FILE..." +#define chown_full_usage \ + "Change the owner and/or group of each FILE to OWNER and/or GROUP.\n" \ + "\nOptions:\n" \ + "\t-R\tChanges files and directories recursively.\n" \ + "\t-h\tDo not dereference symbolic links." +#define chown_example_usage \ + "$ ls -l /tmp/foo\n" \ + "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" \ + "$ chown root /tmp/foo\n" \ + "$ ls -l /tmp/foo\n" \ + "-r--r--r-- 1 root andersen 0 Apr 12 18:25 /tmp/foo\n" \ + "$ chown root.root /tmp/foo\n" \ + "ls -l /tmp/foo\n" \ + "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" + +#define chroot_trivial_usage \ + "NEWROOT [COMMAND...]" +#define chroot_full_usage \ + "Run COMMAND with root directory set to NEWROOT." +#define chroot_example_usage \ + "$ ls -l /bin/ls\n" \ + "lrwxrwxrwx 1 root root 12 Apr 13 00:46 /bin/ls -> /BusyBox\n" \ + "# mount /dev/hdc1 /mnt -t minix\n" \ + "# chroot /mnt\n" \ + "# ls -l /bin/ls\n" \ + "-rwxr-xr-x 1 root root 40816 Feb 5 07:45 /bin/ls*\n" + +#define chvt_trivial_usage \ + "N" +#define chvt_full_usage \ + "Changes the foreground virtual terminal to /dev/ttyN" + +#define clear_trivial_usage \ + "" +#define clear_full_usage \ + "Clear screen." + +#define cmp_trivial_usage \ + "[-l] [-s] FILE1 [FILE2]" +#define cmp_full_usage \ + "Compare files. Compares FILE1 vs stdin if FILE2 is not specified.\n\n" \ + "Options:\n" \ + "\t-l\tWrite the byte numbers (decimal) and values (octal)\n" \ + "\t\t for all differing bytes.\n" \ + "\t-s\tquiet mode - do not print" + +#define cp_trivial_usage \ + "[OPTION]... SOURCE DEST" +#define cp_full_usage \ + "Copies SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n" \ + "\n" \ + "\t-a\tSame as -dpR\n" \ + "\t-d\tPreserves links\n" \ + "\t-p\tPreserves file attributes if possible\n" \ + "\t-f\tforce (implied; ignored) - always set\n" \ + "\t-i\tinteractive, prompt before overwrite\n" \ + "\t-R,-r\tCopies directories recursively" + +#define cpio_trivial_usage \ + "-[dimtuv][F cpiofile]" +#define cpio_full_usage \ + "Extract or list files from a cpio archive\n" \ + "Main operation mode:\n" \ + "\td\t\tmake leading directories\n" \ + "\ti\t\textract\n" \ + "\tm\t\tpreserve mtime\n" \ + "\tt\t\tlist\n" \ + "\tv\t\tverbose\n" \ + "\tu\t\tunconditional overwrite\n" \ + "\tF\t\tinput from file" + +#define crond_trivial_usage \ + "-d[#] -c -f -b" +#define crond_full_usage \ + "\t-d [#] -l [#] -S -L logfile -f -b -c dir\n" \ + "\t-d num\tdebug level\n" \ + "\t-l num\tlog level (8 - default)\n" \ + "\t-S\tlog to syslogd (default)\n" \ + "\t-L file\tlog to file\n" \ + "\t-f\trun in fordeground\n" \ + "\t-b\trun in background (default)\n" \ + "\t-c dir\tworking dir" + +#define crontab_trivial_usage \ + "[-c dir] {file|-}|[-u|-l|-e|-d user]" +#define crontab_full_usage \ + "\tfile replace crontab from file\n" \ + "\t- replace crontab from stdin\n" \ + "\t-u user specify user\n" \ + "\t-l [user] list crontab for user\n" \ + "\t-e [user] edit crontab for user\n" \ + "\t-d [user] delete crontab for user\n" \ + "\t-c dir specify crontab directory" + + +#define cut_trivial_usage \ + "[OPTION]... [FILE]..." +#define cut_full_usage \ + "Prints selected fields from each input FILE to standard output.\n\n" \ + "Options:\n" \ + "\t-b LIST\t\tOutput only bytes from LIST\n" \ + "\t-c LIST\t\tOutput only characters from LIST\n" \ + "\t-d CHAR\t\tUse CHAR instead of tab as the field delimiter\n" \ + "\t-s\t\tOutput only the lines containing delimiter\n" \ + "\t-f N\t\tPrint only these fields\n" \ + "\t-n\t\tIgnored" +#define cut_example_usage \ + "$ echo "Hello world" | cut -f 1 -d ' '\n" \ + "Hello\n" \ + "$ echo "Hello world" | cut -f 2 -d ' '\n" \ + "world\n" + +#ifdef CONFIG_FEATURE_DATE_ISOFMT +#define USAGE_DATE_ISOFMT(a) a +#else +#define USAGE_DATE_ISOFMT(a) +#endif + +#define date_trivial_usage \ + "[OPTION]... [MMDDhhmm[[CC]YY][.ss]] [+FORMAT]" +#define date_full_usage \ + "Displays the current time in the given FORMAT, or sets the system date.\n" \ + "\nOptions:\n" \ + "\t-R\t\tOutputs RFC-822 compliant date string\n" \ + "\t-d STRING\tDisplays time described by STRING, not `now'\n" \ + USAGE_DATE_ISOFMT("\t-I[TIMESPEC]\tOutputs an ISO-8601 compliant date/time string.\n" \ + "\t\t\tTIMESPEC=`date' (or missing) for date only,\n" \ + "\t\t\t`hours', `minutes', or `seconds' for date and,\n" \ + "\t\t\ttime to the indicated precision.\n") \ + "\t-s\t\tSets time described by STRING\n" \ + "\t-r FILE\t\tDisplays the last modification time of FILE\n" \ + "\t-u\t\tPrints or sets Coordinated Universal Time" +#define date_example_usage \ + "$ date\n" \ + "Wed Apr 12 18:52:41 MDT 2000\n" + +#define dc_trivial_usage \ + "expression ..." +#define dc_full_usage \ + "This is a Tiny RPN calculator that understands the\n" \ + "following operations: +, add, -, sub, *, mul, /, div, %, mod, "\ + "**, exp, and, or, not, eor.\n" \ + "For example: 'dc 2 2 add' -> 4, and 'dc 8 8 \\* 2 2 + /' -> 16.\n" \ + "\nOptions:\n" \ + "p - Prints the value on the top of the stack, without altering the stack.\n" \ + "f - Prints the entire contents of the stack without altering anything.\n" \ + "o - Pops the value off the top of the stack and uses it to set the output radix.\n" \ + " Only 10 and 16 are supported." +#define dc_example_usage \ + "$ dc 2 2 + p\n" \ + "4\n" \ + "$ dc 8 8 \\* 2 2 + / p\n" \ + "16\n" \ + "$ dc 0 1 and p\n" \ + "0\n" \ + "$ dc 0 1 or p\n" \ + "1\n" \ + "$ echo 72 9 div 8 mul p | dc\n" \ + "64\n" + +#define dd_trivial_usage \ + "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \ + "\t [seek=N] [conv=notrunc|noerror|sync]" +#define dd_full_usage \ + "Copy a file, converting and formatting according to options\n\n" \ + "\tif=FILE\t\tread from FILE instead of stdin\n" \ + "\tof=FILE\t\twrite to FILE instead of stdout\n" \ + "\tbs=N\t\tread and write N bytes at a time\n" \ + "\tcount=N\t\tcopy only N input blocks\n" \ + "\tskip=N\t\tskip N input blocks\n" \ + "\tseek=N\t\tskip N output blocks\n" \ + "\tconv=notrunc\tdon't truncate output file\n" \ + "\tconv=noerror\tcontinue after read errors\n" \ + "\tconv=sync\tpad blocks with zeros\n" \ + "\n" \ + "Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\n" \ + "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)." +#define dd_example_usage \ + "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" \ + "4+0 records in\n" \ + "4+0 records out\n" + +#define deallocvt_trivial_usage \ + "[N]" +#define deallocvt_full_usage \ + "Deallocate unused virtual terminal /dev/ttyN" + +#define delgroup_trivial_usage \ + "GROUP" +#define delgroup_full_usage \ + "Deletes group GROUP from the system" + +#define deluser_trivial_usage \ + "USER" +#define deluser_full_usage \ + "Deletes user USER from the system" + +#ifdef CONFIG_DEVFSD_FG_NP + #define USAGE_DEVFSD_FG_NP(a) a +#else + #define USAGE_DEVFSD_FG_NP(a) +#endif + +#define devfsd_trivial_usage \ + "mntpnt [-v]"\ + USAGE_DEVFSD_FG_NP("[-fg][-np]" ) +#define devfsd_full_usage \ + "Optional daemon for managing devfs permissions and old device name symlinks.\n" \ + "\nOptions:\n" \ + "\tmntpnt\tThe mount point where devfs is mounted.\n\n" \ + "\t-v\tPrint the protocol version numbers for devfsd\n" \ + "\t\tand the kernel-side protocol version and exits." \ + USAGE_DEVFSD_FG_NP( "\n\n\t-fg\tRun the daemon in the foreground.\n\n" \ + "\t-np\tExit after parsing the configuration file\n" \ + "\t\tand processing synthetic REGISTER events.\n" \ + "\t\tDo not poll for events.") + +#ifdef CONFIG_FEATURE_HUMAN_READABLE + #define USAGE_HUMAN_READABLE(a) a + #define USAGE_NOT_HUMAN_READABLE(a) +#else + #define USAGE_HUMAN_READABLE(a) + #define USAGE_NOT_HUMAN_READABLE(a) a +#endif +#define df_trivial_usage \ + "[-" USAGE_HUMAN_READABLE("hm") USAGE_NOT_HUMAN_READABLE("") "k] [FILESYSTEM ...]" +#define df_full_usage \ + "Print the filesystem space used and space available.\n\n" \ + "Options:\n" \ + USAGE_HUMAN_READABLE( \ + "\n\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n" \ + "\t-m\tprint sizes in megabytes\n" \ + "\t-k\tprint sizes in kilobytes(default)") USAGE_NOT_HUMAN_READABLE( \ + "\n\t-k\tprint sizes in kilobytes(compatibility)") +#define df_example_usage \ + "$ df\n" \ + "Filesystem 1k-blocks Used Available Use% Mounted on\n" \ + "/dev/sda3 8690864 8553540 137324 98% /\n" \ + "/dev/sda1 64216 36364 27852 57% /boot\n" \ + "$ df /dev/sda3\n" \ + "Filesystem 1k-blocks Used Available Use% Mounted on\n" \ + "/dev/sda3 8690864 8553540 137324 98% /\n" + +#define dirname_trivial_usage \ + "FILENAME" +#define dirname_full_usage \ + "Strips non-directory suffix from FILENAME" +#define dirname_example_usage \ + "$ dirname /tmp/foo\n" \ + "/tmp\n" \ + "$ dirname /tmp/foo/\n" \ + "/tmp\n" + +#define dmesg_trivial_usage \ + "[-c] [-n LEVEL] [-s SIZE]" +#define dmesg_full_usage \ + "Prints or controls the kernel ring buffer\n\n" \ + "Options:\n" \ + "\t-c\t\tClears the ring buffer's contents after printing\n" \ + "\t-n LEVEL\tSets console logging level\n" \ + "\t-s SIZE\t\tUse a buffer of size SIZE" + +#define dos2unix_trivial_usage \ + "[option] [FILE]" +#define dos2unix_full_usage \ + "Converts FILE from dos format to unix format. When no option\n" \ + "is given, the input is converted to the opposite output format.\n" \ + "When no file is given, uses stdin for input and stdout for output.\n\n" \ + "Options:\n" \ + "\t-u\toutput will be in UNIX format\n" \ + "\t-d\toutput will be in DOS format" + +#define dpkg_trivial_usage \ + "[-ilCPru] [-F option] package_name" +#define dpkg_full_usage \ + "dpkg is a utility to install, remove and manage Debian packages.\n\n" \ + "Options:\n" \ + "\t-i\t\tInstall the package\n" \ + "\t-l\t\tList of installed packages\n" \ + "\t-C\t\tConfigure an unpackaged package\n" \ + "\t-F depends\tIgnore depency problems\n" \ + "\t-P\t\tPurge all files of a package\n" \ + "\t-r\t\tRemove all but the configuration files for a package\n" \ + "\t-u\t\tUnpack a package, but don't configure it" + +#define dpkg_deb_trivial_usage \ + "[-cefxX] FILE [argument]" +#define dpkg_deb_full_usage \ + "Perform actions on Debian packages (.debs)\n\n" \ + "Options:\n" \ + "\t-c\tList contents of filesystem tree\n" \ + "\t-e\tExtract control files to [argument] directory\n" \ + "\t-f\tDisplay control field name starting with [argument]\n" \ + "\t-x\tExtract packages filesystem tree to directory\n" \ + "\t-X\tVerbose extract" +#define dpkg_deb_example_usage \ + "$ dpkg-deb -X ./busybox_0.48-1_i386.deb /tmp\n" + +#ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K +#define USAGE_DU_DEFALT_BLOCKSIZE_1k(a) a +#define USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k(a) +#else +#define USAGE_DU_DEFALT_BLOCKSIZE_1k(a) +#define USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k(a) a +#endif + +#define du_trivial_usage \ + "[-aHLdclsx" USAGE_HUMAN_READABLE("hm") "k] [FILE]..." +#define du_full_usage \ + "Summarizes disk space used for each FILE and/or directory.\n" \ + "Disk space is printed in units of " \ + USAGE_DU_DEFALT_BLOCKSIZE_1k("1024") USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k("512") \ + " bytes.\n\n" \ + "Options:\n" \ + "\t-a\tshow sizes of files in addition to directories\n" \ + "\t-H\tfollow symbolic links that are FILE command line args\n" \ + "\t-L\tfollow all symbolic links encountered\n" \ + "\t-d N\tlimit output to directories (and files with -a) of depth < N\n" \ + "\t-c\toutput a grand total\n" \ + "\t-l\tcount sizes many times if hard linked\n" \ + "\t-s\tdisplay only a total for each argument\n" \ + "\t-x\tskip directories on different filesystems\n" \ + USAGE_HUMAN_READABLE( \ + "\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n" \ + "\t-m\tprint sizes in megabytes\n" ) \ + "\t-k\tprint sizes in kilobytes" USAGE_DU_DEFALT_BLOCKSIZE_1k("(default)") +#define du_example_usage \ + "$ du\n" \ + "16 ./CVS\n" \ + "12 ./kernel-patches/CVS\n" \ + "80 ./kernel-patches\n" \ + "12 ./tests/CVS\n" \ + "36 ./tests\n" \ + "12 ./scripts/CVS\n" \ + "16 ./scripts\n" \ + "12 ./docs/CVS\n" \ + "104 ./docs\n" \ + "2417 .\n" + +#define dumpkmap_trivial_usage \ + "> keymap" +#define dumpkmap_full_usage \ + "Prints out a binary keyboard translation table to standard output." +#define dumpkmap_example_usage \ + "$ dumpkmap > keymap\n" + +#define dumpleases_trivial_usage \ + "[-r|-a] [-f LEASEFILE]" +#define dumpleases_full_usage \ + "Displays the DHCP leases granted by udhcpd.\n\n" \ + "Options:\n" \ + "\t-f,\t--file=FILENAME\tLeases file to load\n" \ + "\t-r,\t--remaining\tInterpret lease times as time remaing\n" \ + "\t-a,\t--absolute\tInterpret lease times as expire time" + +#ifdef CONFIG_FEATURE_FANCY_ECHO + #define USAGE_FANCY_ECHO(a) a +#else + #define USAGE_FANCY_ECHO(a) +#endif + +#define echo_trivial_usage \ + USAGE_FANCY_ECHO("[-neE] ") "[ARG ...]" +#define echo_full_usage \ + "Prints the specified ARGs to stdout\n\n" \ + USAGE_FANCY_ECHO("Options:\n" \ + "\t-n\tsuppress trailing newline\n" \ + "\t-e\tinterpret backslash-escaped characters (i.e., \\t=tab)\n" \ + "\t-E\tdisable interpretation of backslash-escaped characters") +#define echo_example_usage \ + "$ echo "Erik is cool"\n" \ + "Erik is cool\n" \ + USAGE_FANCY_ECHO("$ echo -e "Erik\\nis\\ncool"\n" \ + "Erik\n" \ + "is\n" \ + "cool\n" \ + "$ echo "Erik\\nis\\ncool"\n" \ + "Erik\\nis\\ncool\n") + +#define env_trivial_usage \ + "[-iu] [-] [name=value]... [command]" +#define env_full_usage \ + "Prints the current environment or runs a program after setting\n" \ + "up the specified environment.\n\n" \ + "Options:\n" \ + "\t-, -i\tstart with an empty environment\n" \ + "\t-u\tremove variable from the environment" + +#define expr_trivial_usage \ + "EXPRESSION" +#define expr_full_usage \ + "Prints the value of EXPRESSION to standard output.\n\n" \ + "EXPRESSION may be:\n" \ + "\tARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" \ + "\tARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" \ + "\tARG1 < ARG2 ARG1 is less than ARG2\n" \ + "\tARG1 <= ARG2 ARG1 is less than or equal to ARG2\n" \ + "\tARG1 = ARG2 ARG1 is equal to ARG2\n" \ + "\tARG1 != ARG2 ARG1 is unequal to ARG2\n" \ + "\tARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n" \ + "\tARG1 > ARG2 ARG1 is greater than ARG2\n" \ + "\tARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n" \ + "\tARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n" \ + "\tARG1 * ARG2 arithmetic product of ARG1 and ARG2\n" \ + "\tARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n" \ + "\tARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n" \ + "\tSTRING : REGEXP anchored pattern match of REGEXP in STRING\n" \ + "\tmatch STRING REGEXP same as STRING : REGEXP\n" \ + "\tsubstr STRING POS LENGTH substring of STRING, POS counted from 1\n" \ + "\tindex STRING CHARS index in STRING where any CHARS is found,\n" \ + "\t or 0\n" \ + "\tlength STRING length of STRING\n" \ + "\tquote TOKEN interpret TOKEN as a string, even if\n" \ + "\t it is a keyword like `match' or an\n" \ + "\t operator like `/'\n" \ + "\t( EXPRESSION ) value of EXPRESSION\n\n" \ + "Beware that many operators need to be escaped or quoted for shells.\n" \ + "Comparisons are arithmetic if both ARGs are numbers, else\n" \ + "lexicographical. Pattern matches return the string matched between \n" \ + "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" \ + "of characters matched or 0." + +#define false_trivial_usage \ + "" +#define false_full_usage \ + "Return an exit code of FALSE (1)." +#define false_example_usage \ + "$ false\n" \ + "$ echo $?\n" \ + "1\n" + +#define fbset_trivial_usage \ + "[options] [mode]" +#define fbset_full_usage \ + "Show and modify frame buffer settings" +#define fbset_example_usage \ + "$ fbset\n" \ + "mode "1024x768-76"\n" \ + "\t# D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n" \ + "\tgeometry 1024 768 1024 768 16\n" \ + "\ttimings 12714 128 32 16 4 128 4\n" \ + "\taccel false\n" \ + "\trgba 5/11,6/5,5/0,0/0\n" \ + "endmode\n" + +#define fdflush_trivial_usage \ + "DEVICE" +#define fdflush_full_usage \ + "Forces floppy disk drive to detect disk change" + +#define fdformat_trivial_usage \ + "[-n] DEVICE" +#define fdformat_full_usage \ + "Low-level formats a floppy disk\n\n" \ + "Options:\n" \ + "\t-n\tDon't verify after format" + +#define fdisk_trivial_usage \ + "[-luv] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK" +#define fdisk_full_usage \ + "Change partition table\n" \ + "Options:\n" \ + "\t-l List partition table(s)\n" \ + "\t-u Give Start and End in sector (instead of cylinder) units\n" \ + "\t-s PARTITION Give partition size(s) in blocks\n" \ + "\t-b 2048: (for certain MO disks) use 2048-byte sectors\n" \ + "\t-C CYLINDERS Set the number of cylinders\n" \ + "\t-H HEADS Set the number of heads\n" \ + "\t-S SECTORS Set the number of sectors\n" \ + "\t-v Give fdisk version" + +#ifdef CONFIG_FEATURE_FIND_TYPE + #define USAGE_FIND_TYPE(a) a +#else + #define USAGE_FIND_TYPE(a) +#endif +#ifdef CONFIG_FEATURE_FIND_PERM + #define USAGE_FIND_PERM(a) a +#else + #define USAGE_FIND_PERM(a) +#endif +#ifdef CONFIG_FEATURE_FIND_MTIME + #define USAGE_FIND_MTIME(a) a +#else + #define USAGE_FIND_MTIME(a) +#endif +#ifdef CONFIG_FEATURE_FIND_NEWER + #define USAGE_FIND_NEWER(a) a +#else + #define USAGE_FIND_NEWER(a) +#endif +#ifdef CONFIG_FEATURE_FIND_INUM + #define USAGE_FIND_INUM(a) a +#else + #define USAGE_FIND_INUM(a) +#endif + +#define find_trivial_usage \ + "[PATH...] [EXPRESSION]" +#define find_full_usage \ + "Search for files in a directory hierarchy. The default PATH is\n" \ + "the current directory; default EXPRESSION is '-print'\n" \ + "\nEXPRESSION may consist of:\n" \ + "\t-follow\t\tDereference symbolic links.\n" \ + "\t-name PATTERN\tFile name (leading directories removed) matches PATTERN.\n" \ + "\t-print\t\tPrint (default and assumed).\n" \ + USAGE_FIND_TYPE( \ + "\n\t-type X\t\tFiletype matches X (where X is one of: f,d,l,b,c,...)" \ +) USAGE_FIND_PERM( \ + "\n\t-perm PERMS\tPermissions match any of (+NNN); all of (-NNN);\n\t\t\tor exactly (NNN)" \ +) USAGE_FIND_MTIME( \ + "\n\t-mtime TIME\tModified time is greater than (+N); less than (-N);\n\t\t\tor exactly (N) days" \ +) USAGE_FIND_NEWER( \ + "\n\t-newer FILE\tModified time is more recent than FILE's" \ +) USAGE_FIND_INUM( \ + "\n\t-inum N\t\tFile has inode number N") +#define find_example_usage \ + "$ find / -name passwd\n" \ + "/etc/passwd\n" + +#define fold_trivial_usage \ + "[-bsw] [FILE]" +#define fold_full_usage \ + "Wrap input lines in each FILE (standard input by default), writing to\n" \ + "standard output.\n\n" \ + "Options:\n" \ + "\t-b\tcount bytes rather than columns\n" \ + "\t-s\tbreak at spaces\n" \ + "\t-w\tuse WIDTH columns instead of 80" + +#define free_trivial_usage \ + "" +#define free_full_usage \ + "Displays the amount of free and used system memory" +#define free_example_usage \ + "$ free\n" \ + " total used free shared buffers\n" \ + " Mem: 257628 248724 8904 59644 93124\n" \ + " Swap: 128516 8404 120112\n" \ + "Total: 386144 257128 129016\n" \ + +#define freeramdisk_trivial_usage \ + "DEVICE" +#define freeramdisk_full_usage \ + "Frees all memory used by the specified ramdisk." +#define freeramdisk_example_usage \ + "$ freeramdisk /dev/ram2\n" + +#define fsck_minix_trivial_usage \ + "[-larvsmf] /dev/name" +#define fsck_minix_full_usage \ + "Performs a consistency check for MINIX filesystems.\n\n" \ + "Options:\n" \ + "\t-l\tLists all filenames\n" \ + "\t-r\tPerform interactive repairs\n" \ + "\t-a\tPerform automatic repairs\n" \ + "\t-v\tverbose\n" \ + "\t-s\tOutputs super-block information\n" \ + "\t-m\tActivates MINIX-like \"mode not cleared\" warnings\n" \ + "\t-f\tForce file system check." + +#define ftpget_trivial_usage \ + "[options] remote-host local-file remote-file" +#define ftpget_full_usage \ + "Retrieve a remote file via FTP.\n\n" \ + "Options:\n" \ + "\t-c, --continue Continue a previous transfer\n" \ + "\t-v, --verbose Verbose\n" \ + "\t-u, --username Username to be used\n" \ + "\t-p, --password Password to be used\n" \ + "\t-P, --port Port number to be used" + +#define ftpput_trivial_usage \ + "[options] remote-host remote-file local-file" +#define ftpput_full_usage \ + "Store a local file on a remote machine via FTP.\n\n" \ + "Options:\n" \ + "\t-v, --verbose Verbose\n" \ + "\t-u, --username Username to be used\n" \ + "\t-p, --password Password to be used\n" \ + "\t-P, --port Port number to be used" + +#define getopt_trivial_usage \ + "[OPTIONS]..." +#define getopt_full_usage \ + "Parse command options\n" \ + "\t-a, --alternative Allow long options starting with single -\n" \ + "\t-l, --longoptions=longopts Long options to be recognized\n" \ + "\t-n, --name=progname The name under which errors are reported\n" \ + "\t-o, --options=optstring Short options to be recognized\n" \ + "\t-q, --quiet Disable error reporting by getopt(3)\n" \ + "\t-Q, --quiet-output No normal output\n" \ + "\t-s, --shell=shell Set shell quoting conventions\n" \ + "\t-T, --test Test for getopt(1) version\n" \ + "\t-u, --unquoted Do not quote the output" +#define getopt_example_usage \ + "$ cat getopt.test\n" \ + "#!/bin/sh\n" \ + "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" \ + " -n 'example.busybox' -- "$@"`\n" \ + "if [ $? != 0 ] ; then exit 1 ; fi\n" \ + "eval set -- "$GETOPT"\n" \ + "while true ; do\n" \ + " case $1 in\n" \ + " -a|--a-long) echo \"Option a\" ; shift ;;\n" \ + " -b|--b-long) echo \"Option b, argument `$2'\" ; shift 2 ;;\n" \ + " -c|--c-long)\n" \ + " case "$2" in\n" \ + " \"\") echo \"Option c, no argument\"; shift 2 ;;\n" \ + " *) echo \"Option c, argument `$2'\" ; shift 2 ;;\n" \ + " esac ;;\n" \ + " --) shift ; break ;;\n" \ + " *) echo \"Internal error!\" ; exit 1 ;;\n" \ + " esac\n" \ + "done\n" + +#define getty_trivial_usage \ + "[OPTIONS]... baud_rate,... line [termtype]" +#define getty_full_usage \ + "Opens a tty, prompts for a login name, then invokes /bin/login\n\n" \ + "Options:\n" \ + "\t-h\t\tEnable hardware (RTS/CTS) flow control.\n" \ + "\t-i\t\tDo not display /etc/issue before running login.\n" \ + "\t-L\t\tLocal line, so do not do carrier detect.\n" \ + "\t-m\t\tGet baud rate from modem's CONNECT status message.\n" \ + "\t-w\t\tWait for a CR or LF before sending /etc/issue.\n" \ + "\t-n\t\tDo not prompt the user for a login name.\n" \ + "\t-f issue_file\tDisplay issue_file instead of /etc/issue.\n" \ + "\t-l login_app\tInvoke login_app instead of /bin/login.\n" \ + "\t-t timeout\tTerminate after timeout if no username is read.\n" \ + "\t-I initstring\tSets the init string to send before anything else.\n" \ + "\t-H login_host\tLog login_host into the utmp file as the hostname." + + +#define grep_trivial_usage \ + "[-ihHnqvs] PATTERN [FILEs...]" +#define grep_full_usage \ + "Search for PATTERN in each FILE or standard input.\n\n" \ + "Options:\n" \ + "\t-H\tprefix output lines with filename where match was found\n" \ + "\t-h\tsuppress the prefixing filename on output\n" \ + "\t-i\tignore case distinctions\n" \ + "\t-l\tlist names of files that match\n" \ + "\t-n\tprint line number with output lines\n" \ + "\t-q\tbe quiet. Returns 0 if result was found, 1 otherwise\n" \ + "\t-v\tselect non-matching lines\n" \ + "\t-s\tsuppress file open/read error messages" +#define grep_example_usage \ + "$ grep root /etc/passwd\n" \ + "root:x:0:0:root:/root:/bin/bash\n" \ + "$ grep ^[rR]oo. /etc/passwd\n" \ + "root:x:0:0:root:/root:/bin/bash\n" + +#define gunzip_trivial_usage \ + "[OPTION]... FILE" +#define gunzip_full_usage \ + "Uncompress FILE (or standard input if FILE is '-').\n\n" \ + "Options:\n" \ + "\t-c\tWrite output to standard output\n" \ + "\t-f\tForce read when source is a terminal\n" \ + "\t-t\tTest compressed file integrity" +#define gunzip_example_usage \ + "$ ls -la /tmp/BusyBox*\n" \ + "-rw-rw-r-- 1 andersen andersen 557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n" \ + "$ gunzip /tmp/BusyBox-0.43.tar.gz\n" \ + "$ ls -la /tmp/BusyBox*\n" \ + "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n" + +#define gzip_trivial_usage \ + "[OPTION]... [FILE]..." +#define gzip_full_usage \ + "Compress FILE(s) with maximum compression.\n" \ + "When FILE is '-' or unspecified, reads standard input. Implies -c.\n\n" \ + "Options:\n" \ + "\t-c\tWrite output to standard output instead of FILE.gz\n" \ + "\t-d\tDecompress\n" \ + "\t-f\tForce write when destination is a terminal" +#define gzip_example_usage \ + "$ ls -la /tmp/busybox*\n" \ + "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/busybox.tar\n" \ + "$ gzip /tmp/busybox.tar\n" \ + "$ ls -la /tmp/busybox*\n" \ + "-rw-rw-r-- 1 andersen andersen 554058 Apr 14 17:49 /tmp/busybox.tar.gz\n" + +#define halt_trivial_usage \ + "[-d]" +#define halt_full_usage \ + "Halt the system.\n" \ + "Options:\n" \ + "\t-d\t\tdelay interval for halting." + +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +#define USAGE_HDPARM_IDENT(a) a +#else +#define USAGE_HDPARM_IDENT(a) +#endif + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF +#define USAGE_SCAN_HWIF(a) a +#else +#define USAGE_SCAN_HWIF(a) +#endif + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF +#define USAGE_UNREGISTER_HWIF(a) a +#else +#define USAGE_UNREGISTER_HWIF(a) +#endif + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET +#define USAGE_DRIVE_RESET(a) a +#else +#define USAGE_DRIVE_RESET(a) +#endif + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF +#define USAGE_TRISTATE_HWIF(a) a +#else +#define USAGE_TRISTATE_HWIF(a) +#endif + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA +#define USAGE_GETSET_DMA(a) a +#else +#define USAGE_GETSET_DMA(a) +#endif + +#define hdparm_trivial_usage \ + "[options] [device] .." +#define hdparm_full_usage \ + "Options:" \ + "\t-a get/set fs readahead\n" \ + "\t-A set drive read-lookahead flag (0/1)\n" \ + "\t-b get/set bus state (0 == off, 1 == on, 2 == tristate)\n" \ + "\t-B set Advanced Power Management setting (1-255)\n" \ + "\t-c get/set IDE 32-bit IO setting\n" \ + "\t-C check IDE power mode status\n" \ + USAGE_GETSET_DMA("\t-d get/set using_dma flag\n") \ + "\t-D enable/disable drive defect-mgmt\n" \ + "\t-f flush buffer cache for device on exit\n" \ + "\t-g display drive geometry\n" \ + "\t-h display terse usage information\n" \ + "\t-i display drive identification\n" \ + USAGE_HDPARM_IDENT("\t-I detailed/current information directly from drive\n") \ + USAGE_HDPARM_IDENT("\t-Istdin similar to -I, but wants /proc/ide/" "*" "/hd?/identify as input\n") \ + "\t-k get/set keep_settings_over_reset flag (0/1)\n" \ + "\t-K set drive keep_features_over_reset flag (0/1)\n" \ + "\t-L set drive doorlock (0/1) (removable harddisks only)\n" \ + "\t-m get/set multiple sector count\n" \ + "\t-n get/set ignore-write-errors flag (0/1)\n" \ + "\t-p set PIO mode on IDE interface chipset (0,1,2,3,4,...)\n" \ + "\t-P set drive prefetch count\n" \ + "\t-q change next setting quietly\n" \ + "\t-Q get/set DMA tagged-queuing depth (if supported)\n" \ + "\t-r get/set readonly flag (DANGEROUS to set)\n" \ + USAGE_SCAN_HWIF("\t-R register an IDE interface (DANGEROUS)\n") \ + "\t-S set standby (spindown) timeout\n" \ + "\t-t perform device read timings\n" \ + "\t-T perform cache read timings\n" \ + "\t-u get/set unmaskirq flag (0/1)\n" \ + USAGE_UNREGISTER_HWIF("\t-U un-register an IDE interface (DANGEROUS)\n") \ + "\t-v defaults; same as -mcudkrag for IDE drives\n" \ + "\t-V display program version and exit immediately\n" \ + USAGE_DRIVE_RESET("\t-w perform device reset (DANGEROUS)\n") \ + "\t-W set drive write-caching flag (0/1) (DANGEROUS)\n" \ + USAGE_TRISTATE_HWIF("\t-x tristate device for hotswap (0/1) (DANGEROUS)\n") \ + "\t-X set IDE xfer mode (DANGEROUS)\n" \ + "\t-y put IDE drive in standby mode\n" \ + "\t-Y put IDE drive to sleep\n" \ + "\t-Z disable Seagate auto-powersaving mode\n" \ + "\t-z re-read partition table" + +#ifdef CONFIG_FEATURE_FANCY_HEAD +#define USAGE_FANCY_HEAD(a) a +#else +#define USAGE_FANCY_HEAD(a) +#endif + +#define head_trivial_usage \ + "[OPTION]... [FILE]..." +#define head_full_usage \ + "Print first 10 lines of each FILE to standard output.\n" \ + "With more than one FILE, precede each with a header giving the\n" \ + "file name. With no FILE, or when FILE is -, read standard input.\n\n" \ + "Options:\n" \ + "\t-n NUM\t\tPrint first NUM lines instead of first 10" \ + USAGE_FANCY_HEAD( \ + "\n\t-c NUM\t\toutput the first NUM bytes\n" \ + "\t-q\t\tnever output headers giving file names\n" \ + "\t-v\t\talways output headers giving file names" ) +#define head_example_usage \ + "$ head -n 2 /etc/passwd\n" \ + "root:x:0:0:root:/root:/bin/bash\n" \ + "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n" + +#define hexdump_trivial_usage \ + "[-[bcdefnosvx]] [OPTION] FILE" +#define hexdump_full_usage \ + "The hexdump utility is a filter which displays the specified files,\n" \ + "or the standard input, if no files are specified, in a user specified\n"\ + "format\n" \ + "\t-b\t\tOne-byte octal display\n" \ + "\t-c\t\tOne-byte character display\n" \ + "\t-d\t\tTwo-byte decimal display\n" \ + "\t-e FORMAT STRING\n" \ + "\t-f FORMAT FILE\n" \ + "\t-n LENGTH\tInterpret only length bytes of input\n" \ + "\t-o\t\tTwo-byte octal display\n" \ + "\t-s OFFSET\tSkip offset byte\n" \ + "\t-v\t\tdisplay all input data\n" \ + "\t-x\t\tTwo-byte hexadecimal display" + +#define hostid_trivial_usage \ + "" +#define hostid_full_usage \ + "Print out a unique 32-bit identifier for the machine." + +#define hostname_trivial_usage \ + "[OPTION] {hostname | -F FILE}" +#define hostname_full_usage \ + "Get or set the hostname or DNS domain name. If a hostname is given\n" \ + "(or FILE with the -F parameter), the host name will be set.\n\n" \ + "Options:\n" \ + "\t-s\tShort\n" \ + "\t-i\tAddresses for the hostname\n" \ + "\t-d\tDNS domain name\n" \ + "\t-f\tFully qualified domain name\n" \ + "\t-F FILE\tUse the contents of FILE to specify the hostname" +#define hostname_example_usage \ + "$ hostname\n" \ + "sage\n" + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + #define USAGE_HTTPD_BASIC_AUTH(a) a + #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + #define USAGE_HTTPD_AUTH_MD5(a) a + #else + #define USAGE_HTTPD_AUTH_MD5(a) + #endif +#else + #define USAGE_HTTPD_BASIC_AUTH(a) + #define USAGE_HTTPD_AUTH_MD5(a) +#endif +#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + #define USAGE_HTTPD_STANDALONE(a) + #define USAGE_HTTPD_SETUID(a) +#else + #define USAGE_HTTPD_STANDALONE(a) a + #ifdef CONFIG_FEATURE_HTTPD_SETUID + #define USAGE_HTTPD_SETUID(a) a + #else + #define USAGE_HTTPD_SETUID(a) + #endif +#endif +#define httpd_trivial_usage \ + "[-c ]" \ + USAGE_HTTPD_STANDALONE(" [-p ]") \ + USAGE_HTTPD_SETUID(" [-u user]") \ + USAGE_HTTPD_BASIC_AUTH(" [-r ]") \ + USAGE_HTTPD_AUTH_MD5(" [-m pass]") \ + " [-h home]" \ + " [-d/-e ]" +#define httpd_full_usage \ + "Listens for incoming http server requests.\n\n"\ + "Options:\n" \ + "\t-c FILE\t\tSpecifies configuration file. (default httpd.conf)\n" \ + USAGE_HTTPD_STANDALONE("\t-p PORT\tServer port (default 80)\n") \ + USAGE_HTTPD_SETUID("\t-u USER\tSet uid to USER after listening privileges port\n") \ + USAGE_HTTPD_BASIC_AUTH("\t-r REALM\tAuthentication Realm for Basic Authentication\n") \ + USAGE_HTTPD_AUTH_MD5("\t-m PASS\t\tCrypt PASS with md5 algorithm\n") \ + "\t-h HOME \tSpecifies http HOME directory (default ./)\n" \ + "\t-e STRING\tHtml encode STRING\n" \ + "\t-d STRING\tURL decode STRING" + +#define hwclock_trivial_usage \ + "[-r|--show] [-s|--hctosys] [-w|--systohc] [-l|--localtime] [-u|--utc]" +#define hwclock_full_usage \ + "Query and set the hardware clock (RTC)\n\n" \ + "Options:\n" \ + "\t-r\tread hardware clock and print result\n" \ + "\t-s\tset the system time from the hardware clock\n" \ + "\t-w\tset the hardware clock to the current system time\n" \ + "\t-u\tthe hardware clock is kept in coordinated universal time\n" \ + "\t-l\tthe hardware clock is kept in local time" + +#ifdef CONFIG_SELINUX + #define USAGE_SELINUX(a) a +#else + #define USAGE_SELINUX(a) +#endif + +#define id_trivial_usage \ + "[OPTIONS]... [USERNAME]" +#define id_full_usage \ + "Print information for USERNAME or the current user\n\n" \ + "Options:\n" \ + USAGE_SELINUX("\t-c\tprints only the security context\n") \ + "\t-g\tprints only the group ID\n" \ + "\t-u\tprints only the user ID\n" \ + "\t-n\tprint a name instead of a number\n" \ + "\t-r\tprints the real user ID instead of the effective ID" +#define id_example_usage \ + "$ id\n" \ + "uid=1000(andersen) gid=1000(andersen)\n" + +#ifdef CONFIG_FEATURE_IFCONFIG_SLIP + #define USAGE_SIOCSKEEPALIVE(a) a +#else + #define USAGE_SIOCSKEEPALIVE(a) +#endif +#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + #define USAGE_IFCONFIG_MII(a) a +#else + #define USAGE_IFCONFIG_MII(a) +#endif +#ifdef CONFIG_FEATURE_IFCONFIG_HW + #define USAGE_IFCONFIG_HW(a) a +#else + #define USAGE_IFCONFIG_HW(a) +#endif +#ifdef CONFIG_FEATURE_IFCONFIG_STATUS + #define USAGE_IFCONFIG_OPT_A(a) a +#else + #define USAGE_IFCONFIG_OPT_A(a) +#endif +#ifdef CONFIG_FEATURE_IPV6 + #define USAGE_IPV6(a) a +#else + #define USAGE_IPV6(a) +#endif + +#define ifconfig_trivial_usage \ + USAGE_IFCONFIG_OPT_A("[-a]") " [
]" +#define ifconfig_full_usage \ + "configure a network interface\n\n" \ + "Options:\n" \ + USAGE_IPV6("[add
[/]]\n") \ + USAGE_IPV6("[del
[/]]\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 ifup_trivial_usage \ + "<-ahinv> " +#define ifup_full_usage \ + "ifup \n\n" \ + "Options:\n" \ + "\t-h\tthis help\n" \ + "\t-a\tde/configure all interfaces automatically\n" \ + "\t-i FILE\tuse FILE for interface definitions\n" \ + "\t-n\tprint out what would happen, but don't do it\n" \ + "\t\t\t(note that this option doesn't disable mappings)\n" \ + "\t-v\tprint out what would happen before doing it\n" \ + "\t-m\tdon't run any mappings\n" \ + "\t-f\tforce de/configuration" + +#define ifdown_trivial_usage \ + "<-ahinv> " +#define ifdown_full_usage \ + "ifdown \n\n" \ + "Options:\n" \ + "\t-h\tthis help\n" \ + "\t-a\tde/configure all interfaces automatically\n" \ + "\t-i FILE\tuse FILE for interface definitions\n" \ + "\t-n\tprint out what would happen, but don't do it\n" \ + "\t\t(note that this option doesn't disable mappings)\n" \ + "\t-v\tprint out what would happen before doing it\n" \ + "\t-m\tdon't run any mappings\n" \ + "\t-f\tforce de/configuration" + +#define inetd_trivial_usage \ + "[-q len] [conf]" +#define inetd_full_usage \ + "Listens for network connections and launches programs\n\n" \ + "Option:\n" \ + "\t-q\tSets the size of the socket listen queue to\n" \ + "\t\tthe specified value. Default is 128." + +#define init_trivial_usage \ + "" +#define init_full_usage \ + "Init is the parent of all processes." +#define init_notes_usage \ +"This version of init is designed to be run only by the kernel.\n" \ +"\n" \ +"BusyBox init doesn't support multiple runlevels. The runlevels field of\n" \ +"the /etc/inittab file is completely ignored by BusyBox init. If you want \n" \ +"runlevels, use sysvinit.\n" \ +"\n" \ +"BusyBox init works just fine without an inittab. If no inittab is found, \n" \ +"it has the following default behavior:\n" \ +"\n" \ +" ::sysinit:/etc/init.d/rcS\n" \ +" ::askfirst:/bin/sh\n" \ +" ::ctrlaltdel:/sbin/reboot\n" \ +" ::shutdown:/sbin/swapoff -a\n" \ +" ::shutdown:/bin/umount -a -r\n" \ +" ::restart:/sbin/init\n" \ +"\n" \ +"if it detects that /dev/console is _not_ a serial console, it will also run:\n" \ +"\n" \ +" tty2::askfirst:/bin/sh\n" \ +" tty3::askfirst:/bin/sh\n" \ +" tty4::askfirst:/bin/sh\n" \ +"\n" \ +"If you choose to use an /etc/inittab file, the inittab entry format is as follows:\n" \ +"\n" \ +" :::\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, restart, ctrlaltdel, and shutdown.\n" \ +"\n" \ +" The available actions can be classified into two groups: actions\n" \ +" that are run only once, and actions that are re-run when the specified\n" \ +" process exits.\n" \ +"\n" \ +" Run only-once actions:\n" \ +"\n" \ +" 'sysinit' is the first item run on boot. init waits until all\n" \ +" sysinit actions are completed before continuing. Following the\n" \ +" completion of all sysinit actions, all 'wait' actions are run.\n" \ +" 'wait' actions, like 'sysinit' actions, cause init to wait until\n" \ +" the specified task completes. 'once' actions are asynchronous,\n" \ +" therefore, init does not wait for them to complete. 'restart' is\n" \ +" the action taken to restart the init process. By default this should\n" \ +" simply run /sbin/init, but can be a script which runs pivot_root or it\n" \ +" can do all sorts of other interesting things. The 'ctrlaltdel' init\n" \ +" actions are run when the system detects that someone on the system\n" \ +" console has pressed the CTRL-ALT-DEL key combination. Typically one\n" \ +" wants to run 'reboot' at this point to cause the system to reboot.\n" \ +" Finally the 'shutdown' action specifies the actions to taken when\n" \ +" init is told to reboot. Unmounting filesystems and disabling swap\n" \ +" is a very good here\n" \ +"\n" \ +" Run repeatedly actions:\n" \ +"\n" \ +" 'respawn' actions are run after the 'once' actions. When a process\n" \ +" started with a 'respawn' action exits, init automatically restarts\n" \ +" it. Unlike sysvinit, BusyBox init does not stop processes from\n" \ +" respawning out of control. The 'askfirst' actions acts just like\n" \ +" respawn, except that before running the specified process it\n" \ +" displays the line "Please press Enter to activate this console."\n" \ +" and then waits for the user to press enter before starting the\n" \ +" specified process.\n" \ +"\n" \ +" Unrecognized actions (like initdefault) will cause init to emit an\n" \ +" error message, and then go along with its business. All actions are\n" \ +" run in the order they appear in /etc/inittab.\n" \ +"\n" \ +" :\n" \ +"\n" \ +" Specifies the process to be executed and its command line.\n" \ +"\n" \ +"Example /etc/inittab file:\n" \ +"\n" \ +" # This is run first except when booting in single-user mode.\n" \ +" #\n" \ +" ::sysinit:/etc/init.d/rcS\n" \ +" \n" \ +" # /bin/sh invocations on selected ttys\n" \ +" #\n" \ +" # Start an "askfirst" shell on the console (whatever that may be)\n" \ +" ::askfirst:-/bin/sh\n" \ +" # Start an "askfirst" shell on /dev/tty2-4\n" \ +" tty2::askfirst:-/bin/sh\n" \ +" tty3::askfirst:-/bin/sh\n" \ +" tty4::askfirst:-/bin/sh\n" \ +" \n" \ +" # /sbin/getty invocations for selected ttys\n" \ +" #\n" \ +" tty4::respawn:/sbin/getty 38400 tty4\n" \ +" tty5::respawn:/sbin/getty 38400 tty5\n" \ +" \n" \ +" \n" \ +" # Example of how to put a getty on a serial line (for a terminal)\n" \ +" #\n" \ +" #::respawn:/sbin/getty -L ttyS0 9600 vt100\n" \ +" #::respawn:/sbin/getty -L ttyS1 9600 vt100\n" \ +" #\n" \ +" # Example how to put a getty on a modem line.\n" \ +" #::respawn:/sbin/getty 57600 ttyS2\n" \ +" \n" \ +" # Stuff to do when restarting the init process\n" \ +" ::restart:/sbin/init\n" \ +" \n" \ +" # Stuff to do before rebooting\n" \ +" ::ctrlaltdel:/sbin/reboot\n" \ +" ::shutdown:/bin/umount -a -r\n" \ +" ::shutdown:/sbin/swapoff -a\n" + +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + #define USAGE_INSMOD_MAP(a) a +#else + #define USAGE_INSMOD_MAP(a) +#endif +#define insmod_trivial_usage \ + "[OPTION]... MODULE [symbol=value]..." +#define insmod_full_usage \ + "Loads the specified kernel modules into the kernel.\n\n" \ + "Options:\n" \ + "\t-f\tForce module to load into the wrong kernel version.\n" \ + "\t-k\tMake module autoclean-able.\n" \ + "\t-v\tverbose output\n" \ + "\t-q\tquiet output\n" \ + "\t-L\tLock to prevent simultaneous loads of a module\n" \ + USAGE_INSMOD_MAP("\t-m\tOutput load map to stdout\n") \ + "\t-o NAME\tSet internal module name to NAME\n" \ + "\t-x\tdo not export externs" + +#define install_trivial_usage \ + "[-cgmops] [sources] " +#define install_full_usage \ + "Copies files and set attributes\n\n" \ + "Options:\n" \ + "\t-c\tcopy the file, default\n" \ + "\t-d\tcreate directories\n" \ + "\t-g\tset group ownership\n" \ + "\t-m\tset permission modes\n" \ + "\t-o\tset ownership\n" \ + "\t-p\tpreserve date\n" \ + "\t-s\tstrip symbol tables" + +#define ip_trivial_usage \ + "[ OPTIONS ] { address | link | route | tunnel } { COMMAND | help }" +#define ip_full_usage \ + "ip [ OPTIONS ] OBJECT { COMMAND | help }\n" \ + "where OBJECT := { link | addr | route | tunnel }\n" \ + "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }" + +#define ipaddr_trivial_usage \ + "{ {add|del} IFADDR dev STRING | {show|flush}\n" \ + "\t\t[ dev STRING ] [ to PREFIX ] }" +#define ipaddr_full_usage \ + "ipaddr {add|del} IFADDR dev STRING\n" \ + "ipaddr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n" \ + "\t\t\t[ to PREFIX ] [ label PATTERN ]\n" \ + "\t\t\tIFADDR := PREFIX | ADDR peer PREFIX\n" \ + "\t\t\t[ broadcast ADDR ] [ anycast ADDR ]\n" \ + "\t\t\t[ label STRING ] [ scope SCOPE-ID ]\n" \ + "\t\t\tSCOPE-ID := [ host | link | global | NUMBER ]" + +#ifdef CONFIG_FEATURE_IPCALC_FANCY + #define XUSAGE_IPCALC_FANCY(a) a +#else + #define XUSAGE_IPCALC_FANCY(a) +#endif +#define ipcalc_trivial_usage \ + "[OPTION]...
[[/]] [NETMASK]" +#define ipcalc_full_usage \ + "Calculate IP network settings from a IP address\n\n" \ + "Options:\n" \ + "\t-b\t--broadcast\tDisplay calculated broadcast address.\n" \ + "\t-n\t--network\tDisplay calculated network address.\n" \ + "\t-m\t--netmask\tDisplay default netmask for IP." \ + XUSAGE_IPCALC_FANCY(\ + "\n\t-p\t--prefix\tDisplay the prefix for IP/NETMASK." \ + "\t-h\t--hostname\tDisplay first resolved host name.\n" \ + "\t-s\t--silent\tDon't ever display error messages.") + +#define iplink_trivial_usage \ + "{ set DEVICE { up | down | arp { on | off } | show [ DEVICE ] }" +#define iplink_full_usage \ + "iplink set DEVICE { up | down | arp { on | off } |\n" \ + "\t\t\tdynamic { on | off } |\n" \ + "\t\t\tmtu MTU }\n" \ + "\tiplink show [ DEVICE ]" + +#define iproute_trivial_usage \ + "{ list | flush | { add | del | change | append |\n" \ + "\t\treplace | monitor } ROUTE }" +#define iproute_full_usage \ + "iproute { list | flush } SELECTOR\n" \ + "iproute get ADDRESS [ from ADDRESS iif STRING ]\n" \ + "\t\t\t[ oif STRING ] [ tos TOS ]\n" \ + "\tiproute { add | del | change | append | replace | monitor } ROUTE\n" \ + "\t\t\tSELECTOR := [ root PREFIX ] [ match PREFIX ] [ proto RTPROTO ]\n" \ + "\t\t\tROUTE := [ TYPE ] PREFIX [ tos TOS ] [ proto RTPROTO ]" + +#define iptunnel_trivial_usage \ + "{ add | change | del | show } [ NAME ]\n" \ + "\t\t[ mode { ipip | gre | sit } ]\n" \ + "\t\t[ remote ADDR ] [ local ADDR ] [ ttl TTL ]" +#define iptunnel_full_usage \ + "iptunnel { add | change | del | show } [ NAME ]\n" \ + "\t\t\t[ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n" \ + "\t\t\t[ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n" \ + "\t\t\t[ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]" + +#define kill_trivial_usage \ + "[-signal] process-id [process-id ...]" +#define kill_full_usage \ + "Send a signal (default is SIGTERM) to the specified process(es).\n\n"\ + "Options:\n" \ + "\t-l\tList all signal names and numbers." +#define kill_example_usage \ + "$ ps | grep apache\n" \ + "252 root root S [apache]\n" \ + "263 www-data www-data S [apache]\n" \ + "264 www-data www-data S [apache]\n" \ + "265 www-data www-data S [apache]\n" \ + "266 www-data www-data S [apache]\n" \ + "267 www-data www-data S [apache]\n" \ + "$ kill 252\n" + +#define killall_trivial_usage \ + "[-q] [-signal] process-name [process-name ...]" +#define killall_full_usage \ + "Send a signal (default is SIGTERM) to the specified process(es).\n\n"\ + "Options:\n" \ + "\t-l\tList all signal names and numbers.\n"\ + "\t-q\tDo not complain if no processes were killed." +#define killall_example_usage \ + "$ killall apache\n" + +#define klogd_trivial_usage \ + "[-c n] [-n]" +#define klogd_full_usage \ + "Kernel logger.\n"\ + "Options:\n"\ + "\t-c n\tSets the default log level of console messages to n.\n"\ + "\t-n\tRun as a foreground process." + +#define length_trivial_usage \ + "STRING" +#define length_full_usage \ + "Prints out the length of the specified STRING." +#define length_example_usage \ + "$ length Hello\n" \ + "5\n" + +#define ln_trivial_usage \ + "[OPTION] TARGET... LINK_NAME|DIRECTORY" +#define ln_full_usage \ + "Create a link named LINK_NAME or DIRECTORY to the specified TARGET\n"\ + "\nYou may use '--' to indicate that all following arguments are non-options.\n\n" \ + "Options:\n" \ + "\t-s\tmake symbolic links instead of hard links\n" \ + "\t-f\tremove existing destination files\n" \ + "\t-n\tno dereference symlinks - treat like normal file" +#define ln_example_usage \ + "$ ln -s BusyBox /tmp/ls\n" \ + "$ ls -l /tmp/ls\n" \ + "lrwxrwxrwx 1 root root 7 Apr 12 18:39 ls -> BusyBox*\n" + +#define loadfont_trivial_usage \ + "< font" +#define loadfont_full_usage \ + "Loads a console font from standard input." +#define loadfont_example_usage \ + "$ loadfont < /etc/i18n/fontname\n" + +#define loadkmap_trivial_usage \ + "< keymap" +#define loadkmap_full_usage \ + "Loads a binary keyboard translation table from standard input." +#define loadkmap_example_usage \ + "$ loadkmap < /etc/i18n/lang-keymap\n" + +#define logger_trivial_usage \ + "[OPTION]... [MESSAGE]" +#define logger_full_usage \ + "Write MESSAGE to the system log. If MESSAGE is omitted, log stdin.\n\n" \ + "Options:\n" \ + "\t-s\tLog to stderr as well as the system log.\n" \ + "\t-t TAG\tLog using the specified tag (defaults to user name).\n" \ + "\t-p PRIORITY\tEnter the message with the specified priority.\n" \ + "\t\tThis may be numerical or a ``facility.level'' pair." +#define logger_example_usage \ + "$ logger "hello"\n" + +#define login_trivial_usage \ + "[OPTION]... [username] [ENV=VAR ...]" +#define login_full_usage \ + "Begin a new session on the system\n\n" \ + "Options:\n" \ + "\t-f\tDo not authenticate (user already authenticated)\n" \ + "\t-h\tName of the remote host for this login.\n" \ + "\t-p\tPreserve environment." + +#define logname_trivial_usage \ + "" +#define logname_full_usage \ + "Print the name of the current user." +#define logname_example_usage \ + "$ logname\n" \ + "root\n" + +#define logread_trivial_usage \ + "[OPTION]..." +#define logread_full_usage \ + "Shows the messages from syslogd (using circular buffer).\n\n" \ + "Options:\n" \ + "\t-f\t\toutput data as the log grows" + +#define losetup_trivial_usage \ + "[OPTION]... LOOPDEVICE FILE\n" \ + "or: losetup [OPTION]... -d LOOPDEVICE" +#define losetup_full_usage \ + "Associate LOOPDEVICE with FILE.\n\n" \ + "Options:\n" \ + "\t-d\t\tDisassociate LOOPDEVICE.\n" \ + "\t-o OFFSET\tStart OFFSET bytes into FILE." + +#ifdef CONFIG_FEATURE_LS_TIMESTAMPS + #define USAGE_LS_TIMESTAMPS(a) a +#else + #define USAGE_LS_TIMESTAMPS(a) +#endif +#ifdef CONFIG_FEATURE_LS_FILETYPES + #define USAGE_LS_FILETYPES(a) a +#else + #define USAGE_LS_FILETYPES(a) +#endif +#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS + #define USAGE_LS_FOLLOWLINKS(a) a +#else + #define USAGE_LS_FOLLOWLINKS(a) +#endif +#ifdef CONFIG_FEATURE_LS_RECURSIVE + #define USAGE_LS_RECURSIVE(a) a +#else + #define USAGE_LS_RECURSIVE(a) +#endif +#ifdef CONFIG_FEATURE_LS_SORTFILES + #define USAGE_LS_SORTFILES(a) a +#else + #define USAGE_LS_SORTFILES(a) +#endif +#ifdef CONFIG_FEATURE_AUTOWIDTH + #define USAGE_AUTOWIDTH(a) a +#else + #define USAGE_AUTOWIDTH(a) +#endif + +#define ls_trivial_usage \ + "[-1Aa" USAGE_LS_TIMESTAMPS("c") "Cd" USAGE_LS_TIMESTAMPS("e") USAGE_LS_FILETYPES("F") "iln" USAGE_LS_FILETYPES("p") USAGE_LS_FOLLOWLINKS("L") USAGE_LS_RECURSIVE("R") USAGE_LS_SORTFILES("rS") "s" USAGE_AUTOWIDTH("T") USAGE_LS_TIMESTAMPS("tu") USAGE_LS_SORTFILES("v") USAGE_AUTOWIDTH("w") "x" USAGE_LS_SORTFILES("X") USAGE_HUMAN_READABLE("h") USAGE_NOT_HUMAN_READABLE("") "k" USAGE_SELINUX("K") "] [filenames...]" +#define ls_full_usage \ + "List directory contents\n\n" \ + "Options:\n" \ + "\t-1\tlist files in a single column\n" \ + "\t-A\tdo not list implied . and ..\n" \ + "\t-a\tdo not hide entries starting with .\n" \ + "\t-C\tlist entries by columns\n" \ + USAGE_LS_TIMESTAMPS("\t-c\twith -l: show ctime\n") \ + "\t-d\tlist directory entries instead of contents\n" \ + USAGE_LS_TIMESTAMPS("\t-e\tlist both full date and full time\n") \ + USAGE_LS_FILETYPES("\t-F\tappend indicator (one of */=@|) to entries\n") \ + "\t-i\tlist the i-node for each file\n" \ + "\t-l\tuse a long listing format\n" \ + "\t-n\tlist numeric UIDs and GIDs instead of names\n" \ + USAGE_LS_FILETYPES("\t-p\tappend indicator (one of /=@|) to entries\n") \ + USAGE_LS_FOLLOWLINKS("\t-L\tlist entries pointed to by symbolic links\n") \ + USAGE_LS_RECURSIVE("\t-R\tlist subdirectories recursively\n") \ + USAGE_LS_SORTFILES("\t-r\tsort the listing in reverse order\n") \ + USAGE_LS_SORTFILES("\t-S\tsort the listing by file size\n") \ + "\t-s\tlist the size of each file, in blocks\n" \ + USAGE_AUTOWIDTH("\t-T NUM\tassume Tabstop every NUM columns\n") \ + USAGE_LS_TIMESTAMPS("\t-t\twith -l: show modification time\n") \ + USAGE_LS_TIMESTAMPS("\t-u\twith -l: show access time\n") \ + USAGE_LS_SORTFILES("\t-v\tsort the listing by version\n") \ + USAGE_AUTOWIDTH("\t-w NUM\tassume the terminal is NUM columns wide\n") \ + "\t-x\tlist entries by lines instead of by columns\n" \ + USAGE_LS_SORTFILES("\t-X\tsort the listing by extension\n") \ + USAGE_HUMAN_READABLE( \ + "\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n") \ + USAGE_SELINUX("\t-k\tprint security context\n") \ + USAGE_SELINUX("\t-K\tprint security context in long format\n") + +#define lsmod_trivial_usage \ + "" +#define lsmod_full_usage \ + "List the currently loaded kernel modules." + +#define makedevs_trivial_usage \ + "NAME TYPE MAJOR MINOR FIRST LAST [s]" +#define makedevs_full_usage \ + "Creates a range of block or character special files\n\n" \ + "TYPEs include:\n" \ + "\tb:\tMake a block (buffered) device.\n" \ + "\tc or u:\tMake a character (un-buffered) device.\n" \ + "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes.\n\n" \ + "FIRST specifies the number appended to NAME to create the first device.\n" \ + "LAST specifies the number of the last item that should be created.\n" \ + "If 's' is the last argument, the base device is created as well.\n\n" \ + "For example:\n" \ + "\tmakedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63\n" \ + "\tmakedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8" +#define makedevs_example_usage \ + "# makedevs /dev/ttyS c 4 66 2 63\n" \ + "[creates ttyS2-ttyS63]\n" \ + "# makedevs /dev/hda b 3 0 0 8 s\n" \ + "[creates hda,hda1-hda8]\n" + +#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK +#define USAGE_MD5_SHA1_SUM_CHECK(a) a +#else +#define USAGE_MD5_SHA1_SUM_CHECK(a) +#endif + +#define md5sum_trivial_usage \ + "[OPTION] [FILEs...]" \ + USAGE_MD5_SHA1_SUM_CHECK("\n or: md5sum [OPTION] -c [FILE]") +#define md5sum_full_usage \ + "Print" USAGE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums.\n\n" \ + "Options:\n" \ + "With no FILE, or when FILE is -, read standard input." \ + USAGE_MD5_SHA1_SUM_CHECK("\n\n" \ + "\t-c\tcheck MD5 sums against given list\n" \ + "\nThe following two options are useful only when verifying checksums:\n" \ + "\t-s\tdon't output anything, status code shows success\n" \ + "\t-w\twarn about improperly formated MD5 checksum lines") +#define md5sum_example_usage \ + "$ md5sum < busybox\n" \ + "6fd11e98b98a58f64ff3398d7b324003\n" \ + "$ md5sum busybox\n" \ + "6fd11e98b98a58f64ff3398d7b324003 busybox\n" \ + "$ md5sum -c -\n" \ + "6fd11e98b98a58f64ff3398d7b324003 busybox\n" \ + "busybox: OK\n" \ + "^D\n" + +#define mesg_trivial_usage \ + "[y|n]" +#define mesg_full_usage \ + "mesg controls write access to your terminal\n" \ + "\ty\tAllow write access to your terminal.\n" \ + "\tn\tDisallow write access to your terminal.\n" + +#define mkdir_trivial_usage \ + "[OPTION] DIRECTORY..." +#define mkdir_full_usage \ + "Create the DIRECTORY(ies) if they do not already exist\n\n" \ + "Options:\n" \ + "\t-m\tset permission mode (as in chmod), not rwxrwxrwx - umask\n" \ + "\t-p\tno error if existing, make parent directories as needed" +#define mkdir_example_usage \ + "$ mkdir /tmp/foo\n" \ + "$ mkdir /tmp/foo\n" \ + "/tmp/foo: File exists\n" \ + "$ mkdir /tmp/foo/bar/baz\n" \ + "/tmp/foo/bar/baz: No such file or directory\n" \ + "$ mkdir -p /tmp/foo/bar/baz\n" + +#define mkfifo_trivial_usage \ + "[OPTIONS] name" +#define mkfifo_full_usage \ + "Creates a named pipe (identical to 'mknod name p')\n\n" \ + "Options:\n" \ + "\t-m\tcreate the pipe using the specified mode (default a=rw)" + +#define mkfs_minix_trivial_usage \ + "[-c | -l filename] [-nXX] [-iXX] /dev/name [blocks]" +#define mkfs_minix_full_usage \ + "Make a MINIX filesystem.\n\n" \ + "Options:\n" \ + "\t-c\t\tCheck the device for bad blocks\n" \ + "\t-n [14|30]\tSpecify the maximum length of filenames\n" \ + "\t-i INODES\tSpecify the number of inodes for the filesystem\n" \ + "\t-l FILENAME\tRead the bad blocks list from FILENAME\n" \ + "\t-v\t\tMake a Minix version 2 filesystem" + +#define mknod_trivial_usage \ + "[OPTIONS] NAME TYPE MAJOR MINOR" +#define mknod_full_usage \ + "Create a special file (block, character, or pipe).\n\n" \ + "Options:\n" \ + "\t-m\tcreate the special file using the specified mode (default a=rw)\n\n" \ + "TYPEs include:\n" \ + "\tb:\tMake a block (buffered) device.\n" \ + "\tc or u:\tMake a character (un-buffered) device.\n" \ + "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes." +#define mknod_example_usage \ + "$ mknod /dev/fd0 b 2 0\n" \ + "$ mknod -m 644 /tmp/pipe p\n" + +#define mkswap_trivial_usage \ + "[-c] [-v0|-v1] device [block-count]" +#define mkswap_full_usage \ + "Prepare a disk partition to be used as a swap partition.\n\n" \ + "Options:\n" \ + "\t-c\t\tCheck for read-ability.\n" \ + "\t-v0\t\tMake version 0 swap [max 128 Megs].\n" \ + "\t-v1\t\tMake version 1 swap [big!] (default for kernels >\n\t\t\t2.1.117).\n" \ + "\tblock-count\tNumber of block to use (default is entire partition)." + +#define mktemp_trivial_usage \ + "[-dq] TEMPLATE" +#define mktemp_full_usage \ + "Creates a temporary file with its name based on TEMPLATE.\n" \ + "TEMPLATE is any name with six `Xs' (i.e., /tmp/temp.XXXXXX).\n\n" \ + "Options:\n" \ + "\t-d\t\tMake a directory instead of a file\n" \ + "\t-q\t\tFail silently if an error occurs" +#define mktemp_example_usage \ + "$ mktemp /tmp/temp.XXXXXX\n" \ + "/tmp/temp.mWiLjM\n" \ + "$ ls -la /tmp/temp.mWiLjM\n" \ + "-rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM\n" + +#define modprobe_trivial_usage \ + "[-knqrsv] [MODULE ...]" +#define modprobe_full_usage \ + "Used for high level module loading and unloading.\n\n" \ + "Options:\n" \ + "\t-k\tMake module autoclean-able.\n" \ + "\t-n\tJust show what would be done.\n" \ + "\t-q\tQuiet output.\n" \ + "\t-r\tRemove module (stacks) or do autoclean.\n" \ + "\t-s\tReport via syslog instead of stderr.\n" \ + "\t-v\tVerbose output." +#define modprobe_example_usage \ + "$ modprobe cdrom\n" + +#define more_trivial_usage \ + "[FILE ...]" +#define more_full_usage \ + "More is a filter for viewing FILE one screenful at a time." +#define more_example_usage \ + "$ dmesg | more\n" + +#ifdef CONFIG_FEATURE_MOUNT_LOOP + #define USAGE_MOUNT_LOOP(a) a +#else + #define USAGE_MOUNT_LOOP(a) +#endif +#ifdef CONFIG_FEATURE_MTAB_SUPPORT + #define USAGE_MTAB(a) a +#else + #define USAGE_MTAB(a) +#endif +#define mount_trivial_usage \ + "[flags] DEVICE NODE [-o options,more-options]" +#define mount_full_usage \ + "Mount a filesystem. Autodetection of filesystem type requires the\n" \ + "/proc filesystem be already mounted.\n\n" \ + "Flags:\n" \ + "\t-a:\t\tMount all filesystems in fstab.\n" \ + USAGE_MTAB( \ + "\t-f:\t\t\"Fake\" Add entry to mount table but don't mount it.\n" \ + "\t-n:\t\tDon't write a mount table entry.\n" \ + ) \ + "\t-o option:\tOne of many filesystem options, listed below.\n" \ + "\t-r:\t\tMount the filesystem read-only.\n" \ + "\t-t fs-type:\tSpecify the filesystem type.\n" \ + "\t-w:\t\tMount for reading and writing (default).\n" \ + "\n" \ + "Options for use with the \"-o\" flag:\n" \ + "\tasync/sync:\tWrites are asynchronous / synchronous.\n" \ + "\tatime/noatime:\tEnable / disable updates to inode access times.\n" \ + "\tdev/nodev:\tAllow use of special device files / disallow them.\n" \ + "\texec/noexec:\tAllow use of executable files / disallow them.\n" \ + USAGE_MOUNT_LOOP( \ + "\tloop:\t\tMounts a file via loop device.\n" \ + ) \ + "\tsuid/nosuid:\tAllow set-user-id-root programs / disallow them.\n" \ + "\tremount:\tRe-mount a mounted filesystem, changing its flags.\n" \ + "\tro/rw:\t\tMount for read-only / read-write.\n" \ + "\tbind:\t\tUse the linux 2.4.x \"bind\" feature.\n" \ + "\nThere are EVEN MORE flags that are specific to each filesystem.\n" \ + "You'll have to see the written documentation for those filesystems." +#define mount_example_usage \ + "$ mount\n" \ + "/dev/hda3 on / type minix (rw)\n" \ + "proc on /proc type proc (rw)\n" \ + "devpts on /dev/pts type devpts (rw)\n" \ + "$ mount /dev/fd0 /mnt -t msdos -o ro\n" \ + "$ mount /tmp/diskimage /opt -t ext2 -o loop\n" + +#define mt_trivial_usage \ + "[-f device] opcode value" +#define mt_full_usage \ + "Control magnetic tape drive operation\n" \ + "\nAvailable Opcodes:\n\n" \ + "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n" \ + "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n" \ + "ras3 reset retension rewind rewoffline seek setblk setdensity\n" \ + "setpart tell unload unlock weof wset" + +#define mv_trivial_usage \ + "[OPTION]... SOURCE DEST\n" \ + "or: mv [OPTION]... SOURCE... DIRECTORY" +#define mv_full_usage \ + "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\n" \ + "Options:\n" \ + "\t-f\tdon't prompt before overwriting\n" \ + "\t-i\tinteractive, prompt before overwrite" +#define mv_example_usage \ + "$ mv /tmp/foo /bin/bar\n" + +#define nameif_trivial_usage \ + "[-s] [-c FILE] [{IFNAME MACADDR}]" +#define nameif_full_usage \ + "Nameif renaming network interface while it in the down state.\n\n" \ + "Options:\n" \ + "\t-c FILE\t\tUse configuration file (default is /etc/mactab)\n" \ + "\t-s\t\tUse syslog (LOCAL0 facility).\n" \ + "\tIFNAME MACADDR\tnew_interface_name interface_mac_address" +#define nameif_example_usage \ + "$ nameif -s dmz0 00:A0:C9:8C:F6:3F\n" \ + " or\n" \ + "$ nameif -c /etc/my_mactab_file\n" \ + +#define nc_trivial_usage \ + "[OPTIONS] [IP] [port]" +#define nc_full_usage \ + "Netcat opens a pipe to IP:port\n\n" \ + "Options:\n" \ + "\t-l\t\tlisten mode, for inbound connects\n" \ + "\t-p PORT\t\tlocal port number\n" \ + "\t-i SECS\t\tdelay interval for lines sent\n" \ + "\t-e PROG\t\tprogram to exec after connect (dangerous!)" +#define nc_example_usage \ + "$ nc foobar.somedomain.com 25\n" \ + "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n" \ + "help\n" \ + "214-Commands supported:\n" \ + "214- HELO EHLO MAIL RCPT DATA AUTH\n" \ + "214 NOOP QUIT RSET HELP\n" \ + "quit\n" \ + "221 foobar closing connection\n" + +#define netstat_trivial_usage \ + "[-laenrtuwx]" +#define netstat_full_usage \ + "Netstat displays Linux networking information.\n\n" \ + "Options:\n" \ + "\t-l display listening server sockets\n" \ + "\t-a display all sockets (default: connected)\n" \ + "\t-e display other/more information\n" \ + "\t-n don't resolve names\n" \ + "\t-r display routing table\n" \ + "\t-t tcp sockets\n" \ + "\t-u udp sockets\n" \ + "\t-w raw sockets\n" \ + "\t-x unix sockets" + +#define nslookup_trivial_usage \ + "[HOST] [SERVER]" +#define nslookup_full_usage \ + "Queries the nameserver for the IP address of the given HOST\n" \ + "optionally using a specified DNS server" +#define nslookup_example_usage \ + "$ nslookup localhost\n" \ + "Server: default\n" \ + "Address: default\n" \ + "\n" \ + "Name: debian\n" \ + "Address: 127.0.0.1\n" + +#define od_trivial_usage \ + "[-aBbcDdeFfHhIiLlOovXx] [FILE]" +#define od_full_usage \ + "Write an unambiguous representation, octal bytes by default, of FILE\n"\ + "to standard output. With no FILE, or when FILE is -, read standard input." + +#define openvt_trivial_usage \ + " [ARGS...]" +#define openvt_full_usage \ + "Start a command on a new virtual terminal" +#define openvt_example_usage \ + "openvt 2 /bin/ash\n" + +#ifdef CONFIG_FEATURE_SHA1_PASSWORDS + #define PASSWORD_ALG_TYPES(a) a +#else + #define PASSWORD_ALG_TYPES(a) +#endif +#define passwd_trivial_usage \ + "[OPTION] [name]" +#define passwd_full_usage \ + "Change a user password. If no name is specified,\n" \ + "changes the password for the current user.\n" \ + "Options:\n" \ + "\t-a\tDefine which algorithm shall be used for the password.\n" \ + "\t\t\t(Choices: des, md5" \ + PASSWORD_ALG_TYPES(", sha1") \ + ")\n\t-d\tDelete the password for the specified user account.\n" \ + "\t-l\tLocks (disables) the specified user account.\n" \ + "\t-u\tUnlocks (re-enables) the specified user account." + +#define patch_trivial_usage \ + "[-p]" +#define patch_full_usage \ + "[-p]" +#define patch_example_usage \ + "$ patch -p1 ]" +#define poweroff_full_usage \ + "Halt the system and request that the kernel shut off the power.\n" \ + "Options:\n" \ + "\t-d\t\tdelay interval for shutting off." + +#define printf_trivial_usage \ + "FORMAT [ARGUMENT...]" +#define printf_full_usage \ + "Formats and prints ARGUMENT(s) according to FORMAT,\n" \ + "Where FORMAT controls the output exactly as in C printf." +#define printf_example_usage \ + "$ printf "Val=%d\\n" 5\n" \ + "Val=5\n" + +#ifdef CONFIG_SELINUX +#define USAGE_NONSELINUX(a) +#else +#define USAGE_NONSELINUX(a) a +#endif + +#define ps_trivial_usage \ + "" +#define ps_full_usage \ + "Report process status\n" \ + USAGE_NONSELINUX("\n\tThis version of ps accepts no options.") \ + USAGE_SELINUX("\nOptions:\n\t-c\tshow SE Linux context") + +#define ps_example_usage \ + "$ ps\n" \ + " PID Uid Gid State Command\n" \ + " 1 root root S init\n" \ + " 2 root root S [kflushd]\n" \ + " 3 root root S [kupdate]\n" \ + " 4 root root S [kpiod]\n" \ + " 5 root root S [kswapd]\n" \ + " 742 andersen andersen S [bash]\n" \ + " 743 andersen andersen S -bash\n" \ + " 745 root root S [getty]\n" \ + " 2990 andersen andersen R ps\n" + +#define pwd_trivial_usage \ + "" +#define pwd_full_usage \ + "Print the full filename of the current working directory." +#define pwd_example_usage \ + "$ pwd\n" \ + "/root\n" + +#define rdate_trivial_usage \ + "[-sp] HOST" +#define rdate_full_usage \ + "Get and possibly set the system date and time from a remote HOST.\n\n" \ + "Options:\n" \ + "\t-s\tSet the system date and time (default).\n" \ + "\t-p\tPrint the date and time." + +#define readlink_trivial_usage \ + "" +#define readlink_full_usage \ + "Displays the value of a symbolic link." + +#define realpath_trivial_usage \ + "pathname ..." +#define realpath_full_usage \ + "Returns the absolute pathnames of given argument." + +#define reboot_trivial_usage \ + "[-d]" +#define reboot_full_usage \ + "Reboot the system.\n" \ + "Options:\n" \ + "\t-d\t\tdelay interval for rebooting." + +#define renice_trivial_usage \ + "priority pid [pid ...]" +#define renice_full_usage \ + "Changes priority of running processes. Allowed priorities range\n" \ + "from 20 (the process runs only when nothing else is running) to 0\n" \ + "(default priority) to -20 (almost nothing else ever gets to run)." + +#define reset_trivial_usage \ + "" +#define reset_full_usage \ + "Resets the screen." + +#define rm_trivial_usage \ + "[OPTION]... FILE..." +#define rm_full_usage \ + "Remove (unlink) the FILE(s). You may use '--' to\n" \ + "indicate that all following arguments are non-options.\n\n" \ + "Options:\n" \ + "\t-i\t\talways prompt before removing each destination\n" \ + "\t-f\t\tremove existing destinations, never prompt\n" \ + "\t-r or -R\tremove the contents of directories recursively" +#define rm_example_usage \ + "$ rm -rf /tmp/foo\n" + +#define rmdir_trivial_usage \ + "[OPTION]... DIRECTORY..." +#define rmdir_full_usage \ + "Remove the DIRECTORY(ies), if they are empty." +#define rmdir_example_usage \ + "# rmdir /tmp/foo\n" + +#define rmmod_trivial_usage \ + "[OPTION]... [MODULE]..." +#define rmmod_full_usage \ + "Unloads the specified kernel modules from the kernel.\n\n" \ + "Options:\n" \ + "\t-a\tRemove all unused modules (recursively)" +#define rmmod_example_usage \ + "$ rmmod tulip\n" + +#ifdef CONFIG_FEATURE_IPV6 + #define USAGE_ROUTE_IPV6(a) a +#else + #define USAGE_ROUTE_IPV6(a) "\t" +#endif + + +#define route_trivial_usage \ + "[{add|del|delete}]" +#define route_full_usage \ + "Edit the kernel's routing tables.\n\n" \ + "Options:\n" \ + "\t-n\t\tDont resolve names.\n" \ + "\t-e\t\tDisplay other/more information.\n" \ + "\t-A inet" USAGE_ROUTE_IPV6("{6}") "\tSelect address family." + +#define rpm_trivial_usage \ + "-i -q[ildc]p package.rpm" +#define rpm_full_usage \ + "Manipulates RPM packages" \ + "\n\nOptions:" \ + "\n\t-i Install package" \ + "\n\t-q Query package" \ + "\n\t-p Query uninstalled package" \ + "\n\t-i Show information" \ + "\n\t-l List contents" \ + "\n\t-d List documents" \ + "\n\t-c List config files" + +#define rpm2cpio_trivial_usage \ + "package.rpm" +#define rpm2cpio_full_usage \ + "Outputs a cpio archive of the rpm file." + +#define run_parts_trivial_usage \ + "[-t] [-a ARG] [-u MASK] DIRECTORY" +#define run_parts_full_usage \ + "Run a bunch of scripts in a directory.\n\n" \ + "Options:\n" \ + "\t-t\tPrints what would be run, but does not actually run anything.\n" \ + "\t-a ARG\tPass ARG as an argument for every program invoked.\n" \ + "\t-u MASK\tSet the umask to MASK before executing every program." + +#define rx_trivial_usage \ + "FILE" +#define rx_full_usage \ + "Receive a file using the xmodem protocol." +#define rx_example_usage \ + "$ rx /tmp/foo\n" + +#define sed_trivial_usage \ + "[-efinr] pattern [files...]" +#define sed_full_usage \ + "Options:\n" \ + "\t-e script\tadd the script to the commands to be executed\n" \ + "\t-f scriptfile\tadd script-file contents to the\n" \ + "\t\t\tcommands to be executed\n" \ + "\t-i\t\tedit files in-place\n" \ + "\t-n\t\tsuppress automatic printing of pattern space\n" \ + "\t-r\t\tuse extended regular expression syntax\n" \ + "\n" \ + "If no -e or -f is given, the first non-option argument is taken as the sed\n"\ + "script to interpret. All remaining arguments are names of input files; if no\n"\ + "input files are specified, then the standard input is read. Source files\n" \ + "will not be modified unless -i option is given." + +#define sed_example_usage \ + "$ echo "foo" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \ + "bar\n" + +#define seq_trivial_usage \ + "[first [increment]] last" +#define seq_full_usage \ + "Print numbers from FIRST to LAST, in steps of INCREMENT.\n" \ + "FIRST, INCREMENT default to 1\n" \ + "Arguments:\n" \ + "\tLAST\n" \ + "\tFIRST\tLAST\n" \ + "\tFIRST\tINCREMENT\tLAST" + +#define setkeycodes_trivial_usage \ + "SCANCODE KEYCODE ..." +#define setkeycodes_full_usage \ + "Set entries into the kernel's scancode-to-keycode map,\n" \ + "allowing unusual keyboards to generate usable keycodes.\n\n" \ + "SCANCODE may be either xx or e0xx (hexadecimal),\n" \ + "and KEYCODE is given in decimal" +#define setkeycodes_example_usage \ + "$ setkeycodes e030 127\n" + +#define lash_trivial_usage \ + "[FILE]...\n" \ + "or: sh -c command [args]..." +#define lash_full_usage \ + "The BusyBox LAme SHell (command interpreter)" +#define lash_notes_usage \ + "This command does not yet have proper documentation.\n\n" \ + "Use lash just as you would use any other shell. It properly handles pipes,\n" \ + "redirects, job control, can be used as the shell for scripts, and has a\n" \ + "sufficient set of builtins to do what is needed. It does not (yet) support\n" \ + "Bourne Shell syntax. If you need things like "if-then-else", "while", and such\n" \ + "use ash or bash. If you just need a very simple and extremely small shell,\n" \ + "this will do the job." + +#define last_trivial_usage \ + "" +#define last_full_usage \ + "Shows listing of the last users that logged into the system" + +#define sha1sum_trivial_usage \ + "[OPTION] [FILEs...]" \ + USAGE_MD5_SHA1_SUM_CHECK("\n or: sha1sum [OPTION] -c [FILE]") +#define sha1sum_full_usage \ + "Print" USAGE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums.\n\n" \ + "Options:\n" \ + "With no FILE, or when FILE is -, read standard input." \ + USAGE_MD5_SHA1_SUM_CHECK("\n\n" \ + "\t-c\tcheck SHA1 sums against given list\n" \ + "\nThe following two options are useful only when verifying checksums:\n" \ + "\t-s\tdon't output anything, status code shows success\n" \ + "\t-w\twarn about improperly formated SHA1 checksum lines") + +#ifdef CONFIG_FEATURE_FANCY_SLEEP + #define USAGE_FANCY_SLEEP(a) a + #define USAGE_NOT_FANCY_SLEEP(a) +#else + #define USAGE_FANCY_SLEEP(a) + #define USAGE_NOT_FANCY_SLEEP(a) a +#endif + +#define sleep_trivial_usage \ + USAGE_FANCY_SLEEP("[") "N" USAGE_FANCY_SLEEP("]...") +#define sleep_full_usage \ + USAGE_NOT_FANCY_SLEEP("Pause for N seconds.") \ + USAGE_FANCY_SLEEP( \ + "Pause for a time equal to the total of the args given, where each arg can\n" \ + "\t\thave an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays.") +#define sleep_example_usage \ + "$ sleep 2\n" \ + "[2 second delay results]\n" \ + USAGE_FANCY_SLEEP("$ sleep 1d 3h 22m 8s\n" \ + "[98528 second delay results]\n") + +#ifdef CONFIG_FEATURE_SORT_UNIQUE + #define USAGE_SORT_UNIQUE(a) a +#else + #define USAGE_SORT_UNIQUE(a) +#endif +#ifdef CONFIG_FEATURE_SORT_REVERSE + #define USAGE_SORT_REVERSE(a) a +#else + #define USAGE_SORT_REVERSE(a) +#endif +#define sort_trivial_usage \ + "[-n" USAGE_SORT_REVERSE("r") USAGE_SORT_UNIQUE("u") "] [FILE]..." +#define sort_full_usage \ + "Sorts lines of text in the specified files\n\n"\ + "Options:\n" \ + USAGE_SORT_UNIQUE("\t-u\tsuppress duplicate lines\n") \ + USAGE_SORT_REVERSE("\t-r\tsort in reverse order\n") \ + "\t-n\tsort numerics" +#define sort_example_usage \ + "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" \ + "a\n" \ + "b\n" \ + "c\n" \ + "d\n" \ + "e\n" \ + "f\n" + +#define start_stop_daemon_trivial_usage \ + "[OPTIONS] [--start|--stop] ... [-- arguments...]\n" +#define start_stop_daemon_full_usage \ + "Program to start and stop services."\ + "\n\nOptions:"\ + "\n\t-S|--start\t\t\tstart"\ + "\n\t-K|--stop\t\t\tstop"\ + "\n\t-a|--startas \t\tstarts process specified by pathname"\ + "\n\t-b|--background\t\t\tforce process into background"\ + "\n\t-u|--user |\tstop this user's processes"\ + "\n\t-x|--exec \t\tprogram to either start or check"\ + "\n\t-m|--make-pidfile \tcreate the -p file and enter pid in it"\ + "\n\t-n|--name \tstop processes with this name"\ + "\n\t-p|--pidfile \t\tsave or load pid using a pid-file"\ + "\n\t-q|--quiet\t\t\tbe quiet" \ + "\n\t-s|--signal \t\tsignal to send (default TERM)" + +#define strings_trivial_usage \ + "[-afo] [-n length] [file ... ]" +#define strings_full_usage \ + "Display printable strings in a binary file." \ + "\n\nOptions:" \ + "\n\t-a\tScan the whole files (this is the default)."\ + "\n\t-f\tPrecede each string with the name of the file where it was found." \ + "\n\t-n N\tSpecifies that at least N characters forms a sequence (default 4)" \ + "\n\t-o\tEach string is preceded by its decimal offset in the file." + +#define stty_trivial_usage \ + "[-a|g] [-F DEVICE] [SETTING]..." +#define stty_full_usage \ + "Without arguments, prints baud rate, line discipline," \ + "\nand deviations from stty sane." \ + "\n\nOptions:" \ + "\n\t-F DEVICE\topen device instead of stdin" \ + "\n\t-a\t\tprint all current settings in human-readable form" \ + "\n\t-g\t\tprint in stty-readable form" \ + "\n\t[SETTING]\tsee manpage" + +#define su_trivial_usage \ + "[OPTION]... [-] [username]" +#define su_full_usage \ + "Change user id or become root.\n" \ + "Options:\n" \ + "\t-p\tPreserve environment" + +#define sulogin_trivial_usage \ + "[OPTION]... [tty-device]" +#define sulogin_full_usage \ + "Single user login\n" \ + "Options:\n" \ + "\t-f\tDo not authenticate (user already authenticated)\n" \ + "\t-h\tName of the remote host for this login.\n" \ + "\t-p\tPreserve environment." + +#define swapoff_trivial_usage \ + "[OPTION] [DEVICE]" +#define swapoff_full_usage \ + "Stop swapping virtual memory pages on DEVICE.\n\n" \ + "Options:\n" \ + "\t-a\tStop swapping on all swap devices" + +#define swapon_trivial_usage \ + "[OPTION] [DEVICE]" +#define swapon_full_usage \ + "Start swapping virtual memory pages on DEVICE.\n\n" \ + "Options:\n" \ + "\t-a\tStart swapping on all swap devices" + +#define sync_trivial_usage \ + "" +#define sync_full_usage \ + "Write all buffered filesystem blocks to disk." + + +#ifdef CONFIG_FEATURE_ROTATE_LOGFILE + #define USAGE_ROTATE_LOGFILE(a) a +#else + #define USAGE_ROTATE_LOGFILE(a) +#endif +#ifdef CONFIG_FEATURE_REMOTE_LOG + #define USAGE_REMOTE_LOG(a) a +#else + #define USAGE_REMOTE_LOG(a) +#endif +#ifdef CONFIG_FEATURE_IPC_SYSLOG + #define USAGE_IPC_LOG(a) a +#else + #define USAGE_IPC_LOG(a) +#endif + +#ifdef CONFIG_SYSCTL +#define sysctl_trivial_usage \ + "[OPTIONS]... [VALUE]...\n" +#define sysctl_full_usage + "sysctl - configure kernel parameters at runtime\n\n" \ + "Options:\n" \ + "\t-n\tUse this option to disable printing of the key name when printing values.\n" \ + "\t-w\tUse this option when you want to change a sysctl setting.\n" \ + "\t-p\tLoad in sysctl settings from the file specified or /etc/sysctl.conf if none given.\n" \ + "\t-a\tDisplay all values currently available.\n" \ + "\t-A\tDisplay all values currently available in table form." +#define sysctl_example_usage + "sysctl [-n] variable ...\n" \ + "sysctl [-n] -w variable=value ...\n" \ + "sysctl [-n] -a\n" \ + "sysctl [-n] -p \t(default /etc/sysctl.conf)\n" \ + "sysctl [-n] -A\n" +#endif + +#define syslogd_trivial_usage \ + "[OPTION]..." +#define syslogd_full_usage \ + "Linux system and kernel logging utility.\n" \ + "Note that this version of syslogd ignores /etc/syslog.conf.\n\n" \ + "Options:\n" \ + "\t-m MIN\t\tMinutes between MARK lines (default=20, 0=off)\n" \ + "\t-n\t\tRun as a foreground process\n" \ + "\t-O FILE\t\tUse an alternate log file (default=/var/log/messages)\n" \ + "\t-S\t\tMake logging output smaller." \ + USAGE_ROTATE_LOGFILE( \ + "\n\t-s SIZE\t\tMax size (KB) before rotate (default=200KB, 0=off)\n" \ + "\t-b NUM\t\tNumber of rotated logs to keep (default=1, max=99, 0=purge)") \ + USAGE_REMOTE_LOG( \ + "\n\t-R HOST[:PORT]\tLog to IP or hostname on PORT (default PORT=514/UDP)\n" \ + "\t-L\t\tLog locally and via network logging (default is network only)") \ + USAGE_IPC_LOG( \ + "\n\t-C [size(KiB)]\tLog to a circular buffer (read the buffer using logread)") +#define syslogd_example_usage \ + "$ syslogd -R masterlog:514\n" \ + "$ syslogd -R 192.168.1.1:601\n" + + +#ifndef CONFIG_FEATURE_FANCY_TAIL + #define USAGE_UNSIMPLE_TAIL(a) +#else + #define USAGE_UNSIMPLE_TAIL(a) a +#endif +#define tail_trivial_usage \ + "[OPTION]... [FILE]..." +#define tail_full_usage \ + "Print last 10 lines of each FILE to standard output.\n" \ + "With more than one FILE, precede each with a header giving the\n" \ + "file name. With no FILE, or when FILE is -, read standard input.\n\n" \ + "Options:\n" \ + USAGE_UNSIMPLE_TAIL("\t-c N[kbm]\toutput the last N bytes\n") \ + "\t-n N[kbm]\tprint last N lines instead of last 10\n" \ + "\t-f\t\toutput data as the file grows" \ + USAGE_UNSIMPLE_TAIL( "\n\t-q\t\tnever output headers giving file names\n" \ + "\t-s SEC\t\twait SEC seconds between reads with -f\n" \ + "\t-v\t\talways output headers giving file names\n\n" \ + "If the first character of N (bytes or lines) is a '+', output begins with \n" \ + "the Nth item from the start of each file, otherwise, print the last N items\n" \ + "in the file. N bytes may be suffixed by k (x1024), b (x512), or m (1024^2)." ) +#define tail_example_usage \ + "$ tail -n 1 /etc/resolv.conf\n" \ + "nameserver 10.0.0.1\n" + +#ifdef CONFIG_FEATURE_TAR_CREATE + #define USAGE_TAR_CREATE(a) a +#else + #define USAGE_TAR_CREATE(a) +#endif +#ifdef CONFIG_FEATURE_TAR_EXCLUDE + #define USAGE_TAR_EXCLUDE(a) a +#else + #define USAGE_TAR_EXCLUDE(a) +#endif +#ifdef CONFIG_FEATURE_TAR_GZIP + #define USAGE_TAR_GZIP(a) a +#else + #define USAGE_TAR_GZIP(a) +#endif +#ifdef CONFIG_FEATURE_TAR_BZIP2 + #define USAGE_TAR_BZIP2(a) a +#else + #define USAGE_TAR_BZIP2(a) +#endif +#ifdef CONFIG_FEATURE_TAR_COMPRESS + #define USAGE_TAR_COMPRESS(a) a +#else + #define USAGE_TAR_COMPRESS(a) +#endif + +#define tar_trivial_usage \ + "-[" USAGE_TAR_CREATE("c") USAGE_TAR_GZIP("z") USAGE_TAR_BZIP2("j") USAGE_TAR_COMPRESS("Z") "xtvO] " \ + USAGE_TAR_EXCLUDE("[-X FILE]") \ + "[-f TARFILE] [-C DIR] [FILE(s)] ..." +#define tar_full_usage \ + "Create, extract, or list files from a tar file.\n\n" \ + "Options:\n" \ + USAGE_TAR_CREATE("\tc\t\tcreate\n") \ + "\tx\t\textract\n" \ + "\tt\t\tlist\n" \ + "\nArchive format selection:\n" \ + USAGE_TAR_GZIP("\tz\t\tFilter the archive through gzip\n") \ + USAGE_TAR_BZIP2("\tj\t\tFilter the archive through bzip2\n") \ + USAGE_TAR_COMPRESS("\tZ\t\tFilter the archive through compress\n") \ + "\nFile selection:\n" \ + "\tf\t\tname of TARFILE or \"-\" for stdin\n" \ + "\tO\t\textract to stdout\n" \ + USAGE_TAR_EXCLUDE( \ + "\texclude\t\tfile to exclude\n" \ + "\tX\t\tfile with names to exclude\n" \ + ) \ + "\tC\t\tchange to directory DIR before operation\n" \ + "\tv\t\tverbosely list files processed" +#define tar_example_usage \ + "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" \ + "$ tar -cf /tmp/tarball.tar /usr/local\n" + +#define tee_trivial_usage \ + "[OPTION]... [FILE]..." +#define tee_full_usage \ + "Copy standard input to each FILE, and also to standard output.\n\n" \ + "Options:\n" \ + "\t-a\tappend to the given FILEs, do not overwrite\n" \ + "\t-i\tignore interrupt signals (SIGINT)" +#define tee_example_usage \ + "$ echo "Hello" | tee /tmp/foo\n" \ + "$ cat /tmp/foo\n" \ + "Hello\n" + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +#define telnet_trivial_usage \ + "[-a] [-l USER] HOST [PORT]" +#define telnet_full_usage \ + "Telnet is used to establish interactive communication with another\n" \ + "computer over a network using the TELNET protocol.\n\n" \ + "Options:\n" \ + "\t-a\t\tAttempt an automatic login with the USER variable.\n" \ + "\t-l USER\t\tAttempt an automatic login with the USER argument.\n" \ + "\tHOST\t\tThe official name, alias or the IP address of the\n" \ + "\t\t\tremote host.\n" \ + "\tPORT\t\tThe remote port number to connect to. If it is not\n" \ + "\t\t\tspecified, the default telnet (23) port is used." +#else +#define telnet_trivial_usage \ + "HOST [PORT]" +#define telnet_full_usage \ + "Telnet is used to establish interactive communication with another\n"\ + "computer over a network using the TELNET protocol." +#endif + +#ifdef CONFIG_FEATURE_TELNETD_INETD +#define telnetd_trivial_usage \ + "(inetd mode) [OPTION]" +#define telnetd_full_usage \ + "Telnetd uses incoming TELNET connections via inetd.\n"\ + "Options:\n" \ + "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \ + "\t-f issue_file\tDisplay issue_file instead of /etc/issue." +#else +#define telnetd_trivial_usage \ + "[OPTION]" +#define telnetd_full_usage \ + "Telnetd listens for incoming TELNET connections on PORT.\n"\ + "Options:\n" \ + "\t-p PORT\tlisten for connections on PORT (default 23)\n"\ + "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n"\ + "\t-f issue_file\tDisplay issue_file instead of /etc/issue." +#endif + +#define test_trivial_usage \ + "EXPRESSION\n or [ EXPRESSION ]" +#define test_full_usage \ + "Checks file types and compares values returning an exit\n" \ + "code determined by the value of EXPRESSION." +#define test_example_usage \ + "$ test 1 -eq 2\n" \ + "$ echo $?\n" \ + "1\n" \ + "$ test 1 -eq 1\n" \ + "$ echo $?\n" \ + "0\n" \ + "$ [ -d /etc ]\n" \ + "$ echo $?\n" \ + "0\n" \ + "$ [ -d /junk ]\n" \ + "$ echo $?\n" \ + "1\n" + +#ifdef CONFIG_FEATURE_TFTP_GET + #define USAGE_TFTP_GET(a) a +#else + #define USAGE_TFTP_GET(a) +#endif +#ifdef CONFIG_FEATURE_TFTP_PUT + #define USAGE_TFTP_PUT(a) a +#else + #define USAGE_TFTP_PUT(a) +#endif +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + #define USAGE_TFTP_BS(a) a +#else + #define USAGE_TFTP_BS(a) +#endif + +#define tftp_trivial_usage \ + "[OPTION]... HOST [PORT]" +#define tftp_full_usage \ + "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ + "Options:\n" \ + "\t-l FILE\tLocal FILE.\n" \ + "\t-r FILE\tRemote FILE." \ + USAGE_TFTP_GET( \ + "\n\t-g\tGet file." \ + ) \ + USAGE_TFTP_PUT( \ + "\n\t-p\tPut file." \ + ) \ + USAGE_TFTP_BS( \ + "\n\t-b SIZE\tTransfer blocks of SIZE octets." \ + ) +#define time_trivial_usage \ + "[OPTION]... COMMAND [ARGS...]" +#define time_full_usage \ + "Runs the program COMMAND with arguments ARGS. When COMMAND finishes,\n" \ + "COMMAND's resource usage information is displayed\n\n" \ + "Options:\n" \ + "\t-v\tDisplays verbose resource usage information." + +#define top_trivial_usage \ + "[-d ]" +#define top_full_usage \ + "top provides an view of processor activity in real time.\n" \ + "This utility reads the status for all processes in /proc each \n" \ + "and shows the status for however many processes will fit on the screen.\n" \ + "This utility will not show processes that are started after program startup,\n" \ + "but it will show the EXIT status for and PIDs that exit while it is running." + +#define touch_trivial_usage \ + "[-c] FILE [FILE ...]" +#define touch_full_usage \ + "Update the last-modified date on the given FILE[s].\n\n" \ + "Options:\n" \ + "\t-c\tDo not create any files" +#define touch_example_usage \ + "$ ls -l /tmp/foo\n" \ + "/bin/ls: /tmp/foo: No such file or directory\n" \ + "$ touch /tmp/foo\n" \ + "$ ls -l /tmp/foo\n" \ + "-rw-rw-r-- 1 andersen andersen 0 Apr 15 01:11 /tmp/foo\n" + +#define tr_trivial_usage \ + "[-cds] STRING1 [STRING2]" +#define tr_full_usage \ + "Translate, squeeze, and/or delete characters from\n" \ + "standard input, writing to standard output.\n\n" \ + "Options:\n" \ + "\t-c\ttake complement of STRING1\n" \ + "\t-d\tdelete input characters coded STRING1\n" \ + "\t-s\tsqueeze multiple output characters of STRING2 into one character" +#define tr_example_usage \ + "$ echo "gdkkn vnqkc" | tr [a-y] [b-z]\n" \ + "hello world\n" + +#define traceroute_trivial_usage \ + "[-dnrv] [-m max_ttl] [-p port#] [-q nqueries]\n"\ + "\t[-s src_addr] [-t tos] [-w wait] host [data size]" +#define traceroute_full_usage \ + "trace the route ip packets follow going to \"host\"\n" \ + "Options:\n" \ + "\t-d\tset SO_DEBUG options to socket\n" \ + "\t-n\tPrint hop addresses numerically rather than symbolically\n" \ + "\t-r\tBypass the normal routing tables and send directly to a host\n" \ + "\t-v\tVerbose output\n" \ + "\t-m max_ttl\tSet the max time-to-live (max number of hops)\n" \ + "\t-p port#\tSet the base UDP port number used in probes\n" \ + "\t\t(default is 33434)\n" \ + "\t-q nqueries\tSet the number of probes per ``ttl'' to nqueries\n" \ + "\t\t(default is 3)\n" \ + "\t-s src_addr\tUse the following IP address as the source address\n" \ + "\t-t tos\tSet the type-of-service in probe packets to the following value\n" \ + "\t\t(default 0)\n" \ + "\t-w wait\tSet the time (in seconds) to wait for a response to a probe\n" \ + "\t\t(default 3 sec.)." + + +#define true_trivial_usage \ + "" +#define true_full_usage \ + "Return an exit code of TRUE (0)." +#define true_example_usage \ + "$ true\n" \ + "$ echo $?\n" \ + "0\n" + +#define tty_trivial_usage \ + "" +#define tty_full_usage \ + "Print the file name of the terminal connected to standard input.\n\n"\ + "Options:\n" \ + "\t-s\tprint nothing, only return an exit status" +#define tty_example_usage \ + "$ tty\n" \ + "/dev/tty2\n" + +#define udhcpc_trivial_usage \ + "[-fbnqv] [-c CLIENTID] [-H HOSTNAME] [-i INTERFACE]\n[-p pidfile] [-r IP] [-s script]" +#define udhcpc_full_usage \ + "\t-c,\t--clientid=CLIENTID\tClient identifier\n" \ + "\t-H,\t--hostname=HOSTNAME\tClient hostname\n" \ + "\t-h,\t \tAlias for -H\n" \ + "\t-f,\t--foreground\tDo not fork after getting lease\n" \ + "\t-b,\t--background\tFork to background if lease cannot be immediately negotiated.\n" \ + "\t-i,\t--interface=INTERFACE\tInterface to use (default: eth0)\n" \ + "\t-n,\t--now\tExit with failure if lease cannot be immediately negotiated.\n" \ + "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \ + "\t-q,\t--quit\tQuit after obtaining lease\n" \ + "\t-r,\t--request=IP\tIP address to request (default: none)\n" \ + "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \ + "\t-v,\t--version\tDisplay version" + +#define udhcpd_trivial_usage \ + "[configfile]\n" \ + +#define udhcpd_full_usage \ + "" + +#ifdef CONFIG_FEATURE_MOUNT_FORCE + #define USAGE_MOUNT_FORCE(a) a +#else + #define USAGE_MOUNT_FORCE(a) +#endif +#define umount_trivial_usage \ + "[flags] FILESYSTEM|DIRECTORY" +#define umount_full_usage \ + "Unmount file systems\n" \ + "\nFlags:\n" "\t-a\tUnmount all file systems" \ + USAGE_MTAB(" in /etc/mtab\n\t-n\tDon't erase /etc/mtab entries") \ + "\n\t-r\tTry to remount devices as read-only if mount is busy" \ + USAGE_MOUNT_FORCE("\n\t-f\tForce umount (i.e., unreachable NFS server)") \ + USAGE_MOUNT_LOOP("\n\t-l\tDo not free loop device (if a loop device has been used)") +#define umount_example_usage \ + "$ umount /dev/hdc1 \n" + +#define uname_trivial_usage \ + "[OPTION]..." +#define uname_full_usage \ + "Print certain system information. With no OPTION, same as -s.\n\n" \ + "Options:\n" \ + "\t-a\tprint all information\n" \ + "\t-m\tthe machine (hardware) type\n" \ + "\t-n\tprint the machine's network node hostname\n" \ + "\t-r\tprint the operating system release\n" \ + "\t-s\tprint the operating system name\n" \ + "\t-p\tprint the host processor type\n" \ + "\t-v\tprint the operating system version" +#define uname_example_usage \ + "$ uname -a\n" \ + "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n" + +#define uncompress_trivial_usage \ + "[-c] [-f] [ name ... ]" +#define uncompress_full_usage \ + "Uncompress .Z file[s]\n" \ + "Options:\n" \ + "\t-c\textract to stdout\n" \ + "\t-f\tforce overwrite an existing file" + +#define uniq_trivial_usage \ + "[OPTION]... [INPUT [OUTPUT]]" +#define uniq_full_usage \ + "Discard all but one of successive identical lines from INPUT\n" \ + "(or standard input), writing to OUTPUT (or standard output).\n\n" \ + "Options:\n" \ + "\t-c\tprefix lines by the number of occurrences\n" \ + "\t-d\tonly print duplicate lines\n" \ + "\t-u\tonly print unique lines\n" \ + "\t-f N\tskip the first N fields\n" \ + "\t-s N\tskip the first N chars (after any skipped fields)" +#define uniq_example_usage \ + "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n" \ + "a\n" \ + "b\n" \ + "c\n" + +#define unix2dos_trivial_usage \ + "[option] [FILE]" +#define unix2dos_full_usage \ + "Converts FILE from unix format to dos format. When no option\n" \ + "is given, the input is converted to the opposite output format.\n" \ + "When no file is given, uses stdin for input and stdout for output.\n" \ + "Options:\n" \ + "\t-u\toutput will be in UNIX format\n" \ + "\t-d\toutput will be in DOS format" + +#define unzip_trivial_usage \ + "[-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]" +#define unzip_full_usage \ + "Extracts files from ZIP archives.\n\n" \ + "Options:\n" \ + "\t-l\tlist archive contents (short form)\n" \ + "\t-n\tnever overwrite existing files (default)\n" \ + "\t-o\toverwrite files without prompting\n" \ + "\t-p\tsend output to stdout\n" \ + "\t-q\tbe quiet\n" \ + "\t-x\texclude these files\n" \ + "\t-d\textract files into this directory" + +#define uptime_trivial_usage \ + "" +#define uptime_full_usage \ + "Display the time since the last boot." +#define uptime_example_usage \ + "$ uptime\n" \ + " 1:55pm up 2:30, load average: 0.09, 0.04, 0.00\n" + +#define usleep_trivial_usage \ + "N" +#define usleep_full_usage \ + "Pause for N microseconds." +#define usleep_example_usage \ + "$ usleep 1000000\n" \ + "[pauses for 1 second]\n" + +#define uudecode_trivial_usage \ + "[FILE]..." +#define uudecode_full_usage \ + "Uudecode a file that is uuencoded.\n\n" \ + "Options:\n" \ + "\t-o FILE\tdirect output to FILE" +#define uudecode_example_usage \ + "$ uudecode -o busybox busybox.uu\n" \ + "$ ls -l busybox\n" \ + "-rwxr-xr-x 1 ams ams 245264 Jun 7 21:35 busybox\n" + +#define uuencode_trivial_usage \ + "[OPTION] [INFILE] REMOTEFILE" +#define uuencode_full_usage \ + "Uuencode a file.\n\n" \ + "Options:\n" \ + "\t-m\tuse base64 encoding per RFC1521" +#define uuencode_example_usage \ + "$ uuencode busybox busybox\n" \ + "begin 755 busybox\n" \ + "\n" \ + "$ uudecode busybox busybox > busybox.uu\n" \ + "$\n" + +#define vconfig_trivial_usage \ + "COMMAND [OPTIONS] ..." +#define vconfig_full_usage \ + "vconfig lets you create and remove virtual ethernet devices.\n\n" \ + "Options:\n" \ + "\tadd [interface-name] [vlan_id]\n" \ + "\trem [vlan-name]\n" \ + "\tset_flag [interface-name] [flag-num] [0 | 1]\n" \ + "\tset_egress_map [vlan-name] [skb_priority] [vlan_qos]\n" \ + "\tset_ingress_map [vlan-name] [skb_priority] [vlan_qos]\n" \ + "\tset_name_type [name-type]" + +#define vi_trivial_usage \ + "[OPTION] [FILE]..." +#define vi_full_usage \ + "edit FILE.\n\n" \ + "Options:\n" \ + "\t-R\tRead-only- do not write to the file." + +#define vlock_trivial_usage \ + "[OPTIONS]" +#define vlock_full_usage \ + "Lock a virtual terminal. A password is required to unlock\n" \ + "Options:\n" \ + "\t-a\tLock all VTs" + +#define watch_trivial_usage \ + "[-n ] COMMAND..." +#define watch_full_usage \ + "Executes a program periodically.\n" \ + "Options:\n" \ + "\t-n\tLoop period in seconds - default is 2." +#define watch_example_usage \ + "$ watch date\n" \ + "Mon Dec 17 10:31:40 GMT 2000\n" \ + "Mon Dec 17 10:31:42 GMT 2000\n" \ + "Mon Dec 17 10:31:44 GMT 2000" + +#define watchdog_trivial_usage \ + "[-t ] DEV" +#define watchdog_full_usage \ + "Periodically write to watchdog device DEV.\n" \ + "Options:\n" \ + "\t-t\tTimer period in seconds - default is 30." + +#define wc_trivial_usage \ + "[OPTION]... [FILE]..." +#define wc_full_usage \ + "Print line, word, and byte counts for each FILE, and a total line if\n" \ + "more than one FILE is specified. With no FILE, read standard input.\n\n" \ + "Options:\n" \ + "\t-c\tprint the byte counts\n" \ + "\t-l\tprint the newline counts\n" \ + "\t-L\tprint the length of the longest line\n" \ + "\t-w\tprint the word counts" +#define wc_example_usage \ + "$ wc /etc/passwd\n" \ + " 31 46 1365 /etc/passwd\n" + +#define wget_trivial_usage \ + "[-c|--continue] [-q|--quiet] [-O|--output-document file]\n" \ + "\t\t[--header 'header: value'] [-Y|--proxy on/off] [-P DIR] url" +#define wget_full_usage \ + "wget retrieves files via HTTP or FTP\n\n" \ + "Options:\n" \ + "\t-c\tcontinue retrieval of aborted transfers\n" \ + "\t-q\tquiet mode - do not print\n" \ + "\t-P\tSet directory prefix to DIR\n" \ + "\t-O\tsave to filename ('-' for stdout)\n" \ + "\t-Y\tuse proxy ('on' or 'off')" + +#define which_trivial_usage \ + "[COMMAND ...]" +#define which_full_usage \ + "Locates a COMMAND." +#define which_example_usage \ + "$ which login\n" \ + "/bin/login\n" + +#define who_trivial_usage \ + " " +#define who_full_usage \ + "Prints the current user names and related information" + +#define whoami_trivial_usage \ + "" +#define whoami_full_usage \ + "Prints the user name associated with the current effective user id." + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +#define USAGE_XARGS_CONFIRMATION(a) a +#else +#define USAGE_XARGS_CONFIRMATION(a) +#endif +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT +#define USAGE_XARGS_TERMOPT(a) a +#else +#define USAGE_XARGS_TERMOPT(a) +#endif +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM +#define USAGE_XARGS_ZERO_TERM(a) a +#else +#define USAGE_XARGS_ZERO_TERM(a) +#endif + + +#define xargs_trivial_usage \ + "[COMMAND] [OPTIONS] [ARGS...]" +#define xargs_full_usage \ + "Executes COMMAND on every item given by standard input.\n\n" \ + "Options:\n" \ + USAGE_XARGS_CONFIRMATION("\t-p\tPrompt the user about whether to run each command\n") \ + "\t-r\tDo not run command for empty readed lines\n" \ + USAGE_XARGS_TERMOPT("\t-x\tExit if the size is exceeded\n") \ + USAGE_XARGS_ZERO_TERM("\t-0\tInput filenames are terminated by a null character\n") \ + "\t-t\tPrint the command line on stderr before executing it." +#define xargs_example_usage \ + "$ ls | xargs gzip\n" \ + "$ find . -name '*.c' -print | xargs rm\n" + +#define yes_trivial_usage \ + "[OPTION]... [STRING]..." +#define yes_full_usage \ + "Repeatedly outputs a line with all specified STRING(s), or 'y'." + +#define zcat_trivial_usage \ + "FILE" +#define zcat_full_usage \ + "Uncompress to stdout." + +#endif /* __BB_USAGE_H__ */ diff --git a/busybox/init/Config.in b/busybox/init/Config.in new file mode 100644 index 000000000..4465e75a1 --- /dev/null +++ b/busybox/init/Config.in @@ -0,0 +1,72 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Init Utilities" + +config CONFIG_INIT + bool "init" + default n + help + init is the first program run when the system boots. + +config CONFIG_FEATURE_USE_INITTAB + bool " Support reading an inittab file?" + default y + depends on CONFIG_INIT + help + Allow init to read an inittab file when the system boot. + +config CONFIG_FEATURE_INITRD + bool " Support running init from within an initrd?" + default y + depends on CONFIG_INIT + help + Allow init to be called from an initrd as linuxrc. + +config CONFIG_FEATURE_INIT_COREDUMPS + bool " Support dumping core for child processes (debugging only)?" + default n + depends on CONFIG_INIT + help + If this option is enabled and the file /.init_enable_core + exists, then init will call setrlimit() to allow unlimited + core file sizes. If this option is disabled, processes + will not generate any core files. + +config CONFIG_FEATURE_EXTRA_QUIET + bool " Should init be _extra_ quiet on boot?" + default y + depends on CONFIG_INIT + help + Prevent init from logging some messages to the console + during boot. + +config CONFIG_HALT + bool "halt" + default y + help + Stop all processes and halt the system. + +config CONFIG_POWEROFF + bool "poweroff" + default y + help + Stop all processes and (try to) power off the system. + +config CONFIG_REBOOT + bool "reboot" + default y + help + Stop all processes and reboot the system. + +config CONFIG_MESG + bool "mesg" + default y + help + Mesg controls access to your terminal by others. It is typically + used to allow or disallow other users to write to your terminal + +endmenu + diff --git a/busybox/init/Makefile b/busybox/init/Makefile new file mode 100644 index 000000000..9b0a1d139 --- /dev/null +++ b/busybox/init/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/init +INIT_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include $(srcdir)/Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/init/Makefile.in b/busybox/init/Makefile.in new file mode 100644 index 000000000..807259dee --- /dev/null +++ b/busybox/init/Makefile.in @@ -0,0 +1,62 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +INIT_AR:=init.a +ifndef $(INIT_DIR) +INIT_DIR:=$(top_builddir)/init/ +endif +srcdir=$(top_srcdir)/init + +INIT-y:= +INIT-$(CONFIG_HALT) += halt.o +INIT-$(CONFIG_INIT) += init.o +INIT-$(CONFIG_MESG) += mesg.o +INIT-$(CONFIG_POWEROFF) += poweroff.o +INIT-$(CONFIG_REBOOT) += reboot.o + +ifeq ($(CONFIG_HALT), y) +CONFIG_INIT_SHARED=y +else +ifeq ($(CONFIG_INIT), y) +CONFIG_INIT_SHARED=y +else +ifeq ($(CONFIG_POWEROFF), y) +CONFIG_INIT_SHARED=y +else +ifeq ($(CONFIG_REBOOT), y) +CONFIG_INIT_SHARED=y +else +CONFIG_INIT_SHARED=n +endif +endif +endif +endif + +ifeq ($(CONFIG_INIT_SHARED), y) +INIT-$(CONFIG_INIT_SHARED) += init_shared.o +endif + +libraries-y+=$(INIT_DIR)$(INIT_AR) + +$(INIT_DIR)$(INIT_AR): $(patsubst %,$(INIT_DIR)%, $(INIT-y)) + $(AR) -ro $@ $(patsubst %,$(INIT_DIR)%, $(INIT-y)) + +$(INIT_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/init/halt.c b/busybox/init/halt.c new file mode 100644 index 000000000..bfc0042fa --- /dev/null +++ b/busybox/init/halt.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini halt implementation for busybox + * + * Copyright (C) 1999-2004 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" +#include "init_shared.h" + + +extern int halt_main(int argc, char **argv) +{ + char *delay; /* delay in seconds before rebooting */ + + if(bb_getopt_ulflags(argc, argv, "d:", &delay)) { + sleep(atoi(delay)); + } + +#ifndef CONFIG_INIT +#ifndef RB_HALT_SYSTEM +#define RB_HALT_SYSTEM 0xcdef0123 +#endif + return(bb_shutdown_system(RB_HALT_SYSTEM)); +#else + return kill_init(SIGUSR1); +#endif +} diff --git a/busybox/init/init.c b/busybox/init/init.c new file mode 100644 index 000000000..0c8dc89dc --- /dev/null +++ b/busybox/init/init.c @@ -0,0 +1,1214 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini init implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * Copyright (C) 1999-2004 by Erik Andersen + * 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 +#include "busybox.h" + +#include "init_shared.h" + + +#ifdef CONFIG_SYSLOGD +# include +#endif + + +#define INIT_BUFFS_SIZE 256 + +/* 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; + unsigned int port; + int irq; + int flags; + int xmit_fifo_size; + int custom_divisor; + int baud_base; + unsigned short close_delay; + char io_type; + char reserved_char[1]; + int hub6; + unsigned short closing_wait; /* time to wait before closing */ + unsigned short closing_wait2; /* no longer used... */ + unsigned char *iomem_base; + unsigned short iomem_reg_shift; + unsigned int port_high; + unsigned long iomap_base; /* cookie passed into ioremap */ + int reserved[1]; +}; + + +#ifndef _PATH_STDPATH +#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" +#endif + +#if defined CONFIG_FEATURE_INIT_COREDUMPS +/* + * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called + * before processes are spawned to set core file size as unlimited. + * This is for debugging only. Don't use this is production, unless + * you want core dumps lying about.... + */ +#define CORE_ENABLE_FLAG_FILE "/.init_enable_core" +#include +#include +#endif + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#define INITTAB "/etc/inittab" /* inittab file location */ +#ifndef INIT_SCRIPT +#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ +#endif + +#define MAXENV 16 /* Number of env. vars */ + +#define CONSOLE_BUFF_SIZE 32 + +/* Allowed init action types */ +#define SYSINIT 0x001 +#define RESPAWN 0x002 +#define ASKFIRST 0x004 +#define WAIT 0x008 +#define ONCE 0x010 +#define CTRLALTDEL 0x020 +#define SHUTDOWN 0x040 +#define RESTART 0x080 + +/* A mapping between "inittab" action name strings and action type codes. */ +struct init_action_type { + const char *name; + int action; +}; + +static const struct init_action_type actions[] = { + {"sysinit", SYSINIT}, + {"respawn", RESPAWN}, + {"askfirst", ASKFIRST}, + {"wait", WAIT}, + {"once", ONCE}, + {"ctrlaltdel", CTRLALTDEL}, + {"shutdown", SHUTDOWN}, + {"restart", RESTART}, + {0, 0} +}; + +/* Set up a linked list of init_actions, to be read from inittab */ +struct init_action { + pid_t pid; + char command[INIT_BUFFS_SIZE]; + char terminal[CONSOLE_BUFF_SIZE]; + struct init_action *next; + int action; +}; + +/* Static variables */ +static struct init_action *init_action_list = NULL; +static char console[CONSOLE_BUFF_SIZE] = _PATH_CONSOLE; + +#ifndef CONFIG_SYSLOGD +static char *log = VC_5; +#endif +static sig_atomic_t got_cont = 0; +static const int LOG = 0x1; +static const int CONSOLE = 0x2; + +#if defined CONFIG_FEATURE_EXTRA_QUIET +static const int MAYBE_CONSOLE = 0x0; +#else +#define MAYBE_CONSOLE CONSOLE +#endif +#ifndef RB_HALT_SYSTEM +static const int RB_HALT_SYSTEM = 0xcdef0123; +static const int RB_ENABLE_CAD = 0x89abcdef; +static const int RB_DISABLE_CAD = 0; + +#define RB_POWER_OFF 0x4321fedc +static const int RB_AUTOBOOT = 0x01234567; +#endif + +static const char * const environment[] = { + "HOME=/", + "PATH=" _PATH_STDPATH, + "SHELL=/bin/sh", + "USER=root", + NULL +}; + +/* Function prototypes */ +static void delete_init_action(struct init_action *a); +static int waitfor(const struct init_action *a); +static void halt_signal(int sig); + + +static void loop_forever(void) +{ + while (1) + sleep(1); +} + +/* Print a message to the specified device. + * Device may be bitwise-or'd from LOG | CONSOLE */ +#ifndef DEBUG_INIT +static inline void messageD(int device, const char *fmt, ...) +{ +} +#else +#define messageD message +#endif +static void message(int device, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); +static void message(int device, const char *fmt, ...) +{ + va_list arguments; + int l; + char msg[1024]; +#ifndef CONFIG_SYSLOGD + static int log_fd = -1; +#endif + + msg[0] = '\r'; + va_start(arguments, fmt); + l = vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments) + 1; + va_end(arguments); + +#ifdef CONFIG_SYSLOGD + /* Log the message to syslogd */ + if (device & LOG) { + /* don`t out "\r\n" */ + openlog(bb_applet_name, 0, LOG_DAEMON); + syslog(LOG_INFO, "%s", msg); + closelog(); + } + + msg[l++] = '\n'; + msg[l] = 0; +#else + + msg[l++] = '\n'; + msg[l] = 0; + /* Take full control of the log tty, and never close it. + * It's mine, all mine! Muhahahaha! */ + if (log_fd < 0) { + if ((log_fd = device_open(log, O_RDWR | O_NDELAY | O_NOCTTY)) < 0) { + log_fd = -2; + bb_error_msg("Bummer, can't write to log on %s!", log); + device = CONSOLE; + } else { + fcntl(log_fd, F_SETFD, FD_CLOEXEC); + } + } + if ((device & LOG) && (log_fd >= 0)) { + bb_full_write(log_fd, msg, l); + } +#endif + + if (device & CONSOLE) { + int fd = device_open(_PATH_CONSOLE, + O_WRONLY | O_NOCTTY | O_NDELAY); + /* Always send console messages to /dev/console so people will see them. */ + if (fd >= 0) { + bb_full_write(fd, msg, l); + close(fd); +#ifdef DEBUG_INIT + /* all descriptors may be closed */ + } else { + bb_error_msg("Bummer, can't print: "); + va_start(arguments, fmt); + vfprintf(stderr, fmt, arguments); + va_end(arguments); +#endif + } + } +} + +/* Set terminal settings to reasonable defaults */ +static void set_term(int fd) +{ + struct termios tty; + + tcgetattr(fd, &tty); + + /* set control chars */ + tty.c_cc[VINTR] = 3; /* C-c */ + tty.c_cc[VQUIT] = 28; /* C-\ */ + tty.c_cc[VERASE] = 127; /* C-? */ + tty.c_cc[VKILL] = 21; /* C-u */ + tty.c_cc[VEOF] = 4; /* C-d */ + tty.c_cc[VSTART] = 17; /* C-q */ + tty.c_cc[VSTOP] = 19; /* C-s */ + tty.c_cc[VSUSP] = 26; /* C-z */ + + /* use line dicipline 0 */ + tty.c_line = 0; + + /* Make it be sane */ + tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD; + tty.c_cflag |= CREAD | HUPCL | CLOCAL; + + + /* input modes */ + tty.c_iflag = ICRNL | IXON | IXOFF; + + /* output modes */ + tty.c_oflag = OPOST | ONLCR; + + /* local modes */ + tty.c_lflag = + ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; + + tcsetattr(fd, TCSANOW, &tty); +} + +/* How much memory does this machine have? + Units are kBytes to avoid overflow on 4GB machines */ +static unsigned int check_free_memory(void) +{ + struct sysinfo info; + unsigned int result, u, s = 10; + + if (sysinfo(&info) != 0) { + bb_perror_msg("Error checking free memory"); + return -1; + } + + /* Kernels 2.0.x and 2.2.x return info.mem_unit==0 with values in bytes. + * Kernels 2.4.0 return info.mem_unit in bytes. */ + u = info.mem_unit; + if (u == 0) + u = 1; + while ((u & 1) == 0 && s > 0) { + u >>= 1; + s--; + } + result = (info.totalram >> s) + (info.totalswap >> s); + if (((unsigned long long)result * (unsigned long long)u) > UINT_MAX) { + return(UINT_MAX); + } else { + return(result * u); + } +} + +static void console_init(void) +{ + int fd; + int tried = 0; + struct vt_stat vt; + struct serial_struct sr; + char *s; + + if ((s = getenv("CONSOLE")) != NULL || (s = getenv("console")) != NULL) { + safe_strncpy(console, s, sizeof(console)); +#if #cpu(sparc) + /* sparc kernel supports console=tty[ab] parameter which is also + * passed to init, so catch it here */ + /* remap tty[ab] to /dev/ttyS[01] */ + if (strcmp(s, "ttya") == 0) + safe_strncpy(console, SC_0, sizeof(console)); + else if (strcmp(s, "ttyb") == 0) + safe_strncpy(console, SC_1, sizeof(console)); +#endif + } else { + /* 2.2 kernels: identify the real console backend and try to use it */ + if (ioctl(0, TIOCGSERIAL, &sr) == 0) { + /* this is a serial console */ + snprintf(console, sizeof(console) - 1, SC_FORMAT, sr.line); + } else if (ioctl(0, VT_GETSTATE, &vt) == 0) { + /* this is linux virtual tty */ + snprintf(console, sizeof(console) - 1, VC_FORMAT, vt.v_active); + } else { + safe_strncpy(console, _PATH_CONSOLE, sizeof(console)); + tried++; + } + } + + while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0 && tried < 2) { + /* Can't open selected console -- try + logical system console and VT_MASTER */ + safe_strncpy(console, (tried == 0 ? _PATH_CONSOLE : CURRENT_VC), + sizeof(console)); + tried++; + } + if (fd < 0) { + /* Perhaps we should panic here? */ +#ifndef CONFIG_SYSLOGD + log = +#endif + safe_strncpy(console, "/dev/null", sizeof(console)); + } else { + s = getenv("TERM"); + /* check for serial console */ + if (ioctl(fd, TIOCGSERIAL, &sr) == 0) { + /* Force the TERM setting to vt102 for serial console -- + * if TERM is set to linux (the default) */ + if (s == NULL || strcmp(s, "linux") == 0) + putenv("TERM=vt102"); +#ifndef CONFIG_SYSLOGD + log = console; +#endif + } else { + if (s == NULL) + putenv("TERM=linux"); + } + close(fd); + } + messageD(LOG, "console=%s", console); +} + +static void fixup_argv(int argc, char **argv, char *new_argv0) +{ + int len; + + /* Fix up argv[0] to be certain we claim to be init */ + len = strlen(argv[0]); + memset(argv[0], 0, len); + safe_strncpy(argv[0], new_argv0, len + 1); + + /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ + len = 1; + while (argc > len) { + memset(argv[len], 0, strlen(argv[len])); + len++; + } +} + +static pid_t run(const struct init_action *a) +{ + struct stat sb; + int i, junk; + pid_t pid, pgrp, tmp_pid; + char *s, *tmpCmd, *cmd[INIT_BUFFS_SIZE], *cmdpath; + char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */ + sigset_t nmask, omask; + static const char press_enter[] = +#ifdef CUSTOMIZED_BANNER +#include CUSTOMIZED_BANNER +#endif + "\nPlease press Enter to activate this console. "; + + /* Block sigchild while forking. */ + sigemptyset(&nmask); + sigaddset(&nmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &nmask, &omask); + + if ((pid = fork()) == 0) { + /* Clean up */ + close(0); + close(1); + close(2); + sigprocmask(SIG_SETMASK, &omask, NULL); + + /* Reset signal handlers that were set by the parent process */ + signal(SIGUSR1, SIG_DFL); + signal(SIGUSR2, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGCONT, SIG_DFL); + signal(SIGSTOP, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + + /* Create a new session and make ourself the process + * group leader */ + setsid(); + + /* Open the new terminal device */ + if ((device_open(a->terminal, O_RDWR)) < 0) { + if (stat(a->terminal, &sb) != 0) { + message(LOG | CONSOLE, "device '%s' does not exist.", + a->terminal); + _exit(1); + } + message(LOG | CONSOLE, "Bummer, can't open %s", a->terminal); + _exit(1); + } + + /* Make sure the terminal will act fairly normal for us */ + set_term(0); + /* Setup stdout, stderr for the new process so + * they point to the supplied terminal */ + dup(0); + dup(0); + + /* If the init Action requires us to wait, then force the + * supplied terminal to be the controlling tty. */ + if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { + + /* Now fork off another process to just hang around */ + if ((pid = fork()) < 0) { + message(LOG | CONSOLE, "Can't fork!"); + _exit(1); + } + + if (pid > 0) { + + /* We are the parent -- wait till the child is done */ + signal(SIGINT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGCHLD, SIG_DFL); + + /* Wait for child to exit */ + while ((tmp_pid = waitpid(pid, &junk, 0)) != pid) { + if (tmp_pid == -1 && errno == ECHILD) { + break; + } + /* FIXME handle other errors */ + } + + /* See if stealing the controlling tty back is necessary */ + pgrp = tcgetpgrp(0); + if (pgrp != getpid()) + _exit(0); + + /* Use a temporary process to steal the controlling tty. */ + if ((pid = fork()) < 0) { + message(LOG | CONSOLE, "Can't fork!"); + _exit(1); + } + if (pid == 0) { + setsid(); + ioctl(0, TIOCSCTTY, 1); + _exit(0); + } + while ((tmp_pid = waitpid(pid, &junk, 0)) != pid) { + if (tmp_pid < 0 && errno == ECHILD) + break; + } + _exit(0); + } + + /* Now fall though to actually execute things */ + } + + /* See if any special /bin/sh requiring characters are present */ + if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { + cmd[0] = (char *)DEFAULT_SHELL; + cmd[1] = "-c"; + cmd[2] = strcat(strcpy(buf, "exec "), a->command); + cmd[3] = NULL; + } else { + /* Convert command (char*) into cmd (char**, one word per string) */ + strcpy(buf, a->command); + s = buf; + for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) { + if (*tmpCmd != '\0') { + cmd[i] = tmpCmd; + i++; + } + } + cmd[i] = NULL; + } + + cmdpath = cmd[0]; + + /* + Interactive shells want to see a dash in argv[0]. This + typically is handled by login, argv will be setup this + way if a dash appears at the front of the command path + (like "-/bin/sh"). + */ + + if (*cmdpath == '-') { + + /* skip over the dash */ + ++cmdpath; + + /* find the last component in the command pathname */ + s = bb_get_last_path_component(cmdpath); + + /* make a new argv[0] */ + if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) { + message(LOG | CONSOLE, bb_msg_memory_exhausted); + cmd[0] = cmdpath; + } else { + cmd[0][0] = '-'; + strcpy(cmd[0] + 1, s); + } + } + +#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) + if (a->action & ASKFIRST) { + char c; + /* + * Save memory by not exec-ing anything large (like a shell) + * before the user wants it. This is critical if swap is not + * enabled and the system has low memory. Generally this will + * be run on the second virtual console, and the first will + * be allowed to start a shell or whatever an init script + * specifies. + */ + messageD(LOG, "Waiting for enter to start '%s'" + "(pid %d, terminal %s)\n", + cmdpath, getpid(), a->terminal); + bb_full_write(1, press_enter, sizeof(press_enter) - 1); + while(read(0, &c, 1) == 1 && c != '\n') + ; + } +#endif + + /* Log the process name and args */ + message(LOG, "Starting pid %d, console %s: '%s'", + getpid(), a->terminal, cmdpath); + +#if defined CONFIG_FEATURE_INIT_COREDUMPS + if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) { + struct rlimit limit; + + limit.rlim_cur = RLIM_INFINITY; + limit.rlim_max = RLIM_INFINITY; + setrlimit(RLIMIT_CORE, &limit); + } +#endif + + /* Now run it. The new program will take over this PID, + * so nothing further in init.c should be run. */ + execv(cmdpath, cmd); + + /* We're still here? Some error happened. */ + message(LOG | CONSOLE, "Bummer, could not run '%s': %m", cmdpath); + _exit(-1); + } + sigprocmask(SIG_SETMASK, &omask, NULL); + return pid; +} + +static int waitfor(const struct init_action *a) +{ + int pid; + int status, wpid; + + pid = run(a); + while (1) { + wpid = waitpid(pid,&status,0); + if (wpid == pid) + break; + if (wpid == -1 && errno == ECHILD) { + /* we missed its termination */ + break; + } + /* FIXME other errors should maybe trigger an error, but allow + * the program to continue */ + } + return wpid; +} + +/* Run all commands of a particular type */ +static void run_actions(int action) +{ + struct init_action *a, *tmp; + + for (a = init_action_list; a; a = tmp) { + tmp = a->next; + if (a->action == action) { + if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { + waitfor(a); + delete_init_action(a); + } else if (a->action & ONCE) { + run(a); + delete_init_action(a); + } else if (a->action & (RESPAWN | ASKFIRST)) { + /* Only run stuff with pid==0. If they have + * a pid, that means it is still running */ + if (a->pid == 0) { + a->pid = run(a); + } + } + } + } +} + +#ifndef DEBUG_INIT +static void init_reboot(unsigned long magic) +{ + pid_t pid; + /* We have to fork here, since the kernel calls do_exit(0) in + * linux/kernel/sys.c, which can cause the machine to panic when + * the init process is killed.... */ + if ((pid = fork()) == 0) { + reboot(magic); + _exit(0); + } + waitpid (pid, NULL, 0); +} + +static void shutdown_system(void) +{ + sigset_t block_signals; + + /* run everything to be run at "shutdown". This is done _prior_ + * to killing everything, in case people wish to use scripts to + * shut things down gracefully... */ + run_actions(SHUTDOWN); + + /* first disable all our signals */ + sigemptyset(&block_signals); + sigaddset(&block_signals, SIGHUP); + sigaddset(&block_signals, SIGCHLD); + sigaddset(&block_signals, SIGUSR1); + sigaddset(&block_signals, SIGUSR2); + sigaddset(&block_signals, SIGINT); + sigaddset(&block_signals, SIGTERM); + sigaddset(&block_signals, SIGCONT); + sigaddset(&block_signals, SIGSTOP); + sigaddset(&block_signals, SIGTSTP); + sigprocmask(SIG_BLOCK, &block_signals, NULL); + + /* Allow Ctrl-Alt-Del to reboot system. */ + init_reboot(RB_ENABLE_CAD); + + message(CONSOLE | LOG, "The system is going down NOW !!"); + sync(); + + /* Send signals to every process _except_ pid 1 */ + message(CONSOLE | LOG, "Sending SIGTERM to all processes."); + kill(-1, SIGTERM); + sleep(1); + sync(); + + message(CONSOLE | LOG, "Sending SIGKILL to all processes."); + kill(-1, SIGKILL); + sleep(1); + + sync(); +} + +static void exec_signal(int sig) +{ + struct init_action *a, *tmp; + sigset_t unblock_signals; + + for (a = init_action_list; a; a = tmp) { + tmp = a->next; + if (a->action & RESTART) { + struct stat sb; + + shutdown_system(); + + /* unblock all signals, blocked in shutdown_system() */ + sigemptyset(&unblock_signals); + sigaddset(&unblock_signals, SIGHUP); + sigaddset(&unblock_signals, SIGCHLD); + sigaddset(&unblock_signals, SIGUSR1); + sigaddset(&unblock_signals, SIGUSR2); + sigaddset(&unblock_signals, SIGINT); + sigaddset(&unblock_signals, SIGTERM); + sigaddset(&unblock_signals, SIGCONT); + sigaddset(&unblock_signals, SIGSTOP); + sigaddset(&unblock_signals, SIGTSTP); + sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL); + + /* Close whatever files are open. */ + close(0); + close(1); + close(2); + + /* Open the new terminal device */ + if ((device_open(a->terminal, O_RDWR)) < 0) { + if (stat(a->terminal, &sb) != 0) { + message(LOG | CONSOLE, "device '%s' does not exist.", a->terminal); + } else { + message(LOG | CONSOLE, "Bummer, can't open %s", a->terminal); + } + halt_signal(SIGUSR1); + } + + /* Make sure the terminal will act fairly normal for us */ + set_term(0); + /* Setup stdout, stderr on the supplied terminal */ + dup(0); + dup(0); + + messageD(CONSOLE | LOG, "Trying to re-exec %s", a->command); + execl(a->command, a->command, NULL); + + message(CONSOLE | LOG, "exec of '%s' failed: %m", + a->command); + sync(); + sleep(2); + init_reboot(RB_HALT_SYSTEM); + loop_forever(); + } + } +} + +static void halt_signal(int sig) +{ + shutdown_system(); + message(CONSOLE | LOG, +#if #cpu(s390) + /* Seems the s390 console is Wierd(tm). */ + "The system is halted. You may reboot now." +#else + "The system is halted. Press Reset or turn off power" +#endif + ); + sync(); + + /* allow time for last message to reach serial console */ + sleep(2); + + if (sig == SIGUSR2) + init_reboot(RB_POWER_OFF); + else + init_reboot(RB_HALT_SYSTEM); + + loop_forever(); +} + +static void reboot_signal(int sig) +{ + shutdown_system(); + message(CONSOLE | LOG, "Please stand by while rebooting the system."); + sync(); + + /* allow time for last message to reach serial console */ + sleep(2); + + init_reboot(RB_AUTOBOOT); + + loop_forever(); +} + +static void ctrlaltdel_signal(int sig) +{ + run_actions(CTRLALTDEL); +} + +/* The SIGSTOP & SIGTSTP handler */ +static void stop_handler(int sig) +{ + int saved_errno = errno; + + got_cont = 0; + while (!got_cont) + pause(); + got_cont = 0; + errno = saved_errno; +} + +/* The SIGCONT handler */ +static void cont_handler(int sig) +{ + got_cont = 1; +} + +#endif /* ! DEBUG_INIT */ + +static void new_init_action(int action, const char *command, const char *cons) +{ + struct init_action *new_action, *a, *last; + + if (*cons == '\0') + cons = console; + + /* do not run entries if console device is not available */ + if (access(cons, R_OK | W_OK)) + return; + if (strcmp(cons, "/dev/null") == 0 && (action & ASKFIRST)) + return; + + new_action = calloc((size_t) (1), sizeof(struct init_action)); + if (!new_action) { + message(LOG | CONSOLE, "Memory allocation failure"); + loop_forever(); + } + + /* Append to the end of the list */ + for (a = last = init_action_list; a; a = a->next) { + /* don't enter action if it's already in the list, + * but do overwrite existing actions */ + if ((strcmp(a->command, command) == 0) && + (strcmp(a->terminal, cons) ==0)) { + a->action = action; + free(new_action); + return; + } + last = a; + } + if (last) { + last->next = new_action; + } else { + init_action_list = new_action; + } + strcpy(new_action->command, command); + new_action->action = action; + strcpy(new_action->terminal, cons); +#if 0 /* calloc zeroed always */ + new_action->pid = 0; +#endif + messageD(LOG|CONSOLE, "command='%s' action='%d' terminal='%s'\n", + new_action->command, new_action->action, new_action->terminal); +} + +static void delete_init_action(struct init_action *action) +{ + struct init_action *a, *b = NULL; + + for (a = init_action_list; a; b = a, a = a->next) { + if (a == action) { + if (b == NULL) { + init_action_list = a->next; + } else { + b->next = a->next; + } + free(a); + break; + } + } +} + +/* Make sure there is enough memory to do something useful. * + * Calls "swapon -a" if needed so be sure /etc/fstab is present... */ +static void check_memory(void) +{ + struct stat statBuf; + + if (check_free_memory() > 1000) + return; + +#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) + if (stat("/etc/fstab", &statBuf) == 0) { + /* swapon -a requires /proc typically */ + new_init_action(SYSINIT, "/bin/mount -t proc proc /proc", ""); + /* Try to turn on swap */ + new_init_action(SYSINIT, "/sbin/swapon -a", ""); + run_actions(SYSINIT); /* wait and removing */ + if (check_free_memory() < 1000) + goto goodnight; + } else + goto goodnight; + return; +#endif + + goodnight: + message(CONSOLE, "Sorry, your computer does not have enough memory."); + loop_forever(); +} + +/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, + * then parse_inittab() simply adds in some default + * actions(i.e., runs INIT_SCRIPT and then starts a pair + * of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB + * _is_ defined, but /etc/inittab is missing, this + * results in the same set of default behaviors. + */ +static void parse_inittab(void) +{ +#ifdef CONFIG_FEATURE_USE_INITTAB + FILE *file; + char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE]; + char tmpConsole[CONSOLE_BUFF_SIZE]; + char *id, *runlev, *action, *command, *eol; + const struct init_action_type *a = actions; + + + file = fopen(INITTAB, "r"); + if (file == NULL) { + /* No inittab file -- set up some default behavior */ +#endif + /* Reboot on Ctrl-Alt-Del */ + new_init_action(CTRLALTDEL, "/sbin/reboot", ""); + /* Umount all filesystems on halt/reboot */ + new_init_action(SHUTDOWN, "/bin/umount -a -r", ""); +#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) + /* Swapoff on halt/reboot */ + new_init_action(SHUTDOWN, "/sbin/swapoff -a", ""); +#endif + /* Prepare to restart init when a HUP is received */ + new_init_action(RESTART, "/sbin/init", ""); + /* Askfirst shell on tty1-4 */ + new_init_action(ASKFIRST, bb_default_login_shell, ""); + new_init_action(ASKFIRST, bb_default_login_shell, VC_2); + new_init_action(ASKFIRST, bb_default_login_shell, VC_3); + new_init_action(ASKFIRST, bb_default_login_shell, VC_4); + /* sysinit */ + new_init_action(SYSINIT, INIT_SCRIPT, ""); + + return; +#ifdef CONFIG_FEATURE_USE_INITTAB + } + + while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) { + /* Skip leading spaces */ + for (id = buf; *id == ' ' || *id == '\t'; id++); + + /* Skip the line if it's a comment */ + if (*id == '#' || *id == '\n') + continue; + + /* Trim the trailing \n */ + eol = strrchr(id, '\n'); + if (eol != NULL) + *eol = '\0'; + + /* Keep a copy around for posterity's sake (and error msgs) */ + strcpy(lineAsRead, buf); + + /* Separate the ID field from the runlevels */ + runlev = strchr(id, ':'); + if (runlev == NULL || *(runlev + 1) == '\0') { + message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); + continue; + } else { + *runlev = '\0'; + ++runlev; + } + + /* Separate the runlevels from the action */ + action = strchr(runlev, ':'); + if (action == NULL || *(action + 1) == '\0') { + message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); + continue; + } else { + *action = '\0'; + ++action; + } + + /* Separate the action from the command */ + command = strchr(action, ':'); + if (command == NULL || *(command + 1) == '\0') { + message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); + continue; + } else { + *command = '\0'; + ++command; + } + + /* Ok, now process it */ + for (a = actions; a->name != 0; a++) { + if (strcmp(a->name, action) == 0) { + if (*id != '\0') { + if(strncmp(id, "/dev/", 5) == 0) + id += 5; + strcpy(tmpConsole, "/dev/"); + safe_strncpy(tmpConsole + 5, id, + CONSOLE_BUFF_SIZE - 5); + id = tmpConsole; + } + new_init_action(a->action, command, id); + break; + } + } + if (a->name == 0) { + /* Choke on an unknown action */ + message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); + } + } + fclose(file); + return; +#endif /* CONFIG_FEATURE_USE_INITTAB */ +} + +#ifdef CONFIG_FEATURE_USE_INITTAB +static void reload_signal(int sig) +{ + struct init_action *a, *tmp; + + message(LOG, "Reloading /etc/inittab"); + + /* disable old entrys */ + for (a = init_action_list; a; a = a->next ) { + a->action = ONCE; + } + + parse_inittab(); + + /* remove unused entrys */ + for (a = init_action_list; a; a = tmp) { + tmp = a->next; + if (a->action & (ONCE | SYSINIT | WAIT ) && + a->pid == 0 ) { + delete_init_action(a); + } + } + run_actions(RESPAWN); + return; +} +#endif /* CONFIG_FEATURE_USE_INITTAB */ + +extern int init_main(int argc, char **argv) +{ + struct init_action *a; + pid_t wpid; + int status; + + if (argc > 1 && !strcmp(argv[1], "-q")) { + return kill_init(SIGHUP); + } +#ifndef DEBUG_INIT + /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ + if (getpid() != 1 +#ifdef CONFIG_FEATURE_INITRD + && strstr(bb_applet_name, "linuxrc") == NULL +#endif + ) { + bb_show_usage(); + } + /* Set up sig handlers -- be sure to + * clear all of these in run() */ + signal(SIGHUP, exec_signal); + signal(SIGUSR1, halt_signal); + signal(SIGUSR2, halt_signal); + signal(SIGINT, ctrlaltdel_signal); + signal(SIGTERM, reboot_signal); + signal(SIGCONT, cont_handler); + signal(SIGSTOP, stop_handler); + signal(SIGTSTP, stop_handler); + + /* Turn off rebooting via CTL-ALT-DEL -- we get a + * SIGINT on CAD so we can shut things down gracefully... */ + init_reboot(RB_DISABLE_CAD); +#endif + + /* Figure out where the default console should be */ + console_init(); + + /* Close whatever files are open, and reset the console. */ + close(0); + close(1); + close(2); + + if (device_open(console, O_RDWR | O_NOCTTY) == 0) { + set_term(0); + close(0); + } + + chdir("/"); + setsid(); + { + const char * const *e; + /* Make sure environs is set to something sane */ + for(e = environment; *e; e++) + putenv((char *) *e); + } + /* Hello world */ + message(MAYBE_CONSOLE | LOG, "init started: %s", bb_msg_full_version); + + /* Make sure there is enough memory to do something useful. */ + check_memory(); + + /* Check if we are supposed to be in single user mode */ + if (argc > 1 && (!strcmp(argv[1], "single") || + !strcmp(argv[1], "-s") || !strcmp(argv[1], "1"))) { + /* Start a shell on console */ + new_init_action(RESPAWN, bb_default_login_shell, ""); + } else { + /* Not in single user mode -- see what inittab says */ + + /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, + * then parse_inittab() simply adds in some default + * actions(i.e., runs INIT_SCRIPT and then starts a pair + * of "askfirst" shells */ + parse_inittab(); + } + + /* Make the command line just say "init" -- thats all, nothing else */ + fixup_argv(argc, argv, "init"); + + /* Now run everything that needs to be run */ + + /* First run the sysinit command */ + run_actions(SYSINIT); + + /* Next run anything that wants to block */ + run_actions(WAIT); + + /* Next run anything to be run only once */ + run_actions(ONCE); + +#ifdef CONFIG_FEATURE_USE_INITTAB + /* Redefine SIGHUP to reread /etc/inittab */ + signal(SIGHUP, reload_signal); +#else + signal(SIGHUP, SIG_IGN); +#endif /* CONFIG_FEATURE_USE_INITTAB */ + + + /* Now run the looping stuff for the rest of forever */ + while (1) { + /* run the respawn stuff */ + run_actions(RESPAWN); + + /* run the askfirst stuff */ + run_actions(ASKFIRST); + + /* Don't consume all CPU time -- sleep a bit */ + sleep(1); + + /* Wait for a child process to exit */ + wpid = wait(&status); + while (wpid > 0) { + /* Find out who died and clean up their corpse */ + for (a = init_action_list; a; a = a->next) { + if (a->pid == wpid) { + /* Set the pid to 0 so that the process gets + * restarted by run_actions() */ + a->pid = 0; + message(LOG, "Process '%s' (pid %d) exited. " + "Scheduling it for restart.", + a->command, wpid); + } + } + /* see if anyone else is waiting to be reaped */ + wpid = waitpid (-1, &status, WNOHANG); + } + } +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/init/init_shared.c b/busybox/init/init_shared.c new file mode 100644 index 000000000..0ad55a433 --- /dev/null +++ b/busybox/init/init_shared.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +/* + * Stuff shared between init, reboot, halt, and poweroff + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" +#include "init_shared.h" + +extern int kill_init(int sig) +{ +#ifdef CONFIG_FEATURE_INITRD + /* don't assume init's pid == 1 */ + long *pid = find_pid_by_name("init"); + if (!pid || *pid<=0) { + pid = find_pid_by_name("linuxrc"); + if (!pid || *pid<=0) + bb_error_msg_and_die("no process killed"); + } + return(kill(*pid, sig)); +#else + return(kill(1, sig)); +#endif +} + +#ifndef CONFIG_INIT +const char * const bb_shutdown_format = "\r%s\n"; +extern int bb_shutdown_system(unsigned long magic) +{ + int pri = LOG_KERN|LOG_NOTICE|LOG_FACMASK; + const char *message; + + /* Don't kill ourself */ + signal(SIGTERM,SIG_IGN); + signal(SIGHUP,SIG_IGN); + setpgrp(); + + /* Allow Ctrl-Alt-Del to reboot system. */ +#ifndef RB_ENABLE_CAD +#define RB_ENABLE_CAD 0x89abcdef +#endif + reboot(RB_ENABLE_CAD); + + openlog(bb_applet_name, 0, pri); + + message = "\nThe system is going down NOW !!"; + syslog(pri, "%s", message); + printf(bb_shutdown_format, message); + + sync(); + + /* Send signals to every process _except_ pid 1 */ + message = "Sending SIGTERM to all processes."; + syslog(pri, "%s", message); + printf(bb_shutdown_format, message); + + kill(-1, SIGTERM); + sleep(1); + sync(); + + message = "Sending SIGKILL to all processes."; + syslog(pri, "%s", message); + printf(bb_shutdown_format, message); + + kill(-1, SIGKILL); + sleep(1); + + sync(); + + reboot(magic); + return 0; /* Shrug */ +} +#endif diff --git a/busybox/init/init_shared.h b/busybox/init/init_shared.h new file mode 100644 index 000000000..1e4cfac98 --- /dev/null +++ b/busybox/init/init_shared.h @@ -0,0 +1,3 @@ +extern int kill_init(int sig); +extern int bb_shutdown_system(unsigned long magic); + diff --git a/busybox/init/mesg.c b/busybox/init/mesg.c new file mode 100644 index 000000000..7fd9d24ae --- /dev/null +++ b/busybox/init/mesg.c @@ -0,0 +1,58 @@ +/* + * mesg implementation for busybox + * + * Copyright (c) 2002 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +#ifdef USE_TTY_GROUP +#define S_IWGRP_OR_S_IWOTH S_IWGRP +#else +#define S_IWGRP_OR_S_IWOTH (S_IWGRP | S_IWOTH) +#endif + +extern int mesg_main(int argc, char *argv[]) +{ + struct stat sb; + char *tty; + char c = 0; + + if ((--argc == 0) + || ((argc == 1) && (((c = **++argv) == 'y') || (c == 'n')))) { + if ((tty = ttyname(STDERR_FILENO)) == NULL) { + tty = "ttyname"; + } else if (stat(tty, &sb) == 0) { + if (argc == 0) { + puts(((sb.st_mode & (S_IWGRP | S_IWOTH)) == + 0) ? "is n" : "is y"); + return EXIT_SUCCESS; + } + if (chmod + (tty, + (c == + 'y') ? sb.st_mode | (S_IWGRP_OR_S_IWOTH) : sb. + st_mode & ~(S_IWGRP | S_IWOTH)) == 0) { + return EXIT_SUCCESS; + } + } + bb_perror_msg_and_die("%s", tty); + } + bb_show_usage(); +} diff --git a/busybox/init/poweroff.c b/busybox/init/poweroff.c new file mode 100644 index 000000000..81695087d --- /dev/null +++ b/busybox/init/poweroff.c @@ -0,0 +1,56 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini poweroff implementation for busybox + * + * Copyright (C) 1999-2004 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" +#include "init_shared.h" + + +extern int poweroff_main(int argc, char **argv) +{ + char *delay; /* delay in seconds before rebooting */ + + if(bb_getopt_ulflags(argc, argv, "d:", &delay)) { + sleep(atoi(delay)); + } + +#ifndef CONFIG_INIT +#ifndef RB_POWER_OFF +#define RB_POWER_OFF 0x4321fedc +#endif + return(bb_shutdown_system(RB_POWER_OFF)); +#else + return kill_init(SIGUSR2); +#endif +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/init/reboot.c b/busybox/init/reboot.c new file mode 100644 index 000000000..ca4e9a240 --- /dev/null +++ b/busybox/init/reboot.c @@ -0,0 +1,56 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini reboot implementation for busybox + * + * Copyright (C) 1999-2004 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" +#include "init_shared.h" + + +extern int reboot_main(int argc, char **argv) +{ + char *delay; /* delay in seconds before rebooting */ + + if(bb_getopt_ulflags(argc, argv, "d:", &delay)) { + sleep(atoi(delay)); + } + +#ifndef CONFIG_INIT +#ifndef RB_AUTOBOOT +#define RB_AUTOBOOT 0x01234567 +#endif + return(bb_shutdown_system(RB_AUTOBOOT)); +#else + return kill_init(SIGTERM); +#endif +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/.cvsignore b/busybox/libbb/.cvsignore new file mode 100644 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..e94c05260 --- /dev/null +++ b/busybox/libbb/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/libbb +LIBBB_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/libbb/Makefile.in b/busybox/libbb/Makefile.in new file mode 100644 index 000000000..85d4a967b --- /dev/null +++ b/busybox/libbb/Makefile.in @@ -0,0 +1,107 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 + +LIBBB_AR:=libbb.a +ifndef $(LIBBB_DIR) +LIBBB_DIR:=$(top_builddir)/libbb/ +endif +srcdir=$(top_srcdir)/libbb + +LIBBB_SRC:= \ + bb_asprintf.c ask_confirmation.c change_identity.c chomp.c \ + compare_string_array.c concat_path_file.c copy_file.c copyfd.c \ + correct_password.c create_icmp_socket.c create_icmp6_socket.c \ + device_open.c dump.c error_msg.c error_msg_and_die.c find_mount_point.c \ + find_pid_by_name.c find_root_device.c fgets_str.c full_read.c \ + full_write.c get_last_path_component.c get_line_from_file.c get_ug_id.c \ + get_terminal_width_height.c hash_fd.c herror_msg.c herror_msg_and_die.c \ + human_readable.c inet_common.c inode_hash.c interface.c isdirectory.c \ + kernel_version.c last_char_is.c llist_add_to.c login.c loop.c \ + make_directory.c mode_string.c module_syscalls.c mtab.c mtab_file.c \ + my_getgrgid.c my_getgrnam.c my_getpwnam.c my_getug.c\ + my_getpwuid.c obscure.c parse_mode.c parse_number.c perror_msg.c \ + perror_msg_and_die.c print_file.c get_console.c \ + process_escape_sequence.c procps.c pwd2spwd.c pw_encrypt.c qmodule.c \ + read_package_field.c recursive_action.c remove_file.c \ + restricted_shell.c run_parts.c run_shell.c safe_read.c safe_write.c \ + safe_strncpy.c setup_environment.c simplify_path.c syscalls.c \ + trim.c u_signal_names.c vdprintf.c verror_msg.c \ + vherror_msg.c vperror_msg.c wfopen.c xconnect.c xgetcwd.c \ + xgethostbyname.c xgethostbyname2.c xreadlink.c xregcomp.c xgetlarg.c \ + get_terminal_width_height.c fclose_nonstdin.c fflush_stdout_and_exit.c \ + getopt_ulflags.c default_error_retval.c wfopen_input.c speed_table.c \ + perror_nomsg_and_die.c perror_nomsg.c skip_whitespace.c bb_askpass.c \ + warn_ignoring_args.c concat_subpath_file.c vfork_daemon_rexec.c + +LIBBB_OBJS=$(patsubst %.c,$(LIBBB_DIR)%.o, $(LIBBB_SRC)) + +LIBBB_MSRC0:=$(srcdir)/messages.c +LIBBB_MOBJ0:=full_version.o \ + memory_exhausted.o invalid_date.o io_error.o \ + write_error.o name_longer_than_foo.o unknown.o \ + can_not_create_raw_socket.o perm_denied_are_you_root.o \ + shadow_file.o passwd_file.o group_file.o gshadow_file.o nologin_file.o \ + securetty_file.o motd_file.o \ + msg_standard_input.o msg_standard_output.o shell_file.o + +LIBBB_MSRC1:=$(srcdir)/xfuncs.c +LIBBB_MOBJ1:=xmalloc.o xrealloc.o xcalloc.o xstrdup.o xstrndup.o \ + xfopen.o xopen.o xread.o xread_all.o xread_char.o \ + xferror.o xferror_stdout.o xfflush_stdout.o strlen.o + +LIBBB_MSRC2:=$(srcdir)/printf.c +LIBBB_MOBJ2:=bb_vfprintf.o bb_vprintf.o bb_fprintf.o bb_printf.o + +LIBBB_MSRC3:=$(srcdir)/xgetularg.c +LIBBB_MOBJ3:=xgetularg_bnd_sfx.o xgetlarg_bnd_sfx.o getlarg10_sfx.o \ + xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o + +LIBBB_MSRC4:=$(srcdir)/safe_strtol.c +LIBBB_MOBJ4:=safe_strtoi.o safe_strtod.o safe_strtol.o safe_strtoul.o + +LIBBB_MOBJS0=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ0)) +LIBBB_MOBJS1=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ1)) +LIBBB_MOBJS2=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ2)) +LIBBB_MOBJS3=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ3)) +LIBBB_MOBJS4=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ4)) + +libraries-y+=$(LIBBB_DIR)$(LIBBB_AR) + +$(LIBBB_DIR)$(LIBBB_AR): $(LIBBB_OBJS) $(LIBBB_MOBJS0) $(LIBBB_MOBJS1) \ + $(LIBBB_MOBJS2) $(LIBBB_MOBJS3) $(LIBBB_MOBJS4) + $(AR) -ro $@ $(LIBBB_OBJS) $(LIBBB_MOBJS0) $(LIBBB_MOBJS1) \ + $(LIBBB_MOBJS2) $(LIBBB_MOBJS3) $(LIBBB_MOBJS4) + +$(LIBBB_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +$(LIBBB_MOBJS0): $(LIBBB_MSRC0) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@ + +$(LIBBB_MOBJS1): $(LIBBB_MSRC1) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@ + +$(LIBBB_MOBJS2): $(LIBBB_MSRC2) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@ + +$(LIBBB_MOBJS3): $(LIBBB_MSRC3) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@ + +$(LIBBB_MOBJS4): $(LIBBB_MSRC4) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@ + diff --git a/busybox/libbb/README b/busybox/libbb/README new file mode 100644 index 000000000..4f28f7e34 --- /dev/null +++ b/busybox/libbb/README @@ -0,0 +1,11 @@ +Please see the LICENSE file for copyright information (GPLv2) + +libbb is BusyBox's utility library. All of this stuff used to be stuffed into +a single file named utility.c. When I split utility.c to create libbb, some of +the very oldest stuff ended up without their original copyright and licensing +information (which is now lost in the mists of time). If you see something +that you wrote that is mis-attributed, do let me know so we can fix that up. + + Erik Andersen + + diff --git a/busybox/libbb/ask_confirmation.c b/busybox/libbb/ask_confirmation.c new file mode 100644 index 000000000..a99a4e733 --- /dev/null +++ b/busybox/libbb/ask_confirmation.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_ask_confirmation implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Read a line from stdin. If the first non-whitespace char is 'y' or 'Y', + * return 1. Otherwise return 0. + */ + +#include +#include +#include "libbb.h" + +int bb_ask_confirmation(void) +{ + int retval = 0; + int first = 1; + int c; + + while (((c = getchar()) != EOF) && (c != '\n')) { + /* Make sure we get the actual function call for isspace, + * as speed is not critical here. */ + if (first && !(isspace)(c)) { + --first; + if ((c == 'y') || (c == 'Y')) { + ++retval; + } + } + } + + return retval; +} diff --git a/busybox/libbb/bb_askpass.c b/busybox/libbb/bb_askpass.c new file mode 100644 index 000000000..1ae1520d9 --- /dev/null +++ b/busybox/libbb/bb_askpass.c @@ -0,0 +1,87 @@ +/* vi: set sw=4 ts=4: */ +/* + * Ask for a password + * I use a static buffer in this function. Plan accordingly. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 +#define PWD_BUFFER_SIZE 256 + + +/* do nothing signal handler */ +static void askpass_timeout(int ignore) +{ +} + +char *bb_askpass(int timeout, const char * prompt) +{ + char *ret; + int i, size; + struct sigaction sa; + struct termios old, new; + static char passwd[PWD_BUFFER_SIZE]; + + tcgetattr(STDIN_FILENO, &old); + + size = sizeof(passwd); + ret = passwd; + memset(passwd, 0, size); + + fputs(prompt, stdout); + fflush(stdout); + + tcgetattr(STDIN_FILENO, &new); + new.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); + new.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP); + tcsetattr(STDIN_FILENO, TCSANOW, &new); + + if (timeout) { + sa.sa_flags = 0; + sa.sa_handler = askpass_timeout; + sigaction(SIGALRM, &sa, NULL); + alarm(timeout); + } + + if (read(STDIN_FILENO, passwd, size-1) <= 0) { + ret = NULL; + } else { + for(i = 0; i < size && passwd[i]; i++) { + if (passwd[i]== '\r' || passwd[i] == '\n') { + passwd[i]= 0; + break; + } + } + } + + if (timeout) { + alarm(0); + } + + tcsetattr(STDIN_FILENO, TCSANOW, &old); + fputs("\n", stdout); + fflush(stdout); + return ret; +} + diff --git a/busybox/libbb/bb_asprintf.c b/busybox/libbb/bb_asprintf.c new file mode 100644 index 000000000..a3ba42454 --- /dev/null +++ b/busybox/libbb/bb_asprintf.c @@ -0,0 +1,22 @@ +/* + Copyright (C) 2002 Vladimir Oleynik +*/ + +#include +#include +#include +#include "libbb.h" + +void bb_xasprintf(char **string_ptr, const char *format, ...) +{ + va_list p; + int r; + + va_start(p, format); + r = vasprintf(string_ptr, format, p); + va_end(p); + + if (r < 0) { + bb_perror_msg_and_die("bb_xasprintf"); + } +} diff --git a/busybox/libbb/change_identity.c b/busybox/libbb/change_identity.c new file mode 100644 index 000000000..adebad8ed --- /dev/null +++ b/busybox/libbb/change_identity.c @@ -0,0 +1,62 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "libbb.h" + + +/* Become the user and group(s) specified by PW. */ +const char *change_identity_e2str ( const struct passwd *pw ) +{ + if ( initgroups ( pw-> pw_name, pw-> pw_gid ) == -1 ) + return "cannot set groups"; + endgrent ( ); + + if ( setgid ( pw-> pw_gid )) + return "cannot set group id"; + if ( setuid ( pw->pw_uid )) + return "cannot set user id"; + return NULL; +} + +void change_identity ( const struct passwd *pw ) +{ + const char *err_msg = change_identity_e2str(pw); + + if(err_msg) + bb_perror_msg_and_die ( "%s", err_msg ); +} diff --git a/busybox/libbb/chomp.c b/busybox/libbb/chomp.c new file mode 100644 index 000000000..774e533d4 --- /dev/null +++ b/busybox/libbb/chomp.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#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/compare_string_array.c b/busybox/libbb/compare_string_array.c new file mode 100644 index 000000000..993b46266 --- /dev/null +++ b/busybox/libbb/compare_string_array.c @@ -0,0 +1,31 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +/* returns the array number of the string */ +extern unsigned short compare_string_array(const char *string_array[], const char *key) +{ + unsigned short i; + + for (i = 0; string_array[i] != 0; i++) { + if (strcmp(string_array[i], key) == 0) { + break; + } + } + return(i); +} + diff --git a/busybox/libbb/concat_path_file.c b/busybox/libbb/concat_path_file.c new file mode 100644 index 000000000..77c054530 --- /dev/null +++ b/busybox/libbb/concat_path_file.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* concatenate path and file name to new allocation buffer, + * not addition '/' if path name already have '/' +*/ + +#include +#include "libbb.h" + +extern char *concat_path_file(const char *path, const char *filename) +{ + char *outbuf; + char *lc; + + if (!path) + path=""; + lc = last_char_is(path, '/'); + while (*filename == '/') + filename++; + bb_xasprintf(&outbuf, "%s%s%s", path, (lc==NULL)? "/" : "", filename); + + return outbuf; +} diff --git a/busybox/libbb/concat_subpath_file.c b/busybox/libbb/concat_subpath_file.c new file mode 100644 index 000000000..6d86f5e8c --- /dev/null +++ b/busybox/libbb/concat_subpath_file.c @@ -0,0 +1,36 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) (C) 2003 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 + */ + +/* + This function make special for recursive actions with usage + concat_path_file(path, filename) + and skiping "." and ".." directory entries +*/ + +#include "libbb.h" + +extern char *concat_subpath_file(const char *path, const char *f) +{ + if(f && *f == '.' && (!f[1] || (f[1] == '.' && !f[2]))) + return NULL; + return concat_path_file(path, f); +} diff --git a/busybox/libbb/copy_file.c b/busybox/libbb/copy_file.c new file mode 100644 index 000000000..68a1ded04 --- /dev/null +++ b/busybox/libbb/copy_file.c @@ -0,0 +1,268 @@ +/* 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 "busybox.h" + +int copy_file(const char *source, const char *dest, int flags) +{ + struct stat source_stat; + struct stat dest_stat; + int dest_exists = 0; + int status = 0; + + if ((!(flags & FILEUTILS_DEREFERENCE) && + lstat(source, &source_stat) < 0) || + ((flags & FILEUTILS_DEREFERENCE) && + stat(source, &source_stat) < 0)) { + bb_perror_msg("%s", source); + return -1; + } + + if (lstat(dest, &dest_stat) < 0) { + if (errno != ENOENT) { + bb_perror_msg("unable to stat `%s'", dest); + return -1; + } + } else { + if (source_stat.st_dev == dest_stat.st_dev && + source_stat.st_ino == dest_stat.st_ino) { + bb_error_msg("`%s' and `%s' are the same file", source, dest); + return -1; + } + dest_exists = 1; + } + + if (S_ISDIR(source_stat.st_mode)) { + DIR *dp; + struct dirent *d; + mode_t saved_umask = 0; + + if (!(flags & FILEUTILS_RECUR)) { + bb_error_msg("%s: omitting directory", source); + return -1; + } + + /* Create DEST. */ + if (dest_exists) { + if (!S_ISDIR(dest_stat.st_mode)) { + bb_error_msg("`%s' is not a directory", dest); + return -1; + } + } else { + mode_t mode; + saved_umask = umask(0); + + mode = source_stat.st_mode; + if (!(flags & FILEUTILS_PRESERVE_STATUS)) + mode = source_stat.st_mode & ~saved_umask; + mode |= S_IRWXU; + + if (mkdir(dest, mode) < 0) { + umask(saved_umask); + bb_perror_msg("cannot create directory `%s'", dest); + return -1; + } + + umask(saved_umask); + } + + /* Recursively copy files in SOURCE. */ + if ((dp = opendir(source)) == NULL) { + bb_perror_msg("unable to open directory `%s'", source); + status = -1; + goto end; + } + + while ((d = readdir(dp)) != NULL) { + char *new_source, *new_dest; + + new_source = concat_subpath_file(source, d->d_name); + if(new_source == NULL) + continue; + new_dest = concat_path_file(dest, d->d_name); + if (copy_file(new_source, new_dest, flags) < 0) + status = -1; + free(new_source); + free(new_dest); + } + /* closedir have only EBADF error, but "dp" not changes */ + closedir(dp); + + if (!dest_exists && + chmod(dest, source_stat.st_mode & ~saved_umask) < 0) { + bb_perror_msg("unable to change permissions of `%s'", dest); + status = -1; + } + } else if (S_ISREG(source_stat.st_mode)) { + int src_fd; + int dst_fd; +#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS + char *link_name; + + if (!(flags & FILEUTILS_DEREFERENCE) && + is_in_ino_dev_hashtable(&source_stat, &link_name)) { + if (link(link_name, dest) < 0) { + bb_perror_msg("unable to link `%s'", dest); + return -1; + } + + return 0; + } +#endif + src_fd = open(source, O_RDONLY); + if (src_fd == -1) { + bb_perror_msg("unable to open `%s'", source); + return(-1); + } + + if (dest_exists) { + if (flags & FILEUTILS_INTERACTIVE) { + bb_error_msg("overwrite `%s'? ", dest); + if (!bb_ask_confirmation()) { + close (src_fd); + return 0; + } + } + + dst_fd = open(dest, O_WRONLY|O_TRUNC); + if (dst_fd == -1) { + if (!(flags & FILEUTILS_FORCE)) { + bb_perror_msg("unable to open `%s'", dest); + close(src_fd); + return -1; + } + + if (unlink(dest) < 0) { + bb_perror_msg("unable to remove `%s'", dest); + close(src_fd); + return -1; + } + + dest_exists = 0; + } + } + + if (!dest_exists) { + dst_fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode); + if (dst_fd == -1) { + bb_perror_msg("unable to open `%s'", dest); + close(src_fd); + return(-1); + } + } + + if (bb_copyfd_eof(src_fd, dst_fd) == -1) + status = -1; + + if (close(dst_fd) < 0) { + bb_perror_msg("unable to close `%s'", dest); + status = -1; + } + + if (close(src_fd) < 0) { + bb_perror_msg("unable to close `%s'", source); + status = -1; + } + } + else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || + S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) || + S_ISLNK(source_stat.st_mode)) { + + if (dest_exists && + ((flags & FILEUTILS_FORCE) == 0 || unlink(dest) < 0)) { + bb_perror_msg("unable to remove `%s'", dest); + return -1; + + } + } else { + bb_error_msg("internal error: unrecognized file type"); + return -1; + } + if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || + S_ISSOCK(source_stat.st_mode)) { + if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { + bb_perror_msg("unable to create `%s'", dest); + return -1; + } + } else if (S_ISFIFO(source_stat.st_mode)) { + if (mkfifo(dest, source_stat.st_mode) < 0) { + bb_perror_msg("cannot create fifo `%s'", dest); + return -1; + } + } else if (S_ISLNK(source_stat.st_mode)) { + char *lpath; + + lpath = xreadlink(source); + if (symlink(lpath, dest) < 0) { + bb_perror_msg("cannot create symlink `%s'", dest); + return -1; + } + free(lpath); + +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) + if (flags & FILEUTILS_PRESERVE_STATUS) + if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) + bb_perror_msg("unable to preserve ownership of `%s'", dest); +#endif + +#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS + add_to_ino_dev_hashtable(&source_stat, dest); +#endif + + return 0; + } + +#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS + if (! S_ISDIR(source_stat.st_mode)) { + add_to_ino_dev_hashtable(&source_stat, dest); + } +#endif + +end: + + if (flags & FILEUTILS_PRESERVE_STATUS) { + struct utimbuf times; + + times.actime = source_stat.st_atime; + times.modtime = source_stat.st_mtime; + if (utime(dest, ×) < 0) + bb_perror_msg("unable to preserve times of `%s'", dest); + if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { + source_stat.st_mode &= ~(S_ISUID | S_ISGID); + bb_perror_msg("unable to preserve ownership of `%s'", dest); + } + if (chmod(dest, source_stat.st_mode) < 0) + bb_perror_msg("unable to preserve permissions of `%s'", dest); + } + + return status; +} diff --git a/busybox/libbb/copyfd.c b/busybox/libbb/copyfd.c new file mode 100644 index 000000000..bf0a390a3 --- /dev/null +++ b/busybox/libbb/copyfd.c @@ -0,0 +1,90 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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" + + +#if BUFSIZ < 4096 +#undef BUFSIZ +#define BUFSIZ 4096 +#endif + + +/* If size is 0 copy until EOF */ +static size_t bb_full_fd_action(int src_fd, int dst_fd, const size_t size) +{ + size_t read_total = 0; + RESERVE_CONFIG_BUFFER(buffer,BUFSIZ); + + while ((size == 0) || (read_total < size)) { + size_t read_try; + ssize_t read_actual; + + if ((size == 0) || (size - read_total > BUFSIZ)) { + read_try = BUFSIZ; + } else { + read_try = size - read_total; + } + + read_actual = safe_read(src_fd, buffer, read_try); + if (read_actual > 0) { + if ((dst_fd >= 0) && (bb_full_write(dst_fd, buffer, (size_t) read_actual) != read_actual)) { + bb_perror_msg(bb_msg_write_error); /* match Read error below */ + break; + } + } + else if (read_actual == 0) { + if (size) { + bb_error_msg("Unable to read all data"); + } + break; + } else { + /* read_actual < 0 */ + bb_perror_msg("Read error"); + break; + } + + read_total += read_actual; + } + + RELEASE_CONFIG_BUFFER(buffer); + + return(read_total); +} + + +extern int bb_copyfd_size(int fd1, int fd2, const off_t size) +{ + if (size) { + return(bb_full_fd_action(fd1, fd2, size)); + } + return(0); +} + +extern int bb_copyfd_eof(int fd1, int fd2) +{ + return(bb_full_fd_action(fd1, fd2, 0)); +} diff --git a/busybox/libbb/correct_password.c b/busybox/libbb/correct_password.c new file mode 100644 index 000000000..570aa7e86 --- /dev/null +++ b/busybox/libbb/correct_password.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libbb.h" + + + +/* Ask the user for a password. + Return 1 if the user gives the correct password for entry PW, + 0 if not. Return 1 without asking for a password if run by UID 0 + or if PW has an empty password. */ + +int correct_password ( const struct passwd *pw ) +{ + char *unencrypted, *encrypted, *correct; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + if (( strcmp ( pw-> pw_passwd, "x" ) == 0 ) || ( strcmp ( pw-> pw_passwd, "*" ) == 0 )) { + struct spwd *sp = getspnam ( pw-> pw_name ); + + if ( !sp ) + bb_error_msg_and_die ( "no valid shadow password" ); + + correct = sp-> sp_pwdp; + } + else +#endif + correct = pw-> pw_passwd; + + if ( correct == 0 || correct[0] == '\0' ) + return 1; + + unencrypted = bb_askpass ( 0, "Password: " ); + if ( !unencrypted ) + { + return 0; + } + encrypted = crypt ( unencrypted, correct ); + memset ( unencrypted, 0, bb_strlen ( unencrypted )); + return ( strcmp ( encrypted, correct ) == 0 ) ? 1 : 0; +} diff --git a/busybox/libbb/create_icmp6_socket.c b/busybox/libbb/create_icmp6_socket.c new file mode 100644 index 000000000..d8ff35a0a --- /dev/null +++ b/busybox/libbb/create_icmp6_socket.c @@ -0,0 +1,39 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * create raw socket for icmp (IPv6 version) protocol test permission + * and drop root privileges if running setuid + * + */ + +#include +#include +#include +#include +#include +#include "libbb.h" + +#ifdef CONFIG_FEATURE_IPV6 +int create_icmp6_socket(void) +{ + struct protoent *proto; + int sock; + + proto = getprotobyname("ipv6-icmp"); + /* if getprotobyname failed, just silently force + * proto->p_proto to have the correct value for "ipv6-icmp" */ + if ((sock = socket(AF_INET6, SOCK_RAW, + (proto ? proto->p_proto : IPPROTO_ICMPV6))) < 0) { + if (errno == EPERM) + bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); + else + bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket); + } + + /* drop root privs if running setuid */ + setuid(getuid()); + + return sock; +} +#endif diff --git a/busybox/libbb/create_icmp_socket.c b/busybox/libbb/create_icmp_socket.c new file mode 100644 index 000000000..26120a66d --- /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 permission + * and drop root privileges 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) + bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); + else + bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket); + } + + /* drop root privs if running setuid */ + setuid(getuid()); + + return sock; +} diff --git a/busybox/libbb/default_error_retval.c b/busybox/libbb/default_error_retval.c new file mode 100644 index 000000000..35c34b916 --- /dev/null +++ b/busybox/libbb/default_error_retval.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Seems silly to copyright a global variable. ;-) Oh well. + * + * At least one applet (cmp) returns a value different from the typical + * EXIT_FAILURE values (1) when an error occurs. So, make it configurable + * by the applet. I suppose we could use a wrapper function to set it, but + * that too seems silly. + */ + +#include +#include "libbb.h" + +int bb_default_error_retval = EXIT_FAILURE; diff --git a/busybox/libbb/device_open.c b/busybox/libbb/device_open.c new file mode 100644 index 000000000..61f954f46 --- /dev/null +++ b/busybox/libbb/device_open.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + + +/* try to open up the specified device */ +extern int device_open(const char *device, int mode) +{ + int m, f, fd = -1; + + m = mode | O_NONBLOCK; + + /* Retry up to 5 times */ + for (f = 0; f < 5; f++) + if ((fd = open(device, m, 0600)) >= 0) + break; + if (fd < 0) + return fd; + /* Reset original flags. */ + if (m != mode) + fcntl(fd, F_SETFL, mode); + return fd; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/dump.c b/busybox/libbb/dump.c new file mode 100644 index 000000000..98f004ff6 --- /dev/null +++ b/busybox/libbb/dump.c @@ -0,0 +1,819 @@ +/* + * Support code for the hexdump and od applets, + * based on code from util-linux v 2.11l + * + * Copyright (c) 1989 + * The Regents of the University of California. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Original copyright notice is retained at the end of this file. + */ + +#include +#include +#include +#include /* for isdigit() */ +#include "libbb.h" +#include "dump.h" + +enum _vflag bb_dump_vflag = FIRST; +FS *bb_dump_fshead; /* head of format strings */ +static FU *endfu; +static char **_argv; +static off_t savaddress; /* saved address/offset in stream */ +static off_t eaddress; /* end address */ +static off_t address; /* address/offset in stream */ +off_t bb_dump_skip; /* bytes to skip */ +static int exitval; /* final exit value */ +int bb_dump_blocksize; /* data block size */ +int bb_dump_length = -1; /* max bytes to read */ + +static const char index_str[] = ".#-+ 0123456789"; + +static const char size_conv_str[] = +"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; + +static const char lcc[] = "diouxX"; + +int bb_dump_size(FS * fs) +{ + register FU *fu; + register int bcnt, cur_size; + register char *fmt; + const char *p; + int prec; + + /* figure out the data block bb_dump_size needed for each format unit */ + for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { + if (fu->bcnt) { + cur_size += fu->bcnt * fu->reps; + continue; + } + for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { + if (*fmt != '%') + continue; + /* + * bb_dump_skip any special chars -- save precision in + * case it's a %s format. + */ + while (strchr(index_str + 1, *++fmt)); + if (*fmt == '.' && isdigit(*++fmt)) { + prec = atoi(fmt); + while (isdigit(*++fmt)); + } + if (!(p = strchr(size_conv_str + 12, *fmt))) { + if (*fmt == 's') { + bcnt += prec; + } else if (*fmt == '_') { + ++fmt; + if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) { + bcnt += 1; + } + } + } else { + bcnt += size_conv_str[p - (size_conv_str + 12)]; + } + } + cur_size += bcnt * fu->reps; + } + return (cur_size); +} + +static void rewrite(FS * fs) +{ + enum { NOTOKAY, USEBCNT, USEPREC } sokay; + register PR *pr, **nextpr = NULL; + register FU *fu; + register char *p1, *p2, *p3; + char savech, *fmtp; + const char *byte_count_str; + int nconv, prec = 0; + + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + /* + * break each format unit into print units; each + * conversion character gets its own. + */ + for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { + /* NOSTRICT */ + /* DBU:[dvae@cray.com] calloc so that forward ptrs start out NULL*/ + pr = (PR *) xcalloc(1,sizeof(PR)); + if (!fu->nextpr) + fu->nextpr = pr; + /* ignore nextpr -- its unused inside the loop and is + * uninitialized 1st time thru. + */ + + /* bb_dump_skip preceding text and up to the next % sign */ + for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); + + /* only text in the string */ + if (!*p1) { + pr->fmt = fmtp; + pr->flags = F_TEXT; + break; + } + + /* + * get precision for %s -- if have a byte count, don't + * need it. + */ + if (fu->bcnt) { + sokay = USEBCNT; + /* bb_dump_skip to conversion character */ + for (++p1; strchr(index_str, *p1); ++p1); + } else { + /* bb_dump_skip any special chars, field width */ + while (strchr(index_str + 1, *++p1)); + if (*p1 == '.' && isdigit(*++p1)) { + sokay = USEPREC; + prec = atoi(p1); + while (isdigit(*++p1)); + } else + sokay = NOTOKAY; + } + + p2 = p1 + 1; /* set end pointer */ + + /* + * figure out the byte count for each conversion; + * rewrite the format as necessary, set up blank- + * pbb_dump_adding for end of data. + */ + + if (*p1 == 'c') { + pr->flags = F_CHAR; + DO_BYTE_COUNT_1: + byte_count_str = "\001"; + DO_BYTE_COUNT: + if (fu->bcnt) { + do { + if (fu->bcnt == *byte_count_str) { + break; + } + } while (*++byte_count_str); + } + /* Unlike the original, output the remainder of the format string. */ + if (!*byte_count_str) { + bb_error_msg_and_die("bad byte count for conversion character %s.", p1); + } + pr->bcnt = *byte_count_str; + } else if (*p1 == 'l') { + ++p2; + ++p1; + DO_INT_CONV: + { + const char *e; + if (!(e = strchr(lcc, *p1))) { + goto DO_BAD_CONV_CHAR; + } + pr->flags = F_INT; + if (e > lcc + 1) { + pr->flags = F_UINT; + } + byte_count_str = "\004\002\001"; + goto DO_BYTE_COUNT; + } + /* NOTREACHED */ + } else if (strchr(lcc, *p1)) { + goto DO_INT_CONV; + } else if (strchr("eEfgG", *p1)) { + pr->flags = F_DBL; + byte_count_str = "\010\004"; + goto DO_BYTE_COUNT; + } else if (*p1 == 's') { + pr->flags = F_STR; + if (sokay == USEBCNT) { + pr->bcnt = fu->bcnt; + } else if (sokay == USEPREC) { + pr->bcnt = prec; + } else { /* NOTOKAY */ + bb_error_msg_and_die("%%s requires a precision or a byte count."); + } + } else if (*p1 == '_') { + ++p2; + switch (p1[1]) { + case 'A': + endfu = fu; + fu->flags |= F_IGNORE; + /* FALLTHROUGH */ + case 'a': + pr->flags = F_ADDRESS; + ++p2; + if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) { + goto DO_BAD_CONV_CHAR; + } + *p1 = p1[2]; + break; + case 'c': + pr->flags = F_C; + /* *p1 = 'c'; set in conv_c */ + goto DO_BYTE_COUNT_1; + case 'p': + pr->flags = F_P; + *p1 = 'c'; + goto DO_BYTE_COUNT_1; + case 'u': + pr->flags = F_U; + /* *p1 = 'c'; set in conv_u */ + goto DO_BYTE_COUNT_1; + default: + goto DO_BAD_CONV_CHAR; + } + } else { + DO_BAD_CONV_CHAR: + bb_error_msg_and_die("bad conversion character %%%s.\n", p1); + } + + /* + * copy to PR format string, set conversion character + * pointer, update original. + */ + savech = *p2; + p1[1] = '\0'; + pr->fmt = bb_xstrdup(fmtp); + *p2 = savech; + pr->cchar = pr->fmt + (p1 - fmtp); + + /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost. + * Skip subsequent text and up to the next % sign and tack the + * additional text onto fmt: eg. if fmt is "%x is a HEX number", + * we lose the " is a HEX number" part of fmt. + */ + for (p3 = p2; *p3 && *p3 != '%'; p3++); + if (p3 > p2) + { + savech = *p3; + *p3 = '\0'; + if (!(pr->fmt = realloc(pr->fmt, strlen(pr->fmt)+(p3-p2)+1))) + bb_perror_msg_and_die("hexdump"); + strcat(pr->fmt, p2); + *p3 = savech; + p2 = p3; + } + + fmtp = p2; + + /* only one conversion character if byte count */ + if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) { + bb_error_msg_and_die("byte count with multiple conversion characters.\n"); + } + } + /* + * if format unit byte count not specified, figure it out + * so can adjust rep count later. + */ + if (!fu->bcnt) + for (pr = fu->nextpr; pr; pr = pr->nextpr) + fu->bcnt += pr->bcnt; + } + /* + * if the format string interprets any data at all, and it's + * not the same as the bb_dump_blocksize, and its last format unit + * interprets any data at all, and has no iteration count, + * repeat it as necessary. + * + * if, rep count is greater than 1, no trailing whitespace + * gets output from the last iteration of the format unit. + */ + for (fu = fs->nextfu;; fu = fu->nextfu) { + if (!fu->nextfu && fs->bcnt < bb_dump_blocksize && + !(fu->flags & F_SETREP) && fu->bcnt) + fu->reps += (bb_dump_blocksize - fs->bcnt) / fu->bcnt; + if (fu->reps > 1) { + for (pr = fu->nextpr;; pr = pr->nextpr) + if (!pr->nextpr) + break; + for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) + p2 = isspace(*p1) ? p1 : NULL; + if (p2) + pr->nospace = p2; + } + if (!fu->nextfu) + break; + } +} + +static void do_skip(char *fname, int statok) +{ + struct stat sbuf; + + if (statok) { + if (fstat(STDIN_FILENO, &sbuf)) { + bb_perror_msg_and_die("%s", fname); + } + if ((!(S_ISCHR(sbuf.st_mode) || + S_ISBLK(sbuf.st_mode) || + S_ISFIFO(sbuf.st_mode))) && bb_dump_skip >= sbuf.st_size) { + /* If bb_dump_size valid and bb_dump_skip >= size */ + bb_dump_skip -= sbuf.st_size; + address += sbuf.st_size; + return; + } + } + if (fseek(stdin, bb_dump_skip, SEEK_SET)) { + bb_perror_msg_and_die("%s", fname); + } + savaddress = address += bb_dump_skip; + bb_dump_skip = 0; +} + +static int next(char **argv) +{ + static int done; + int statok; + + if (argv) { + _argv = argv; + return (1); + } + for (;;) { + if (*_argv) { + if (!(freopen(*_argv, "r", stdin))) { + bb_perror_msg("%s", *_argv); + exitval = 1; + ++_argv; + continue; + } + statok = done = 1; + } else { + if (done++) + return (0); + statok = 0; + } + if (bb_dump_skip) + do_skip(statok ? *_argv : "stdin", statok); + if (*_argv) + ++_argv; + if (!bb_dump_skip) + return (1); + } + /* NOTREACHED */ +} + +static u_char *get(void) +{ + static int ateof = 1; + static u_char *curp=NULL, *savp; /*DBU:[dave@cray.com]initialize curp */ + register int n; + int need, nread; + u_char *tmpp; + + if (!curp) { + address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/ + curp = (u_char *) xmalloc(bb_dump_blocksize); + savp = (u_char *) xmalloc(bb_dump_blocksize); + } else { + tmpp = curp; + curp = savp; + savp = tmpp; + address = savaddress += bb_dump_blocksize; + } + for (need = bb_dump_blocksize, nread = 0;;) { + /* + * if read the right number of bytes, or at EOF for one file, + * and no other files are available, zero-pad the rest of the + * block and set the end flag. + */ + if (!bb_dump_length || (ateof && !next((char **) NULL))) { + if (need == bb_dump_blocksize) { + return ((u_char *) NULL); + } + if (bb_dump_vflag != ALL && !bcmp(curp, savp, nread)) { + if (bb_dump_vflag != DUP) { + printf("*\n"); + } + return ((u_char *) NULL); + } + bzero((char *) curp + nread, need); + eaddress = address + nread; + return (curp); + } + n = fread((char *) curp + nread, sizeof(u_char), + bb_dump_length == -1 ? need : MIN(bb_dump_length, need), stdin); + if (!n) { + if (ferror(stdin)) { + bb_perror_msg("%s", _argv[-1]); + } + ateof = 1; + continue; + } + ateof = 0; + if (bb_dump_length != -1) { + bb_dump_length -= n; + } + if (!(need -= n)) { + if (bb_dump_vflag == ALL || bb_dump_vflag == FIRST + || bcmp(curp, savp, bb_dump_blocksize)) { + if (bb_dump_vflag == DUP || bb_dump_vflag == FIRST) { + bb_dump_vflag = WAIT; + } + return (curp); + } + if (bb_dump_vflag == WAIT) { + printf("*\n"); + } + bb_dump_vflag = DUP; + address = savaddress += bb_dump_blocksize; + need = bb_dump_blocksize; + nread = 0; + } else { + nread += n; + } + } +} + +static void bpad(PR * pr) +{ + register char *p1, *p2; + + /* + * remove all conversion flags; '-' is the only one valid + * with %s, and it's not useful here. + */ + pr->flags = F_BPAD; + *pr->cchar = 's'; + for (p1 = pr->fmt; *p1 != '%'; ++p1); + for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1); + while ((*p2++ = *p1++) != 0); +} + +static const char conv_str[] = + "\0\\0\0" + "\007\\a\0" /* \a */ + "\b\\b\0" + "\f\\b\0" + "\n\\n\0" + "\r\\r\0" + "\t\\t\0" + "\v\\v\0" + "\0"; + + +static void conv_c(PR * pr, u_char * p) +{ + const char *str = conv_str; + char buf[10]; + + do { + if (*p == *str) { + ++str; + goto strpr; + } + str += 4; + } while (*str); + + if (isprint(*p)) { + *pr->cchar = 'c'; + (void) printf(pr->fmt, *p); + } else { + sprintf(buf, "%03o", (int) *p); + str = buf; + strpr: + *pr->cchar = 's'; + printf(pr->fmt, str); + } +} + +static void conv_u(PR * pr, u_char * p) +{ + static const char list[] = + "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0" + "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_" + "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0" + "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us"; + + /* od used nl, not lf */ + if (*p <= 0x1f) { + *pr->cchar = 's'; + printf(pr->fmt, list + (4 * (int)*p)); + } else if (*p == 0x7f) { + *pr->cchar = 's'; + printf(pr->fmt, "del"); + } else if (isprint(*p)) { + *pr->cchar = 'c'; + printf(pr->fmt, *p); + } else { + *pr->cchar = 'x'; + printf(pr->fmt, (int) *p); + } +} + +static void display(void) +{ +/* extern FU *endfu; */ + register FS *fs; + register FU *fu; + register PR *pr; + register int cnt; + register u_char *bp; + + off_t saveaddress; + u_char savech = 0, *savebp; + + while ((bp = get()) != NULL) { + for (fs = bb_dump_fshead, savebp = bp, saveaddress = address; fs; + fs = fs->nextfs, bp = savebp, address = saveaddress) { + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + if (fu->flags & F_IGNORE) { + break; + } + for (cnt = fu->reps; cnt; --cnt) { + for (pr = fu->nextpr; pr; address += pr->bcnt, + bp += pr->bcnt, pr = pr->nextpr) { + if (eaddress && address >= eaddress && + !(pr->flags & (F_TEXT | F_BPAD))) { + bpad(pr); + } + if (cnt == 1 && pr->nospace) { + savech = *pr->nospace; + *pr->nospace = '\0'; + } +/* PRINT; */ + switch (pr->flags) { + case F_ADDRESS: + printf(pr->fmt, (unsigned int) address); + break; + case F_BPAD: + printf(pr->fmt, ""); + break; + case F_C: + conv_c(pr, bp); + break; + case F_CHAR: + printf(pr->fmt, *bp); + break; + case F_DBL:{ + double dval; + float fval; + + switch (pr->bcnt) { + case 4: + bcopy((char *) bp, (char *) &fval, + sizeof(fval)); + printf(pr->fmt, fval); + break; + case 8: + bcopy((char *) bp, (char *) &dval, + sizeof(dval)); + printf(pr->fmt, dval); + break; + } + break; + } + case F_INT:{ + int ival; + short sval; + + switch (pr->bcnt) { + case 1: + printf(pr->fmt, (int) *bp); + break; + case 2: + bcopy((char *) bp, (char *) &sval, + sizeof(sval)); + printf(pr->fmt, (int) sval); + break; + case 4: + bcopy((char *) bp, (char *) &ival, + sizeof(ival)); + printf(pr->fmt, ival); + break; + } + break; + } + case F_P: + printf(pr->fmt, isprint(*bp) ? *bp : '.'); + break; + case F_STR: + printf(pr->fmt, (char *) bp); + break; + case F_TEXT: + printf(pr->fmt); + break; + case F_U: + conv_u(pr, bp); + break; + case F_UINT:{ + unsigned int ival; + unsigned short sval; + + switch (pr->bcnt) { + case 1: + printf(pr->fmt, (unsigned int) * bp); + break; + case 2: + bcopy((char *) bp, (char *) &sval, + sizeof(sval)); + printf(pr->fmt, (unsigned int) sval); + break; + case 4: + bcopy((char *) bp, (char *) &ival, + sizeof(ival)); + printf(pr->fmt, ival); + break; + } + break; + } + } + if (cnt == 1 && pr->nospace) { + *pr->nospace = savech; + } + } + } + } + } + } + if (endfu) { + /* + * if eaddress not set, error or file bb_dump_size was multiple of + * bb_dump_blocksize, and no partial block ever found. + */ + if (!eaddress) { + if (!address) { + return; + } + eaddress = address; + } + for (pr = endfu->nextpr; pr; pr = pr->nextpr) { + switch (pr->flags) { + case F_ADDRESS: + (void) printf(pr->fmt, (unsigned int) eaddress); + break; + case F_TEXT: + (void) printf(pr->fmt); + break; + } + } + } +} + +int bb_dump_dump(char **argv) +{ + register FS *tfs; + + /* figure out the data block bb_dump_size */ + for (bb_dump_blocksize = 0, tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) { + tfs->bcnt = bb_dump_size(tfs); + if (bb_dump_blocksize < tfs->bcnt) { + bb_dump_blocksize = tfs->bcnt; + } + } + /* rewrite the rules, do syntax checking */ + for (tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) { + rewrite(tfs); + } + + next(argv); + display(); + + return (exitval); +} + +void bb_dump_add(const char *fmt) +{ + register const char *p; + register char *p1; + register char *p2; + static FS **nextfs; + FS *tfs; + FU *tfu, **nextfu; + const char *savep; + + /* start new linked list of format units */ + /* NOSTRICT */ + tfs = (FS *) xcalloc(1,sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */ + if (!bb_dump_fshead) { + bb_dump_fshead = tfs; + } else { + *nextfs = tfs; + } + nextfs = &tfs->nextfs; + nextfu = &tfs->nextfu; + + /* take the format string and break it up into format units */ + for (p = fmt;;) { + /* bb_dump_skip leading white space */ + p = bb_skip_whitespace(p); + if (!*p) { + break; + } + + /* allocate a new format unit and link it in */ + /* NOSTRICT */ + /* DBU:[dave@cray.com] calloc so that forward pointers start out NULL */ + tfu = (FU *) xcalloc(1,sizeof(FU)); + *nextfu = tfu; + nextfu = &tfu->nextfu; + tfu->reps = 1; + + /* if leading digit, repetition count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p); + if (!isspace(*p) && *p != '/') { + bb_error_msg_and_die("bad format {%s}", fmt); + } + /* may overwrite either white space or slash */ + tfu->reps = atoi(savep); + tfu->flags = F_SETREP; + /* bb_dump_skip trailing white space */ + p = bb_skip_whitespace(++p); + } + + /* bb_dump_skip slash and trailing white space */ + if (*p == '/') { + p = bb_skip_whitespace(++p); + } + + /* byte count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p); + if (!isspace(*p)) { + bb_error_msg_and_die("bad format {%s}", fmt); + } + tfu->bcnt = atoi(savep); + /* bb_dump_skip trailing white space */ + p = bb_skip_whitespace(++p); + } + + /* format */ + if (*p != '"') { + bb_error_msg_and_die("bad format {%s}", fmt); + } + for (savep = ++p; *p != '"';) { + if (*p++ == 0) { + bb_error_msg_and_die("bad format {%s}", fmt); + } + } + tfu->fmt = xmalloc(p - savep + 1); + strncpy(tfu->fmt, savep, p - savep); + tfu->fmt[p - savep] = '\0'; +/* escape(tfu->fmt); */ + + p1 = tfu->fmt; + + /* alphabetic escape sequences have to be done in place */ + for (p2 = p1;; ++p1, ++p2) { + if (!*p1) { + *p2 = *p1; + break; + } + if (*p1 == '\\') { + const char *cs = conv_str + 4; + ++p1; + *p2 = *p1; + do { + if (*p1 == cs[2]) { + *p2 = cs[0]; + break; + } + cs += 4; + } while (*cs); + } + } + + p++; + } +} + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/busybox/libbb/error_msg.c b/busybox/libbb/error_msg.c new file mode 100644 index 000000000..18811b8d1 --- /dev/null +++ b/busybox/libbb/error_msg.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_error_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p); + va_end(p); + putc('\n', stderr); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/error_msg_and_die.c b/busybox/libbb/error_msg_and_die.c new file mode 100644 index 000000000..0937658a3 --- /dev/null +++ b/busybox/libbb/error_msg_and_die.c @@ -0,0 +1,47 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_error_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p); + va_end(p); + putc('\n', stderr); + exit(bb_default_error_retval); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/fclose_nonstdin.c b/busybox/libbb/fclose_nonstdin.c new file mode 100644 index 000000000..8f489c879 --- /dev/null +++ b/busybox/libbb/fclose_nonstdin.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * fclose_nonstdin implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* A number of standard utilities can accept multiple command line args + * of '-' for stdin, according to SUSv3. So we encapsulate the check + * here to save a little space. + */ + +#include +#include + +int bb_fclose_nonstdin(FILE *f) +{ + if (f != stdin) { + return fclose(f); + } + return 0; +} diff --git a/busybox/libbb/fflush_stdout_and_exit.c b/busybox/libbb/fflush_stdout_and_exit.c new file mode 100644 index 000000000..cbba04207 --- /dev/null +++ b/busybox/libbb/fflush_stdout_and_exit.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * fflush_stdout_and_exit implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Attempt to fflush(stdout), and exit with an error code if stdout is + * in an error state. + */ + +#include +#include +#include + +void bb_fflush_stdout_and_exit(int retval) +{ + if (fflush(stdout)) { + retval = bb_default_error_retval; + } + exit(retval); +} diff --git a/busybox/libbb/fgets_str.c b/busybox/libbb/fgets_str.c new file mode 100644 index 000000000..bf828be95 --- /dev/null +++ b/busybox/libbb/fgets_str.c @@ -0,0 +1,67 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include + +#include "libbb.h" + +/* Read up to (and including) TERMINATING_STRING from FILE and return it. + * Return NULL on EOF. */ + +char *fgets_str(FILE *file, const char *terminating_string) +{ + char *linebuf = NULL; + const int term_length = strlen(terminating_string); + int end_string_offset; + int linebufsz = 0; + int idx = 0; + int ch; + + while (1) { + ch = fgetc(file); + if (ch == EOF) { + free(linebuf); + return NULL; + } + + /* grow the line buffer as necessary */ + while (idx > linebufsz - 2) { + linebuf = xrealloc(linebuf, linebufsz += 1000); + } + + linebuf[idx] = ch; + idx++; + + /* Check for terminating string */ + end_string_offset = idx - term_length; + if ((end_string_offset > 0) && (memcmp(&linebuf[end_string_offset], terminating_string, term_length) == 0)) { + idx -= term_length; + break; + } + } + linebuf[idx] = '\0'; + return(linebuf); +} + diff --git a/busybox/libbb/find_mount_point.c b/busybox/libbb/find_mount_point.c new file mode 100644 index 000000000..83824de9e --- /dev/null +++ b/busybox/libbb/find_mount_point.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "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..930710f32 --- /dev/null +++ b/busybox/libbb/find_pid_by_name.c @@ -0,0 +1,70 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +#define COMM_LEN 16 /* synchronize with size of comm in struct task_struct + in /usr/include/linux/sched.h */ + + +/* find_pid_by_name() + * + * Modified by Vladimir Oleynik for use with libbb/procps.c + * This finds the pid of the specified process. + * Currently, it's implemented by rummaging through + * the proc filesystem. + * + * Returns a list of all matching PIDs + */ +extern long* find_pid_by_name( const char* pidName) +{ + long* pidList; + int i=0; + procps_status_t * p; + + pidList = xmalloc(sizeof(long)); +#ifdef CONFIG_SELINUX + while ((p = procps_scan(0, 0, NULL)) != 0) { +#else + while ((p = procps_scan(0)) != 0) { +#endif + if (strncmp(p->short_cmd, pidName, COMM_LEN-1) == 0) { + pidList=xrealloc( pidList, sizeof(long) * (i+2)); + pidList[i++]=p->pid; + } + } + + pidList[i] = i==0 ? -1 : 0; + return pidList; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/find_root_device.c b/busybox/libbb/find_root_device.c new file mode 100644 index 000000000..2600ce5e0 --- /dev/null +++ b/busybox/libbb/find_root_device.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + + + +extern char *find_real_root_device_name(void) +{ + DIR *dir; + struct dirent *entry; + struct stat statBuf, rootStat; + char *fileName = NULL; + dev_t dev; + + if (stat("/", &rootStat) != 0) + bb_perror_msg("could not stat '/'"); + else { + /* This check is here in case they pass in /dev name */ + if ((rootStat.st_mode & S_IFMT) == S_IFBLK) + dev = rootStat.st_rdev; + else + dev = rootStat.st_dev; + + dir = opendir("/dev"); + if (!dir) + bb_perror_msg("could not open '/dev'"); + else { + while((entry = readdir(dir)) != NULL) { + const char *myname = entry->d_name; + /* Must skip ".." since that is "/", and so we + * would get a false positive on ".." */ + if (myname[0] == '.' && myname[1] == '.' && !myname[2]) + continue; +#ifdef CONFIG_FEATURE_DEVFS + /* if there is a link named /dev/root skip that too */ + if (strcmp(myname, "root")==0) + continue; +#endif + fileName = concat_path_file("/dev", myname); + + /* Some char devices have the same dev_t as block + * devices, so make sure this is a block device */ + if (stat(fileName, &statBuf) == 0 && + S_ISBLK(statBuf.st_mode)!=0 && + statBuf.st_rdev == dev) + break; + free(fileName); + fileName=NULL; + } + closedir(dir); + } + } + if(fileName==NULL) + fileName = bb_xstrdup("/dev/root"); + return fileName; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/full_read.c b/busybox/libbb/full_read.c new file mode 100644 index 000000000..221fc947b --- /dev/null +++ b/busybox/libbb/full_read.c @@ -0,0 +1,63 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +ssize_t bb_full_read(int fd, void *buf, size_t len) +{ + ssize_t cc; + ssize_t total; + + total = 0; + + while (len > 0) { + cc = safe_read(fd, buf, len); + + if (cc < 0) + return cc; /* read() returns -1 on failure. */ + + if (cc == 0) + break; + + buf = ((char *)buf) + cc; + total += cc; + len -= cc; + } + + return total; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/full_write.c b/busybox/libbb/full_write.c new file mode 100644 index 000000000..30de4078a --- /dev/null +++ b/busybox/libbb/full_write.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +/* + * Write all of the supplied buffer out to a file. + * This does multiple writes as necessary. + * Returns the amount written, or -1 on an error. + */ +ssize_t bb_full_write(int fd, const void *buf, size_t len) +{ + ssize_t cc; + ssize_t total; + + total = 0; + + while (len > 0) { + cc = safe_write(fd, buf, len); + + if (cc < 0) + return cc; /* write() returns -1 on failure. */ + + total += cc; + buf = ((const char *)buf) + cc; + len -= cc; + } + + return total; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/get_console.c b/busybox/libbb/get_console.c new file mode 100644 index 000000000..bfb7468a8 --- /dev/null +++ b/busybox/libbb/get_console.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. If you wrote this, please + * acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "libbb.h" + + + +/* From */ +static const int KDGKBTYPE = 0x4B33; /* get keyboard type */ + + +static int open_a_console(const char *fnam) +{ + int fd; + + /* try read-write */ + fd = open(fnam, O_RDWR); + + /* if failed, try read-only */ + if (fd < 0 && errno == EACCES) + fd = open(fnam, O_RDONLY); + + /* if failed, try write-only */ + if (fd < 0 && errno == EACCES) + fd = open(fnam, O_WRONLY); + + return fd; +} + +/* + * Get an fd for use with kbd/console ioctls. + * We try several things because opening /dev/console will fail + * if someone else used X (which does a chown on /dev/console). + */ + +int get_console_fd(void) +{ + int fd; + + static const char * const choise_console_names[] = { + CONSOLE_DEV, CURRENT_VC, CURRENT_TTY + }; + + for (fd = 2; fd >= 0; fd--) { + int fd4name; + int choise_fd; + char arg; + + fd4name = open_a_console(choise_console_names[fd]); + chk_std: + choise_fd = fd4name >= 0 ? fd4name : fd; + + arg = 0; + if (ioctl(choise_fd, KDGKBTYPE, &arg) == 0) + return choise_fd; + if(fd4name >= 0) { + close(fd4name); + fd4name = -1; + goto chk_std; + } + } + + bb_error_msg("Couldn't get a file descriptor referring to the console"); + return fd; /* total failure */ +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/get_last_path_component.c b/busybox/libbb/get_last_path_component.c new file mode 100644 index 000000000..497d6ae4e --- /dev/null +++ b/busybox/libbb/get_last_path_component.c @@ -0,0 +1,56 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_get_last_path_component implementation for busybox + * + * Copyright (C) 2001 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Set to 1 if you want basename() behavior for NULL or "". */ +/* WARNING!!! Doing so will break basename applet at least! */ +#define EMULATE_BASENAME 0 + +char *bb_get_last_path_component(char *path) +{ +#if EMULATE_BASENAME + static const char null_or_empty[] = "."; +#endif + char *first = path; + char *last; + +#if EMULATE_BASENAME + if (!path || !*path) { + return (char *) null_or_empty; + } +#endif + + last = path - 1; + + while (*path) { + if ((*path != '/') && (path > ++last)) { + last = first = path; + } + ++path; + } + + if (*first == '/') { + last = first; + } + last[1] = 0; + + return first; +} diff --git a/busybox/libbb/get_line_from_file.c b/busybox/libbb/get_line_from_file.c new file mode 100644 index 000000000..6d12b21c4 --- /dev/null +++ b/busybox/libbb/get_line_from_file.c @@ -0,0 +1,82 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#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. If 'c' is nonzero, the trailing '\n' (if any) + * is removed. In event of a read error or EOF, NULL is returned. */ + +static char *private_get_line_from_file(FILE *file, int c) +{ +#define GROWBY (80) /* how large we will grow strings by */ + + int ch; + int idx = 0; + char *linebuf = NULL; + int linebufsz = 0; + + while ((ch = getc(file)) != EOF) { + /* grow the line buffer as necessary */ + if (idx > linebufsz - 2) { + linebuf = xrealloc(linebuf, linebufsz += GROWBY); + } + linebuf[idx++] = (char)ch; + if (ch == '\n' || ch == '\0') { + if (c) { + --idx; + } + break; + } + } + if (linebuf) { + if (ferror(file)) { + free(linebuf); + return NULL; + } + linebuf[idx] = 0; + } + return linebuf; +} + +extern char *bb_get_line_from_file(FILE *file) +{ + return private_get_line_from_file(file, 0); +} + +extern char *bb_get_chomped_line_from_file(FILE *file) +{ + return private_get_line_from_file(file, 1); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/get_terminal_width_height.c b/busybox/libbb/get_terminal_width_height.c new file mode 100644 index 000000000..7a1af6dc1 --- /dev/null +++ b/busybox/libbb/get_terminal_width_height.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * Determine the width and height of the terminal. + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +/* It is perfectly ok to pass in a NULL for either width or for + * height, in which case that value will not be set. It is also + * perfectly ok to have CONFIG_FEATURE_AUTOWIDTH disabled, in + * which case you will always get 80x24 */ +void get_terminal_width_height(int fd, int *width, int *height) +{ + struct winsize win = { 0, 0, 0, 0 }; +#ifdef CONFIG_FEATURE_AUTOWIDTH + if (ioctl(fd, TIOCGWINSZ, &win) != 0) { + win.ws_row = 24; + win.ws_col = 80; + } +#endif + if (win.ws_row <= 1) { + win.ws_row = 24; + } + if (win.ws_col <= 1) { + win.ws_col = 80; + } + if (height) { + *height = (int) win.ws_row; + } + if (width) { + *width = (int) win.ws_col; + } +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + diff --git a/busybox/libbb/get_ug_id.c b/busybox/libbb/get_ug_id.c new file mode 100644 index 000000000..b0b9b2e53 --- /dev/null +++ b/busybox/libbb/get_ug_id.c @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +extern unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *)) +{ + unsigned long r; + char *p; + + r = strtoul(s, &p, 10); + if (*p || (s == p)) { + r = my_getxxnam(s); + } + + return r; +} diff --git a/busybox/libbb/getopt_ulflags.c b/busybox/libbb/getopt_ulflags.c new file mode 100644 index 000000000..39a7d1d29 --- /dev/null +++ b/busybox/libbb/getopt_ulflags.c @@ -0,0 +1,171 @@ +/* vi: set sw=4 ts=4: */ +/* + * universal getopt_ulflags implementation for busybox + * + * Copyright (C) 2003 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 "libbb.h" + +/* +You can set bb_opt_complementaly as string with one or more +complementaly or incongruously options. +If sequential founded option haved from this string +then your incongruously pairs unsets and complementaly make add sets. +Format: +one char - option for check, +chars - complementaly option for add sets. +- chars - option triggered for unsets. +~ chars - option incongruously. +* - option list, called add_to_list(*ptr_from_usaged, optarg) +: - separator. +Example: du applet can have options "-s" and "-d size" +If getopt found -s then -d option flag unset or if found -d then -s unset. +For this result you must set bb_opt_complementaly = "s-d:d-s". +Result have last option flag only from called arguments. +Warning! You can check returned flag, pointer to "d:" argument seted +to own optarg always. +Example two: cut applet must only one type of list may be specified, +and -b, -c and -f incongruously option, overwited option is error also. +You must set bb_opt_complementaly = "b~cf:c~bf:f~bc". +If called have more one specified, return value have error flag - +high bite set (0x80000000UL). +Example three: grep applet can have one or more "-e pattern" arguments. +You should use bb_getopt_ulflags() as +llist_t *paterns; +bb_opt_complementaly = "e*"; +bb_getopt_ulflags (argc, argv, "e:", &paterns); +*/ + +const char *bb_opt_complementaly; + +typedef struct +{ + unsigned char opt; + char list_flg; + unsigned long switch_on; + unsigned long switch_off; + unsigned long incongruously; + void **optarg; /* char **optarg or llist_t **optarg */ +} t_complementaly; + +/* You can set bb_applet_long_options for parse called long options */ + +static const struct option bb_default_long_options[] = { + /* { "help", 0, NULL, '?' }, */ + { 0, 0, 0, 0 } +}; + +const struct option *bb_applet_long_options = bb_default_long_options; + + +unsigned long +bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...) +{ + unsigned long flags = 0; + t_complementaly complementaly[sizeof(flags) * 8 + 1]; + int c; + const unsigned char *s; + t_complementaly *on_off; + va_list p; + + va_start (p, applet_opts); + + /* skip GNU extension */ + s = applet_opts; + if(*s == '+' || *s == '-') + s++; + + c = 0; + on_off = complementaly; + for (; *s; s++) { + if(c >= (sizeof(flags)*8)) + break; + on_off->opt = *s; + on_off->switch_on = (1 << c); + on_off->list_flg = 0; + on_off->switch_off = 0; + on_off->incongruously = 0; + on_off->optarg = NULL; + if (s[1] == ':') { + on_off->optarg = va_arg (p, void **); + do + s++; + while (s[1] == ':'); + } + on_off++; + c++; + } + on_off->opt = 0; + c = 0; + for (s = bb_opt_complementaly; s && *s; s++) { + t_complementaly *pair; + + if (*s == ':') { + c = 0; + continue; + } + if (c) + continue; + for (on_off = complementaly; on_off->opt; on_off++) + if (on_off->opt == *s) + break; + pair = on_off; + for(s++; *s && *s != ':'; s++) { + if (*s == '-' || *s == '~') { + c = *s; + } else if(*s == '*') { + pair->list_flg++; + } else { + unsigned long *pair_switch = &(pair->switch_on); + + if(c) + pair_switch = c == '-' ? &(pair->switch_off) : &(pair->incongruously); + for (on_off = complementaly; on_off->opt; on_off++) + if (on_off->opt == *s) { + *pair_switch |= on_off->switch_on; + break; + } + } + } + s--; + } + + while ((c = getopt_long (argc, argv, applet_opts, + bb_applet_long_options, NULL)) > 0) { + for (on_off = complementaly; on_off->opt != c; on_off++) { + if(!on_off->opt) + bb_show_usage (); + } + if(flags & on_off->incongruously) + flags |= 0x80000000UL; + flags &= ~on_off->switch_off; + flags |= on_off->switch_on; + if(on_off->list_flg) { + *(llist_t **)(on_off->optarg) = + llist_add_to(*(llist_t **)(on_off->optarg), optarg); + } else if (on_off->optarg) { + *(char **)(on_off->optarg) = optarg; + } + } + return flags; +} diff --git a/busybox/libbb/hash_fd.c b/busybox/libbb/hash_fd.c new file mode 100644 index 000000000..e37ac549a --- /dev/null +++ b/busybox/libbb/hash_fd.c @@ -0,0 +1,859 @@ +/* + * Based on shasum from http://www.netsw.org/crypto/hash/ + * Majorly hacked up to use Dr Brian Gladman's sha1 code + * + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003 Erik Andersen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + + +#ifdef CONFIG_SHA1SUM +/* + --------------------------------------------------------------------------- + Begin Dr. Gladman's sha1 code + --------------------------------------------------------------------------- +*/ + +/* + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 10/11/2002 + + This is a byte oriented version of SHA1 that operates on arrays of bytes + stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor +*/ + +# define SHA1_BLOCK_SIZE 64 +# define SHA1_DIGEST_SIZE 20 +# define SHA1_HASH_SIZE SHA1_DIGEST_SIZE +# define SHA2_GOOD 0 +# define SHA2_BAD 1 + +# define rotl32(x,n) (((x) << n) | ((x) >> (32 - n))) + +# if __BYTE_ORDER == __BIG_ENDIAN +# define swap_b32(x) (x) +# elif defined(bswap_32) +# define swap_b32(x) bswap_32(x) +# else +# define swap_b32(x) ((rotl32((x), 8) & 0x00ff00ff) | (rotl32((x), 24) & 0xff00ff00)) +# endif /* __BYTE_ORDER */ + +# define SHA1_MASK (SHA1_BLOCK_SIZE - 1) + +/* reverse byte order in 32-bit words */ +#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define parity(x,y,z) ((x) ^ (y) ^ (z)) +#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) + +/* A normal version as set out in the FIPS. This version uses */ +/* partial loop unrolling and is optimised for the Pentium 4 */ +# define rnd(f,k) \ + t = a; a = rotl32(a,5) + f(b,c,d) + e + k + w[i]; \ + e = d; d = c; c = rotl32(b, 30); b = t + +/* type to hold the SHA1 context */ +struct sha1_ctx_t { + uint32_t count[2]; + uint32_t hash[5]; + uint32_t wbuf[16]; +}; + +static void sha1_compile(struct sha1_ctx_t *ctx) +{ + uint32_t w[80], i, a, b, c, d, e, t; + + /* note that words are compiled from the buffer into 32-bit */ + /* words in big-endian order so an order reversal is needed */ + /* here on little endian machines */ + for (i = 0; i < SHA1_BLOCK_SIZE / 4; ++i) + w[i] = swap_b32(ctx->wbuf[i]); + + for (i = SHA1_BLOCK_SIZE / 4; i < 80; ++i) + w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + + a = ctx->hash[0]; + b = ctx->hash[1]; + c = ctx->hash[2]; + d = ctx->hash[3]; + e = ctx->hash[4]; + + for (i = 0; i < 20; ++i) { + rnd(ch, 0x5a827999); + } + + for (i = 20; i < 40; ++i) { + rnd(parity, 0x6ed9eba1); + } + + for (i = 40; i < 60; ++i) { + rnd(maj, 0x8f1bbcdc); + } + + for (i = 60; i < 80; ++i) { + rnd(parity, 0xca62c1d6); + } + + ctx->hash[0] += a; + ctx->hash[1] += b; + ctx->hash[2] += c; + ctx->hash[3] += d; + ctx->hash[4] += e; +} + +static void sha1_begin(struct sha1_ctx_t *ctx) +{ + ctx->count[0] = ctx->count[1] = 0; + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->hash[4] = 0xc3d2e1f0; +} + +/* SHA1 hash data in an array of bytes into hash buffer and call the */ +/* hash_compile function as required. */ +static void sha1_hash(const void *data, size_t len, void *ctx_v) +{ + struct sha1_ctx_t *ctx = (struct sha1_ctx_t *) ctx_v; + uint32_t pos = (uint32_t) (ctx->count[0] & SHA1_MASK); + uint32_t freeb = SHA1_BLOCK_SIZE - pos; + const unsigned char *sp = data; + + if ((ctx->count[0] += len) < len) + ++(ctx->count[1]); + + while (len >= freeb) { /* tranfer whole blocks while possible */ + memcpy(((unsigned char *) ctx->wbuf) + pos, sp, freeb); + sp += freeb; + len -= freeb; + freeb = SHA1_BLOCK_SIZE; + pos = 0; + sha1_compile(ctx); + } + + memcpy(((unsigned char *) ctx->wbuf) + pos, sp, len); +} + +/* SHA1 Final padding and digest calculation */ +# if __BYTE_ORDER == __LITTLE_ENDIAN +static uint32_t mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff }; +static uint32_t bits[4] = { 0x00000080, 0x00008000, 0x00800000, 0x80000000 }; +# else +static uint32_t mask[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 }; +static uint32_t bits[4] = { 0x80000000, 0x00800000, 0x00008000, 0x00000080 }; +# endif /* __BYTE_ORDER */ + +void sha1_end(unsigned char hval[], struct sha1_ctx_t *ctx) +{ + uint32_t i, cnt = (uint32_t) (ctx->count[0] & SHA1_MASK); + + /* mask out the rest of any partial 32-bit word and then set */ + /* the next byte to 0x80. On big-endian machines any bytes in */ + /* the buffer will be at the top end of 32 bit words, on little */ + /* endian machines they will be at the bottom. Hence the AND */ + /* and OR masks above are reversed for little endian systems */ + ctx->wbuf[cnt >> 2] = + (ctx->wbuf[cnt >> 2] & mask[cnt & 3]) | bits[cnt & 3]; + + /* we need 9 or more empty positions, one for the padding byte */ + /* (above) and eight for the length count. If there is not */ + /* enough space pad and empty the buffer */ + if (cnt > SHA1_BLOCK_SIZE - 9) { + if (cnt < 60) + ctx->wbuf[15] = 0; + sha1_compile(ctx); + cnt = 0; + } else /* compute a word index for the empty buffer positions */ + cnt = (cnt >> 2) + 1; + + while (cnt < 14) /* and zero pad all but last two positions */ + ctx->wbuf[cnt++] = 0; + + /* assemble the eight byte counter in the buffer in big-endian */ + /* format */ + + ctx->wbuf[14] = swap_b32((ctx->count[1] << 3) | (ctx->count[0] >> 29)); + ctx->wbuf[15] = swap_b32(ctx->count[0] << 3); + + sha1_compile(ctx); + + /* extract the hash value as bytes in case the hash buffer is */ + /* misaligned for 32-bit words */ + + for (i = 0; i < SHA1_DIGEST_SIZE; ++i) + hval[i] = (unsigned char) (ctx->hash[i >> 2] >> 8 * (~i & 3)); +} + +/* + --------------------------------------------------------------------------- + End of Dr. Gladman's sha1 code + --------------------------------------------------------------------------- +*/ +#endif /* CONFIG_SHA1 */ + + + + + +#ifdef CONFIG_MD5SUM +/* + * md5sum.c - Compute MD5 checksum of files or strings according to the + * definition of MD5 in RFC 1321 from April 1992. + * + * Copyright (C) 1995-1999 Free Software Foundation, Inc. + * Written by Ulrich Drepper , 1995. + * + * + * June 29, 2001 Manuel Novoa III + * + * Added MD5SUM_SIZE_VS_SPEED configuration option. + * + * Current valid values, with data from my system for comparison, are: + * (using uClibc and running on linux-2.4.4.tar.bz2) + * user times (sec) text size (386) + * 0 (fastest) 1.1 6144 + * 1 1.4 5392 + * 2 3.0 5088 + * 3 (smallest) 5.1 4912 + */ + +# define MD5SUM_SIZE_VS_SPEED 2 + +/* Handle endian-ness */ +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define SWAP(n) (n) +# elif defined(bswap_32) +# define SWAP(n) bswap_32(n) +# else +# define SWAP(n) ((n << 24) | ((n&65280)<<8) | ((n&16711680)>>8) | (n>>24)) +# endif + +# if MD5SUM_SIZE_VS_SPEED == 0 +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; +# endif /* MD5SUM_SIZE_VS_SPEED == 0 */ + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx_t { + uint32_t A; + uint32_t B; + uint32_t C; + uint32_t D; + uint32_t total[2]; + uint32_t buflen; + char buffer[128]; +}; + +/* Initialize structure containing state of computation. + * (RFC 1321, 3.3: Step 3) + */ +static void md5_begin(struct md5_ctx_t *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + * and defined in the RFC 1321. The first function is a little bit optimized + * (as found in Colin Plumbs public domain implementation). + * #define FF(b, c, d) ((b & c) | (~b & d)) + */ +# define FF(b, c, d) (d ^ (b & (c ^ d))) +# define FG(b, c, d) FF (d, b, c) +# define FH(b, c, d) (b ^ c ^ d) +# define FI(b, c, d) (c ^ (b | ~d)) + +/* Starting with the result of former calls of this function (or the + * initialization function update the context for the next LEN bytes + * starting at BUFFER. + * It is necessary that LEN is a multiple of 64!!! + */ +static void md5_hash_block(const void *buffer, size_t len, struct md5_ctx_t *ctx) +{ + uint32_t correct_words[16]; + const uint32_t *words = buffer; + size_t nwords = len / sizeof(uint32_t); + const uint32_t *endp = words + nwords; + +# if MD5SUM_SIZE_VS_SPEED > 0 + static const uint32_t C_array[] = { + /* round 1 */ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + /* round 2 */ + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + /* round 3 */ + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + /* round 4 */ + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + + static const char P_array[] = { +# if MD5SUM_SIZE_VS_SPEED > 1 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ +# endif /* MD5SUM_SIZE_VS_SPEED > 1 */ + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ + }; + +# if MD5SUM_SIZE_VS_SPEED > 1 + static const char S_array[] = { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; +# endif /* MD5SUM_SIZE_VS_SPEED > 1 */ +# endif + + uint32_t A = ctx->A; + uint32_t B = ctx->B; + uint32_t C = ctx->C; + uint32_t D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) { + uint32_t *cwp = correct_words; + uint32_t A_save = A; + uint32_t B_save = B; + uint32_t C_save = C; + uint32_t D_save = D; + +# if MD5SUM_SIZE_VS_SPEED > 1 +# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + const uint32_t *pc; + const char *pp; + const char *ps; + int i; + uint32_t temp; + + for (i = 0; i < 16; i++) { + cwp[i] = SWAP(words[i]); + } + words += 16; + +# if MD5SUM_SIZE_VS_SPEED > 2 + pc = C_array; + pp = P_array; + ps = S_array - 4; + + for (i = 0; i < 64; i++) { + if ((i & 0x0f) == 0) + ps += 4; + temp = A; + switch (i >> 4) { + case 0: + temp += FF(B, C, D); + break; + case 1: + temp += FG(B, C, D); + break; + case 2: + temp += FH(B, C, D); + break; + case 3: + temp += FI(B, C, D); + } + temp += cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } +# else + pc = C_array; + pp = P_array; + ps = S_array; + + for (i = 0; i < 16; i++) { + temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + +# endif /* MD5SUM_SIZE_VS_SPEED > 2 */ +# else + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +# define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ + /* gcc 2.95.4 seems to be --aaronl */ +# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + +# if MD5SUM_SIZE_VS_SPEED == 1 + const uint32_t *pc; + const char *pp; + int i; +# endif /* MD5SUM_SIZE_VS_SPEED */ + + /* Round 1. */ +# if MD5SUM_SIZE_VS_SPEED == 1 + pc = C_array; + for (i = 0; i < 4; i++) { + OP(A, B, C, D, 7, *pc++); + OP(D, A, B, C, 12, *pc++); + OP(C, D, A, B, 17, *pc++); + OP(B, C, D, A, 22, *pc++); + } +# else + OP(A, B, C, D, 7, 0xd76aa478); + OP(D, A, B, C, 12, 0xe8c7b756); + OP(C, D, A, B, 17, 0x242070db); + OP(B, C, D, A, 22, 0xc1bdceee); + OP(A, B, C, D, 7, 0xf57c0faf); + OP(D, A, B, C, 12, 0x4787c62a); + OP(C, D, A, B, 17, 0xa8304613); + OP(B, C, D, A, 22, 0xfd469501); + OP(A, B, C, D, 7, 0x698098d8); + OP(D, A, B, C, 12, 0x8b44f7af); + OP(C, D, A, B, 17, 0xffff5bb1); + OP(B, C, D, A, 22, 0x895cd7be); + OP(A, B, C, D, 7, 0x6b901122); + OP(D, A, B, C, 12, 0xfd987193); + OP(C, D, A, B, 17, 0xa679438e); + OP(B, C, D, A, 22, 0x49b40821); +# endif /* MD5SUM_SIZE_VS_SPEED == 1 */ + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +# undef OP +# define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ +# if MD5SUM_SIZE_VS_SPEED == 1 + pp = P_array; + for (i = 0; i < 4; i++) { + OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++); + OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++); + OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++); + OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++); + } +# else + OP(FG, A, B, C, D, 1, 5, 0xf61e2562); + OP(FG, D, A, B, C, 6, 9, 0xc040b340); + OP(FG, C, D, A, B, 11, 14, 0x265e5a51); + OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP(FG, A, B, C, D, 5, 5, 0xd62f105d); + OP(FG, D, A, B, C, 10, 9, 0x02441453); + OP(FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP(FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP(FG, D, A, B, C, 14, 9, 0xc33707d6); + OP(FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP(FG, B, C, D, A, 8, 20, 0x455a14ed); + OP(FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP(FG, C, D, A, B, 7, 14, 0x676f02d9); + OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a); +# endif /* MD5SUM_SIZE_VS_SPEED == 1 */ + + /* Round 3. */ +# if MD5SUM_SIZE_VS_SPEED == 1 + for (i = 0; i < 4; i++) { + OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++); + OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++); + OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++); + OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++); + } +# else + OP(FH, A, B, C, D, 5, 4, 0xfffa3942); + OP(FH, D, A, B, C, 8, 11, 0x8771f681); + OP(FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP(FH, B, C, D, A, 14, 23, 0xfde5380c); + OP(FH, A, B, C, D, 1, 4, 0xa4beea44); + OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP(FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP(FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP(FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP(FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP(FH, B, C, D, A, 6, 23, 0x04881d05); + OP(FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP(FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP(FH, B, C, D, A, 2, 23, 0xc4ac5665); +# endif /* MD5SUM_SIZE_VS_SPEED == 1 */ + + /* Round 4. */ +# if MD5SUM_SIZE_VS_SPEED == 1 + for (i = 0; i < 4; i++) { + OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++); + OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++); + OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++); + OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++); + } +# else + OP(FI, A, B, C, D, 0, 6, 0xf4292244); + OP(FI, D, A, B, C, 7, 10, 0x432aff97); + OP(FI, C, D, A, B, 14, 15, 0xab9423a7); + OP(FI, B, C, D, A, 5, 21, 0xfc93a039); + OP(FI, A, B, C, D, 12, 6, 0x655b59c3); + OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP(FI, C, D, A, B, 10, 15, 0xffeff47d); + OP(FI, B, C, D, A, 1, 21, 0x85845dd1); + OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP(FI, C, D, A, B, 6, 15, 0xa3014314); + OP(FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP(FI, A, B, C, D, 4, 6, 0xf7537e82); + OP(FI, D, A, B, C, 11, 10, 0xbd3af235); + OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP(FI, B, C, D, A, 9, 21, 0xeb86d391); +# endif /* MD5SUM_SIZE_VS_SPEED == 1 */ +# endif /* MD5SUM_SIZE_VS_SPEED > 1 */ + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} + +/* Starting with the result of former calls of this function (or the + * initialization function update the context for the next LEN bytes + * starting at BUFFER. + * It is NOT required that LEN is a multiple of 64. + */ + +static void md5_hash_bytes(const void *buffer, size_t len, struct md5_ctx_t *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy(&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (left_over + add > 64) { + md5_hash_block(ctx->buffer, (left_over + add) & ~63, ctx); + /* The regions in the following copy operation cannot overlap. */ + memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + (left_over + add) & 63); + ctx->buflen = (left_over + add) & 63; + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len > 64) { + md5_hash_block(buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) { + memcpy(ctx->buffer, buffer, len); + ctx->buflen = len; + } +} + +static void md5_hash(const void *buffer, size_t length, void *md5_ctx) +{ + if (length % 64 == 0) { + md5_hash_block(buffer, length, md5_ctx); + } else { + md5_hash_bytes(buffer, length, md5_ctx); + } +} + +/* Process the remaining bytes in the buffer and put result from CTX + * in first 16 bytes following RESBUF. The result is always in little + * endian byte order, so that a byte-wise output yields to the wanted + * ASCII representation of the message digest. + * + * IMPORTANT: On some systems it is required that RESBUF is correctly + * aligned for a 32 bits value. + */ +static void *md5_end(void *resbuf, struct md5_ctx_t *ctx) +{ + /* Take yet unprocessed bytes into account. */ + uint32_t bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; +# if MD5SUM_SIZE_VS_SPEED > 0 + memset(&ctx->buffer[bytes], 0, pad); + ctx->buffer[bytes] = 0x80; +# else + memcpy(&ctx->buffer[bytes], fillbuf, pad); +# endif /* MD5SUM_SIZE_VS_SPEED > 0 */ + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(uint32_t *) & ctx->buffer[bytes + pad] = SWAP(ctx->total[0] << 3); + *(uint32_t *) & ctx->buffer[bytes + pad + 4] = + SWAP(((ctx->total[1] << 3) | (ctx->total[0] >> 29))); + + /* Process last bytes. */ + md5_hash_block(ctx->buffer, bytes + pad + 8, ctx); + + /* Put result from CTX in first 16 bytes following RESBUF. The result is + * always in little endian byte order, so that a byte-wise output yields + * to the wanted ASCII representation of the message digest. + * + * IMPORTANT: On some systems it is required that RESBUF is correctly + * aligned for a 32 bits value. + */ + ((uint32_t *) resbuf)[0] = SWAP(ctx->A); + ((uint32_t *) resbuf)[1] = SWAP(ctx->B); + ((uint32_t *) resbuf)[2] = SWAP(ctx->C); + ((uint32_t *) resbuf)[3] = SWAP(ctx->D); + + return resbuf; +} +#endif /* CONFIG_MD5SUM */ + + + + +extern int hash_fd(int src_fd, const size_t size, const uint8_t hash_algo, + uint8_t * hashval) +{ + int result = EXIT_SUCCESS; +// size_t hashed_count = 0; + size_t blocksize = 0; + size_t remaining = size; + unsigned char *buffer = NULL; + void (*hash_fn_ptr)(const void *, size_t, void *) = NULL; + void *cx = NULL; + +#ifdef CONFIG_SHA1SUM + struct sha1_ctx_t sha1_cx; +#endif +#ifdef CONFIG_MD5SUM + struct md5_ctx_t md5_cx; +#endif + + +#ifdef CONFIG_SHA1SUM + if (hash_algo == HASH_SHA1) { + /* Ensure that BLOCKSIZE is a multiple of 64. */ + blocksize = 65536; + buffer = xmalloc(blocksize); + hash_fn_ptr = sha1_hash; + cx = &sha1_cx; + } +#endif +#ifdef CONFIG_MD5SUM + if (hash_algo == HASH_MD5) { + blocksize = 4096; + buffer = xmalloc(blocksize + 72); + hash_fn_ptr = md5_hash; + cx = &md5_cx; + } +#endif + + /* Initialize the computation context. */ +#ifdef CONFIG_SHA1SUM + if (hash_algo == HASH_SHA1) { + sha1_begin(&sha1_cx); + } +#endif +#ifdef CONFIG_MD5SUM + if (hash_algo == HASH_MD5) { + md5_begin(&md5_cx); + } +#endif + /* Iterate over full file contents. */ + while ((remaining == (size_t) -1) || (remaining > 0)) { + size_t read_try; + ssize_t read_got; + + if (remaining > blocksize) { + read_try = blocksize; + } else { + read_try = remaining; + } + read_got = bb_full_read(src_fd, buffer, read_try); + if (read_got < 1) { + /* count == 0 means short read + * count == -1 means read error */ + result = read_got - 1; + break; + } + if (remaining != (size_t) -1) { + remaining -= read_got; + } + + /* Process buffer */ + hash_fn_ptr(buffer, read_got, cx); + } + + /* Finalize and write the hash into our buffer. */ +#ifdef CONFIG_SHA1SUM + if (hash_algo == HASH_SHA1) { + sha1_end(hashval, &sha1_cx); + } +#endif +#ifdef CONFIG_MD5SUM + if (hash_algo == HASH_MD5) { + md5_end(hashval, &md5_cx); + } +#endif + + free(buffer); + return result; +} diff --git a/busybox/libbb/herror_msg.c b/busybox/libbb/herror_msg.c new file mode 100644 index 000000000..87ec15acc --- /dev/null +++ b/busybox/libbb/herror_msg.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_herror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vherror_msg(s, p); + va_end(p); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/herror_msg_and_die.c b/busybox/libbb/herror_msg_and_die.c new file mode 100644 index 000000000..5c765f1be --- /dev/null +++ b/busybox/libbb/herror_msg_and_die.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_herror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vherror_msg(s, p); + va_end(p); + exit(bb_default_error_retval); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/human_readable.c b/busybox/libbb/human_readable.c new file mode 100644 index 000000000..ad9025c08 --- /dev/null +++ b/busybox/libbb/human_readable.c @@ -0,0 +1,87 @@ +/* + * June 30, 2001 Manuel Novoa III + * + * All-integer version (hey, not everyone has floating point) of + * make_human_readable_str, modified from similar code I had written + * for busybox several months ago. + * + * Notes: + * 1) I'm using an unsigned long long to hold the product size * block_size, + * as df (which calls this routine) could request a representation of a + * partition size in bytes > max of unsigned long. If long longs aren't + * available, it would be possible to do what's needed using polynomial + * representations (say, powers of 1024) and manipulating coefficients. + * The base ten "bytes" output could be handled similarly. + * + * 2) This routine always outputs a decimal point and a tenths digit when + * display_unit != 0. Hence, it isn't uncommon for the returned string + * to have a length of 5 or 6. + * + * It might be nice to add a flag to indicate no decimal digits in + * that case. This could be either an additional parameter, or a + * special value of display_unit. Such a flag would also be nice for du. + * + * Some code to omit the decimal point and tenths digit is sketched out + * and "#if 0"'d below. + */ + +#include +#include "libbb.h" + +const char *make_human_readable_str(unsigned long long size, + unsigned long block_size, unsigned long display_unit) +{ + /* The code will adjust for additional (appended) units. */ + static const char zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' }; + static const char fmt[] = "%Lu"; + static const char fmt_tenths[] = "%Lu.%d%c"; + + static char str[21]; /* Sufficient for 64 bit unsigned integers. */ + + unsigned long long val; + int frac; + const char *u; + const char *f; + + u = zero_and_units; + f = fmt; + frac = 0; + + val = size * block_size; + if (val == 0) { + return u; + } + + if (display_unit) { + val += display_unit/2; /* Deal with rounding. */ + val /= display_unit; /* Don't combine with the line above!!! */ + } else { + ++u; + while ((val >= KILOBYTE) + && (u < zero_and_units + sizeof(zero_and_units) - 1)) { + f = fmt_tenths; + ++u; + frac = ((((int)(val % KILOBYTE)) * 10) + (KILOBYTE/2)) / KILOBYTE; + val /= KILOBYTE; + } + if (frac >= 10) { /* We need to round up here. */ + ++val; + frac = 0; + } +#if 0 + /* Sample code to omit decimal point and tenths digit. */ + if ( /* no_tenths */ 1 ) { + if ( frac >= 5 ) { + ++val; + } + f = "%Lu%*c" /* fmt_no_tenths */ ; + frac = 1; + } +#endif + } + + /* If f==fmt then 'frac' and 'u' are ignored. */ + snprintf(str, sizeof(str), f, val, frac, *u); + + return str; +} diff --git a/busybox/libbb/inet_common.c b/busybox/libbb/inet_common.c new file mode 100644 index 000000000..321322d1f --- /dev/null +++ b/busybox/libbb/inet_common.c @@ -0,0 +1,249 @@ +/* + * stolen from net-tools-1.59 and stripped down for busybox by + * Erik Andersen + * + * Heavily modified by Manuel Novoa III Mar 12, 2001 + * + * Version: $Id: inet_common.c,v 1.8 2004/03/10 07:42:38 mjn3 Exp $ + * + */ + +#include "inet_common.h" +#include +#include +#include +#include +#include +#include "libbb.h" + +#ifdef DEBUG +# include +#endif + + +const char bb_INET_default[] = "default"; + +int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) +{ + struct hostent *hp; + struct netent *np; + + /* Grmpf. -FvK */ + s_in->sin_family = AF_INET; + s_in->sin_port = 0; + + /* Default is special, meaning 0.0.0.0. */ + if (!strcmp(name, bb_INET_default)) { + s_in->sin_addr.s_addr = INADDR_ANY; + return (1); + } + /* Look to see if it's a dotted quad. */ + if (inet_aton(name, &s_in->sin_addr)) { + return 0; + } + /* If we expect this to be a hostname, try hostname database first */ +#ifdef DEBUG + if (hostfirst) { + bb_error_msg("gethostbyname (%s)", name); + } +#endif + if (hostfirst && (hp = gethostbyname(name)) != (struct hostent *) NULL) { + memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0], + sizeof(struct in_addr)); + return 0; + } + /* Try the NETWORKS database to see if this is a known network. */ +#ifdef DEBUG + bb_error_msg("getnetbyname (%s)", name); +#endif + if ((np = getnetbyname(name)) != (struct netent *) NULL) { + s_in->sin_addr.s_addr = htonl(np->n_net); + return 1; + } + if (hostfirst) { + /* Don't try again */ + errno = h_errno; + return -1; + } +#ifdef DEBUG + res_init(); + _res.options |= RES_DEBUG; +#endif + +#ifdef DEBUG + bb_error_msg("gethostbyname (%s)", name); +#endif + if ((hp = gethostbyname(name)) == (struct hostent *) NULL) { + errno = h_errno; + return -1; + } + memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0], + sizeof(struct in_addr)); + + return 0; +} + +/* cache */ +struct addr { + struct sockaddr_in addr; + char *name; + int host; + struct addr *next; +}; + +static struct addr *INET_nn = NULL; /* addr-to-name cache */ + +/* numeric: & 0x8000: default instead of *, + * & 0x4000: host instead of net, + * & 0x0fff: don't resolve + */ +int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in, + int numeric, unsigned int netmask) +{ + struct hostent *ent; + struct netent *np; + struct addr *pn; + unsigned long ad, host_ad; + int host = 0; + + /* Grmpf. -FvK */ + if (s_in->sin_family != AF_INET) { +#ifdef DEBUG + bb_error_msg("rresolve: unsupport address family %d !", + s_in->sin_family); +#endif + errno = EAFNOSUPPORT; + return (-1); + } + ad = (unsigned long) s_in->sin_addr.s_addr; +#ifdef DEBUG + bb_error_msg("rresolve: %08lx, mask %08x, num %08x", ad, netmask, numeric); +#endif + if (ad == INADDR_ANY) { + if ((numeric & 0x0FFF) == 0) { + if (numeric & 0x8000) + safe_strncpy(name, bb_INET_default, len); + else + safe_strncpy(name, "*", len); + return (0); + } + } + if (numeric & 0x0FFF) { + safe_strncpy(name, inet_ntoa(s_in->sin_addr), len); + return (0); + } + + if ((ad & (~netmask)) != 0 || (numeric & 0x4000)) + host = 1; +#if 0 + INET_nn = NULL; +#endif + pn = INET_nn; + while (pn != NULL) { + if (pn->addr.sin_addr.s_addr == ad && pn->host == host) { + safe_strncpy(name, pn->name, len); +#ifdef DEBUG + bb_error_msg("rresolve: found %s %08lx in cache", + (host ? "host" : "net"), ad); +#endif + return (0); + } + pn = pn->next; + } + + host_ad = ntohl(ad); + np = NULL; + ent = NULL; + if (host) { +#ifdef DEBUG + bb_error_msg("gethostbyaddr (%08lx)", ad); +#endif + ent = gethostbyaddr((char *) &ad, 4, AF_INET); + if (ent != NULL) { + safe_strncpy(name, ent->h_name, len); + } + } else { +#ifdef DEBUG + bb_error_msg("getnetbyaddr (%08lx)", host_ad); +#endif + np = getnetbyaddr(host_ad, AF_INET); + if (np != NULL) { + safe_strncpy(name, np->n_name, len); + } + } + if ((ent == NULL) && (np == NULL)) { + safe_strncpy(name, inet_ntoa(s_in->sin_addr), len); + } + pn = (struct addr *) xmalloc(sizeof(struct addr)); + pn->addr = *s_in; + pn->next = INET_nn; + pn->host = host; + pn->name = bb_xstrdup(name); + INET_nn = pn; + + return (0); +} + +#ifdef CONFIG_FEATURE_IPV6 + +int INET6_resolve(const char *name, struct sockaddr_in6 *sin6) +{ + struct addrinfo req, *ai; + int s; + + memset(&req, '\0', sizeof req); + req.ai_family = AF_INET6; + if ((s = getaddrinfo(name, NULL, &req, &ai))) { + bb_error_msg("getaddrinfo: %s: %d", name, s); + return -1; + } + memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6)); + + freeaddrinfo(ai); + + return (0); +} + +#ifndef IN6_IS_ADDR_UNSPECIFIED +# define IN6_IS_ADDR_UNSPECIFIED(a) \ + (((__u32 *) (a))[0] == 0 && ((__u32 *) (a))[1] == 0 && \ + ((__u32 *) (a))[2] == 0 && ((__u32 *) (a))[3] == 0) +#endif + + +int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, + int numeric) +{ + int s; + + /* Grmpf. -FvK */ + if (sin6->sin6_family != AF_INET6) { +#ifdef DEBUG + bb_error_msg(_("rresolve: unsupport address family %d !\n"), + sin6->sin6_family); +#endif + errno = EAFNOSUPPORT; + return (-1); + } + if (numeric & 0x7FFF) { + inet_ntop(AF_INET6, &sin6->sin6_addr, name, len); + return (0); + } + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if (numeric & 0x8000) { + strcpy(name, "default"); + } else { + strcpy(name, "*"); + } + return (0); + } + + s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6), name, len, NULL, 0, 0); + if (s) { + bb_error_msg("getnameinfo failed"); + return -1; + } + return (0); +} + +#endif /* CONFIG_FEATURE_IPV6 */ diff --git a/busybox/libbb/inode_hash.c b/busybox/libbb/inode_hash.c new file mode 100644 index 000000000..fbcd81327 --- /dev/null +++ b/busybox/libbb/inode_hash.c @@ -0,0 +1,111 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#include +#include +#include "libbb.h" + +#define HASH_SIZE 311 /* Should be prime */ +#define hash_inode(i) ((i) % HASH_SIZE) + +typedef struct ino_dev_hash_bucket_struct { + struct ino_dev_hash_bucket_struct *next; + ino_t ino; + dev_t dev; + char name[1]; +} ino_dev_hashtable_bucket_t; + +static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE]; + +/* + * Return 1 if statbuf->st_ino && statbuf->st_dev are recorded in + * `ino_dev_hashtable', else return 0 + * + * If NAME is a non-NULL pointer to a character pointer, and there is + * a match, then set *NAME to the value of the name slot in that + * bucket. + */ +int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name) +{ + ino_dev_hashtable_bucket_t *bucket; + + bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)]; + while (bucket != NULL) { + if ((bucket->ino == statbuf->st_ino) && + (bucket->dev == statbuf->st_dev)) + { + if (name) *name = bucket->name; + return 1; + } + bucket = bucket->next; + } + return 0; +} + +/* Add statbuf to statbuf hash table */ +void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) +{ + int i; + size_t s; + ino_dev_hashtable_bucket_t *bucket; + + i = hash_inode(statbuf->st_ino); + s = name ? strlen(name) : 0; + bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + s); + bucket->ino = statbuf->st_ino; + bucket->dev = statbuf->st_dev; + if (name) + strcpy(bucket->name, name); + else + bucket->name[0] = '\0'; + bucket->next = ino_dev_hashtable[i]; + ino_dev_hashtable[i] = bucket; +} + +#ifdef CONFIG_FEATURE_CLEAN_UP +/* Clear statbuf hash table */ +void reset_ino_dev_hashtable(void) +{ + int i; + ino_dev_hashtable_bucket_t *bucket; + + for (i = 0; i < HASH_SIZE; i++) { + while (ino_dev_hashtable[i] != NULL) { + bucket = ino_dev_hashtable[i]->next; + free(ino_dev_hashtable[i]); + ino_dev_hashtable[i] = bucket; + } + } +} +#endif + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/interface.c b/busybox/libbb/interface.c new file mode 100644 index 000000000..fe2d0b4b2 --- /dev/null +++ b/busybox/libbb/interface.c @@ -0,0 +1,2083 @@ +/* + * 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. + * + * ----------------------------------------------------------- + * + * ifconfig This file contains an implementation of the command + * that either displays or sets the characteristics of + * one or more of the system's networking interfaces. + * + * Version: $Id: interface.c,v 1.25 2004/08/26 21:45:21 andersen Exp $ + * + * Author: Fred N. van Kempen, + * 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) + */ + +/* #define KEEP_UNUSED */ + +/* + * + * Protocol Families. + * + */ +#define HAVE_AFINET 1 +#undef HAVE_AFIPX +#undef HAVE_AFATALK +#undef HAVE_AFNETROM +#undef HAVE_AFX25 +#undef HAVE_AFECONET +#undef HAVE_AFASH + +/* + * + * Device Hardware types. + * + */ +#define HAVE_HWETHER 1 +#define HAVE_HWPPP 1 +#undef HAVE_HWSLIP + + +#include "inet_common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libbb.h" + +#ifdef CONFIG_FEATURE_IPV6 +# define HAVE_AFINET6 1 +#else +# undef HAVE_AFINET6 +#endif + +#define _(x) x +#define _PATH_PROCNET_DEV "/proc/net/dev" +#define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6" +#define new(p) ((p) = xcalloc(1,sizeof(*(p)))) +#define KRELEASE(maj,min,patch) ((maj) * 65536 + (min)*256 + (patch)) + +#ifdef HAVE_HWSLIP +#include +#endif + +#if HAVE_AFINET6 + +#ifndef _LINUX_IN6_H +/* + * This is in linux/include/net/ipv6.h. + */ + +struct in6_ifreq { + struct in6_addr ifr6_addr; + uint32_t ifr6_prefixlen; + unsigned int ifr6_ifindex; +}; + +#endif + +#endif /* HAVE_AFINET6 */ + +#if HAVE_AFIPX +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) +#include +#else +#include "ipx.h" +#endif +#endif + +/* Defines for glibc2.0 users. */ +#ifndef SIOCSIFTXQLEN +#define SIOCSIFTXQLEN 0x8943 +#define SIOCGIFTXQLEN 0x8942 +#endif + +/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */ +#ifndef ifr_qlen +#define ifr_qlen ifr_ifru.ifru_mtu +#endif + +#ifndef HAVE_TXQUEUELEN +#define HAVE_TXQUEUELEN 1 +#endif + +#ifndef IFF_DYNAMIC +#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */ +#endif + +/* This structure defines protocol families and their handlers. */ +struct aftype { + const char *name; + const char *title; + int af; + int alen; + char *(*print) (unsigned char *); + char *(*sprint) (struct sockaddr *, int numeric); + int (*input) (int type, char *bufp, struct sockaddr *); + void (*herror) (char *text); + int (*rprint) (int options); + int (*rinput) (int typ, int ext, char **argv); + + /* may modify src */ + int (*getmask) (char *src, struct sockaddr * mask, char *name); + + int fd; + char *flag_file; +}; + +#ifdef KEEP_UNUSED + +static int flag_unx; + +#ifdef HAVE_AFIPX +static int flag_ipx; +#endif +#ifdef HAVE_AFX25 +static int flag_ax25; +#endif +#ifdef HAVE_AFATALK +static int flag_ddp; +#endif +#ifdef HAVE_AFNETROM +static int flag_netrom; +#endif +static int flag_inet; + +#ifdef HAVE_AFINET6 +static int flag_inet6; +#endif +#ifdef HAVE_AFECONET +static int flag_econet; +#endif +#ifdef HAVE_AFX25 +static int flag_x25 = 0; +#endif +#ifdef HAVE_AFASH +static int flag_ash; +#endif + + +static struct aftrans_t { + char *alias; + char *name; + int *flag; +} aftrans[] = { + +#ifdef HAVE_AFX25 + { + "ax25", "ax25", &flag_ax25}, +#endif + { + "ip", "inet", &flag_inet}, +#ifdef HAVE_AFINET6 + { + "ip6", "inet6", &flag_inet6}, +#endif +#ifdef HAVE_AFIPX + { + "ipx", "ipx", &flag_ipx}, +#endif +#ifdef HAVE_AFATALK + { + "appletalk", "ddp", &flag_ddp}, +#endif +#ifdef HAVE_AFNETROM + { + "netrom", "netrom", &flag_netrom}, +#endif + { + "inet", "inet", &flag_inet}, +#ifdef HAVE_AFINET6 + { + "inet6", "inet6", &flag_inet6}, +#endif +#ifdef HAVE_AFATALK + { + "ddp", "ddp", &flag_ddp}, +#endif + { + "unix", "unix", &flag_unx}, { + "tcpip", "inet", &flag_inet}, +#ifdef HAVE_AFECONET + { + "econet", "ec", &flag_econet}, +#endif +#ifdef HAVE_AFX25 + { + "x25", "x25", &flag_x25}, +#endif +#ifdef HAVE_AFASH + { + "ash", "ash", &flag_ash}, +#endif + { + 0, 0, 0} +}; + +static char afname[256] = ""; +#endif /* KEEP_UNUSED */ + +#if HAVE_AFUNIX + +/* Display a UNIX domain address. */ +static char *UNIX_print(unsigned char *ptr) +{ + return (ptr); +} + + +/* Display a UNIX domain address. */ +static char *UNIX_sprint(struct sockaddr *sap, int numeric) +{ + static char buf[64]; + + if (sap->sa_family == 0xFFFF || sap->sa_family == 0) + return safe_strncpy(buf, _("[NONE SET]"), sizeof(buf)); + return (UNIX_print(sap->sa_data)); +} + + +static struct aftype unix_aftype = { + "unix", "UNIX Domain", AF_UNIX, 0, + UNIX_print, UNIX_sprint, NULL, NULL, + NULL, NULL, NULL, + -1, + "/proc/net/unix" +}; +#endif /* HAVE_AFUNIX */ + +#if HAVE_AFINET + +#ifdef KEEP_UNUSED +static void INET_reserror(char *text) +{ + herror(text); +} + +/* Display an Internet socket address. */ +static char *INET_print(unsigned char *ptr) +{ + return (inet_ntoa((*(struct in_addr *) ptr))); +} +#endif /* KEEP_UNUSED */ + +/* Display an Internet socket address. */ +static char *INET_sprint(struct sockaddr *sap, int numeric) +{ + static char buff[128]; + + if (sap->sa_family == 0xFFFF || sap->sa_family == 0) + return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff)); + + if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap, + numeric, 0xffffff00) != 0) + return (NULL); + + return (buff); +} + +#ifdef KEEP_UNUSED +static char *INET_sprintmask(struct sockaddr *sap, int numeric, + unsigned int netmask) +{ + static char buff[128]; + + if (sap->sa_family == 0xFFFF || sap->sa_family == 0) + return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff)); + if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap, + numeric, netmask) != 0) + return (NULL); + return (buff); +} + +static int INET_getsock(char *bufp, struct sockaddr *sap) +{ + char *sp = bufp, *bp; + unsigned int i; + unsigned val; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) sap; + sin->sin_family = AF_INET; + sin->sin_port = 0; + + val = 0; + bp = (char *) &val; + for (i = 0; i < sizeof(sin->sin_addr.s_addr); i++) { + *sp = toupper(*sp); + + if ((*sp >= 'A') && (*sp <= 'F')) + bp[i] |= (int) (*sp - 'A') + 10; + else if ((*sp >= '0') && (*sp <= '9')) + bp[i] |= (int) (*sp - '0'); + else + return (-1); + + bp[i] <<= 4; + sp++; + *sp = toupper(*sp); + + if ((*sp >= 'A') && (*sp <= 'F')) + bp[i] |= (int) (*sp - 'A') + 10; + else if ((*sp >= '0') && (*sp <= '9')) + bp[i] |= (int) (*sp - '0'); + else + return (-1); + + sp++; + } + sin->sin_addr.s_addr = htonl(val); + + return (sp - bufp); +} + +static int INET_input(int type, char *bufp, struct sockaddr *sap) +{ + switch (type) { + case 1: + return (INET_getsock(bufp, sap)); + case 256: + return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1)); + default: + return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0)); + } +} + +static int INET_getnetmask(char *adr, struct sockaddr *m, char *name) +{ + struct sockaddr_in *mask = (struct sockaddr_in *) m; + char *slash, *end; + int prefix; + + if ((slash = strchr(adr, '/')) == NULL) + return 0; + + *slash++ = '\0'; + prefix = strtoul(slash, &end, 0); + if (*end != '\0') + return -1; + + if (name) { + sprintf(name, "/%d", prefix); + } + mask->sin_family = AF_INET; + mask->sin_addr.s_addr = htonl(~(0xffffffffU >> prefix)); + return 1; +} +#endif /* KEEP_UNUSED */ + +static struct aftype inet_aftype = { + "inet", "DARPA Internet", AF_INET, sizeof(unsigned long), + NULL /* UNUSED INET_print */ , INET_sprint, + NULL /* UNUSED INET_input */ , NULL /* UNUSED INET_reserror */ , + NULL /*INET_rprint */ , NULL /*INET_rinput */ , + NULL /* UNUSED INET_getnetmask */ , + -1, + NULL +}; + +#endif /* HAVE_AFINET */ + +#if HAVE_AFINET6 + +#ifdef KEEP_UNUSED +static void INET6_reserror(char *text) +{ + herror(text); +} + +/* Display an Internet socket address. */ +static char *INET6_print(unsigned char *ptr) +{ + static char name[80]; + + inet_ntop(AF_INET6, (struct in6_addr *) ptr, name, 80); + return name; +} +#endif /* KEEP_UNUSED */ + +/* Display an Internet socket address. */ +/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */ +static char *INET6_sprint(struct sockaddr *sap, int numeric) +{ + static char buff[128]; + + if (sap->sa_family == 0xFFFF || sap->sa_family == 0) + return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff)); + if (INET6_rresolve + (buff, sizeof(buff), (struct sockaddr_in6 *) sap, numeric) != 0) + return safe_strncpy(buff, _("[UNKNOWN]"), sizeof(buff)); + return (buff); +} + +#ifdef KEEP_UNUSED +static int INET6_getsock(char *bufp, struct sockaddr *sap) +{ + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *) sap; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + + if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0) + return (-1); + + return 16; /* ?;) */ +} + +static int INET6_input(int type, char *bufp, struct sockaddr *sap) +{ + switch (type) { + case 1: + return (INET6_getsock(bufp, sap)); + default: + return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap)); + } +} +#endif /* KEEP_UNUSED */ + +static struct aftype inet6_aftype = { + "inet6", "IPv6", AF_INET6, sizeof(struct in6_addr), + NULL /* UNUSED INET6_print */ , INET6_sprint, + NULL /* UNUSED INET6_input */ , NULL /* UNUSED INET6_reserror */ , + NULL /*INET6_rprint */ , NULL /*INET6_rinput */ , + NULL /* UNUSED INET6_getnetmask */ , + -1, + NULL +}; + +#endif /* HAVE_AFINET6 */ + +/* Display an UNSPEC address. */ +static char *UNSPEC_print(unsigned char *ptr) +{ + static char buff[sizeof(struct sockaddr) * 3 + 1]; + char *pos; + unsigned int i; + + pos = buff; + for (i = 0; i < sizeof(struct sockaddr); i++) { + /* careful -- not every libc's sprintf returns # bytes written */ + sprintf(pos, "%02X-", (*ptr++ & 0377)); + pos += 3; + } + /* Erase trailing "-". Works as long as sizeof(struct sockaddr) != 0 */ + *--pos = '\0'; + return (buff); +} + +/* Display an UNSPEC socket address. */ +static char *UNSPEC_sprint(struct sockaddr *sap, int numeric) +{ + static char buf[64]; + + if (sap->sa_family == 0xFFFF || sap->sa_family == 0) + return safe_strncpy(buf, _("[NONE SET]"), sizeof(buf)); + return (UNSPEC_print(sap->sa_data)); +} + +static struct aftype unspec_aftype = { + "unspec", "UNSPEC", AF_UNSPEC, 0, + UNSPEC_print, UNSPEC_sprint, NULL, NULL, + NULL, +}; + +static struct aftype *aftypes[] = { +#if HAVE_AFUNIX + &unix_aftype, +#endif +#if HAVE_AFINET + &inet_aftype, +#endif +#if HAVE_AFINET6 + &inet6_aftype, +#endif +#if HAVE_AFAX25 + &ax25_aftype, +#endif +#if HAVE_AFNETROM + &netrom_aftype, +#endif +#if HAVE_AFROSE + &rose_aftype, +#endif +#if HAVE_AFIPX + &ipx_aftype, +#endif +#if HAVE_AFATALK + &ddp_aftype, +#endif +#if HAVE_AFECONET + &ec_aftype, +#endif +#if HAVE_AFASH + &ash_aftype, +#endif +#if HAVE_AFX25 + &x25_aftype, +#endif + &unspec_aftype, + NULL +}; + +#ifdef KEEP_UNUSED +static short sVafinit = 0; + +static void afinit() +{ + unspec_aftype.title = _("UNSPEC"); +#if HAVE_AFUNIX + unix_aftype.title = _("UNIX Domain"); +#endif +#if HAVE_AFINET + inet_aftype.title = _("DARPA Internet"); +#endif +#if HAVE_AFINET6 + inet6_aftype.title = _("IPv6"); +#endif +#if HAVE_AFAX25 + ax25_aftype.title = _("AMPR AX.25"); +#endif +#if HAVE_AFNETROM + netrom_aftype.title = _("AMPR NET/ROM"); +#endif +#if HAVE_AFIPX + ipx_aftype.title = _("Novell IPX"); +#endif +#if HAVE_AFATALK + ddp_aftype.title = _("Appletalk DDP"); +#endif +#if HAVE_AFECONET + ec_aftype.title = _("Econet"); +#endif +#if HAVE_AFX25 + x25_aftype.title = _("CCITT X.25"); +#endif +#if HAVE_AFROSE + rose_aftype.title = _("AMPR ROSE"); +#endif +#if HAVE_AFASH + ash_aftype.title = _("Ash"); +#endif + sVafinit = 1; +} + +static int aftrans_opt(const char *arg) +{ + struct aftrans_t *paft; + char *tmp1, *tmp2; + char buf[256]; + + safe_strncpy(buf, arg, sizeof(buf)); + + tmp1 = buf; + + while (tmp1) { + + tmp2 = strchr(tmp1, ','); + + if (tmp2) + *(tmp2++) = '\0'; + + paft = aftrans; + for (paft = aftrans; paft->alias; paft++) { + if (strcmp(tmp1, paft->alias)) + continue; + if (strlen(paft->name) + strlen(afname) + 1 >= sizeof(afname)) { + bb_error_msg(_("Too many address family arguments.")); + return (0); + } + if (paft->flag) + (*paft->flag)++; + if (afname[0]) + strcat(afname, ","); + strcat(afname, paft->name); + break; + } + if (!paft->alias) { + bb_error_msg(_("Unknown address family `%s'."), tmp1); + return (1); + } + tmp1 = tmp2; + } + + return (0); +} + +/* set the default AF list from the program name or a constant value */ +static void aftrans_def(char *tool, char *argv0, char *dflt) +{ + char *tmp; + char *buf; + + strcpy(afname, dflt); + + if (!(tmp = strrchr(argv0, '/'))) + tmp = argv0; /* no slash?! */ + else + tmp++; + + if (!(buf = strdup(tmp))) + return; + + if (strlen(tool) >= strlen(tmp)) { + free(buf); + return; + } + tmp = buf + (strlen(tmp) - strlen(tool)); + + if (strcmp(tmp, tool) != 0) { + free(buf); + return; + } + *tmp = '\0'; + if ((tmp = strchr(buf, '_'))) + *tmp = '\0'; + + afname[0] = '\0'; + if (aftrans_opt(buf)) + strcpy(afname, buf); + + free(buf); +} + +/* Check our protocol family table for this family. */ +static struct aftype *get_aftype(const char *name) +{ + struct aftype **afp; + +#ifdef KEEP_UNUSED + if (!sVafinit) + afinit(); +#endif /* KEEP_UNUSED */ + + afp = aftypes; + while (*afp != NULL) { + if (!strcmp((*afp)->name, name)) + return (*afp); + afp++; + } + if (strchr(name, ',')) + bb_error_msg(_("Please don't supply more than one address family.")); + return (NULL); +} +#endif /* KEEP_UNUSED */ + +/* Check our protocol family table for this family. */ +static struct aftype *get_afntype(int af) +{ + struct aftype **afp; + +#ifdef KEEP_UNUSED + if (!sVafinit) + afinit(); +#endif /* KEEP_UNUSED */ + + afp = aftypes; + while (*afp != NULL) { + if ((*afp)->af == af) + return (*afp); + afp++; + } + return (NULL); +} + +/* Check our protocol family table for this family and return its socket */ +static int get_socket_for_af(int af) +{ + struct aftype **afp; + +#ifdef KEEP_UNUSED + if (!sVafinit) + afinit(); +#endif /* KEEP_UNUSED */ + + afp = aftypes; + while (*afp != NULL) { + if ((*afp)->af == af) + return (*afp)->fd; + afp++; + } + return -1; +} + +#ifdef KEEP_UNUSED +/* type: 0=all, 1=getroute */ +static void print_aflist(int type) +{ + int count = 0; + char *txt; + struct aftype **afp; + +#ifdef KEEP_UNUSED + if (!sVafinit) + afinit(); +#endif /* KEEP_UNUSED */ + + afp = aftypes; + while (*afp != NULL) { + if ((type == 1 && ((*afp)->rprint == NULL)) || ((*afp)->af == 0)) { + afp++; + continue; + } + if ((count % 3) == 0) + fprintf(stderr, count ? "\n " : " "); + txt = (*afp)->name; + if (!txt) + txt = ".."; + fprintf(stderr, "%s (%s) ", txt, _((*afp)->title)); + count++; + afp++; + } + fprintf(stderr, "\n"); +} +#endif /* KEEP_UNUSED */ + +struct user_net_device_stats { + unsigned long long rx_packets; /* total packets received */ + unsigned long long tx_packets; /* total packets transmitted */ + unsigned long long rx_bytes; /* total bytes received */ + unsigned long long tx_bytes; /* total bytes transmitted */ + unsigned long rx_errors; /* bad packets received */ + unsigned long tx_errors; /* packet transmit problems */ + unsigned long rx_dropped; /* no space in linux buffers */ + unsigned long tx_dropped; /* no space available in linux */ + unsigned long rx_multicast; /* multicast packets received */ + unsigned long rx_compressed; + unsigned long tx_compressed; + unsigned long collisions; + + /* detailed rx_errors: */ + unsigned long rx_length_errors; + unsigned long rx_over_errors; /* receiver ring buff overflow */ + unsigned long rx_crc_errors; /* recved pkt with crc error */ + unsigned long rx_frame_errors; /* recv'd frame alignment error */ + unsigned long rx_fifo_errors; /* recv'r fifo overrun */ + unsigned long rx_missed_errors; /* receiver missed packet */ + /* detailed tx_errors */ + unsigned long tx_aborted_errors; + unsigned long tx_carrier_errors; + unsigned long tx_fifo_errors; + unsigned long tx_heartbeat_errors; + unsigned long tx_window_errors; +}; + +struct interface { + struct interface *next, *prev; + char name[IFNAMSIZ]; /* interface name */ + short type; /* if type */ + short flags; /* various flags */ + int metric; /* routing metric */ + int mtu; /* MTU value */ + int tx_queue_len; /* transmit queue length */ + struct ifmap map; /* hardware setup */ + struct sockaddr addr; /* IP address */ + struct sockaddr dstaddr; /* P-P IP address */ + struct sockaddr broadaddr; /* IP broadcast address */ + struct sockaddr netmask; /* IP network mask */ + struct sockaddr ipxaddr_bb; /* IPX network address */ + struct sockaddr ipxaddr_sn; /* IPX network address */ + struct sockaddr ipxaddr_e3; /* IPX network address */ + struct sockaddr ipxaddr_e2; /* IPX network address */ + struct sockaddr ddpaddr; /* Appletalk DDP address */ + struct sockaddr ecaddr; /* Econet address */ + int has_ip; + int has_ipx_bb; + int has_ipx_sn; + int has_ipx_e3; + int has_ipx_e2; + int has_ax25; + int has_ddp; + int has_econet; + char hwaddr[32]; /* HW address */ + int statistics_valid; + struct user_net_device_stats stats; /* statistics */ + int keepalive; /* keepalive value for SLIP */ + int outfill; /* outfill value for SLIP */ +}; + + +int interface_opt_a = 0; /* show all interfaces */ + +#ifdef KEEP_UNUSED +static int opt_i = 0; /* show the statistics */ +static int opt_v = 0; /* debugging output flag */ + +static int addr_family = 0; /* currently selected AF */ +#endif /* KEEP_UNUSED */ + +static struct interface *int_list, *int_last; +static int skfd = -1; /* generic raw socket desc. */ + + +static int sockets_open(int family) +{ + struct aftype **aft; + int sfd = -1; + static int force = -1; + + if (force < 0) { + force = 0; + if (get_kernel_revision() < KRELEASE(2, 1, 0)) + force = 1; + if (access("/proc/net", R_OK)) + force = 1; + } + for (aft = aftypes; *aft; aft++) { + struct aftype *af = *aft; + int type = SOCK_DGRAM; + + if (af->af == AF_UNSPEC) + continue; + if (family && family != af->af) + continue; + if (af->fd != -1) { + sfd = af->fd; + continue; + } + /* Check some /proc file first to not stress kmod */ + if (!family && !force && af->flag_file) { + if (access(af->flag_file, R_OK)) + continue; + } +#if HAVE_AFNETROM + if (af->af == AF_NETROM) + type = SOCK_SEQPACKET; +#endif +#if HAVE_AFX25 + if (af->af == AF_X25) + type = SOCK_SEQPACKET; +#endif + af->fd = socket(af->af, type, 0); + if (af->fd >= 0) + sfd = af->fd; + } + if (sfd < 0) { + bb_error_msg(_("No usable address families found.")); + } + return sfd; +} + +/* like strcmp(), but knows about numbers */ +static int nstrcmp(const char *a, const char *b) +{ + const char *a_ptr = a; + const char *b_ptr = b; + + while (*a == *b) { + if (*a == '\0') { + return 0; + } + if (!isdigit(*a) && isdigit(*(a+1))) { + a_ptr = a+1; + b_ptr = b+1; + } + a++; + b++; + } + + if (isdigit(*a) && isdigit(*b)) { + return atoi(a_ptr) > atoi(b_ptr) ? 1 : -1; + } + return *a - *b; +} + +static struct interface *add_interface(char *name) +{ + struct interface *ife, **nextp, *new; + + for (ife = int_last; ife; ife = ife->prev) { + int n = nstrcmp(ife->name, name); + + if (n == 0) + return ife; + if (n < 0) + break; + } + new(new); + safe_strncpy(new->name, name, IFNAMSIZ); + nextp = ife ? &ife->next : &int_list; + new->prev = ife; + new->next = *nextp; + if (new->next) + new->next->prev = new; + else + int_last = new; + *nextp = new; + return new; +} + + +static int if_readconf(void) +{ + int numreqs = 30; + struct ifconf ifc; + struct ifreq *ifr; + int n, err = -1; + int skfd2; + + /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets + (as of 2.1.128) */ + skfd2 = get_socket_for_af(AF_INET); + if (skfd2 < 0) { + bb_perror_msg(("warning: no inet socket available")); + /* Try to soldier on with whatever socket we can get hold of. */ + skfd2 = sockets_open(0); + if (skfd2 < 0) + return -1; + } + + ifc.ifc_buf = NULL; + for (;;) { + ifc.ifc_len = sizeof(struct ifreq) * numreqs; + ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len); + + if (ioctl(skfd2, SIOCGIFCONF, &ifc) < 0) { + perror("SIOCGIFCONF"); + goto out; + } + if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) { + /* assume it overflowed and try again */ + numreqs += 10; + continue; + } + break; + } + + ifr = ifc.ifc_req; + for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) { + add_interface(ifr->ifr_name); + ifr++; + } + err = 0; + + out: + free(ifc.ifc_buf); + return err; +} + +char *get_name(char *name, char *p) +{ + /* Extract [:] from nul-terminated p where p matches + [:]: after leading whitespace. + If match is not made, set name empty and return unchanged p */ + int namestart=0, nameend=0, aliasend; + while (isspace(p[namestart])) + namestart++; + nameend=namestart; + while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend])) + nameend++; + if (p[nameend]==':') { + aliasend=nameend+1; + while (p[aliasend] && isdigit(p[aliasend])) + aliasend++; + if (p[aliasend]==':') { + nameend=aliasend; + } + if ((nameend-namestart)stats, 0, sizeof(struct user_net_device_stats)); + + sscanf(bp, ss_fmt[procnetdev_vsn], + &ife->stats.rx_bytes, /* missing for 0 */ + &ife->stats.rx_packets, + &ife->stats.rx_errors, + &ife->stats.rx_dropped, + &ife->stats.rx_fifo_errors, + &ife->stats.rx_frame_errors, + &ife->stats.rx_compressed, /* missing for <= 1 */ + &ife->stats.rx_multicast, /* missing for <= 1 */ + &ife->stats.tx_bytes, /* missing for 0 */ + &ife->stats.tx_packets, + &ife->stats.tx_errors, + &ife->stats.tx_dropped, + &ife->stats.tx_fifo_errors, + &ife->stats.collisions, + &ife->stats.tx_carrier_errors, + &ife->stats.tx_compressed /* missing for <= 1 */ + ); + + if (procnetdev_vsn <= 1) { + if (procnetdev_vsn == 0) { + ife->stats.rx_bytes = 0; + ife->stats.tx_bytes = 0; + } + ife->stats.rx_multicast = 0; + ife->stats.rx_compressed = 0; + ife->stats.tx_compressed = 0; + } +} + +static inline int procnetdev_version(char *buf) +{ + if (strstr(buf, "compressed")) + return 2; + if (strstr(buf, "bytes")) + return 1; + return 0; +} + +static int if_readlist_proc(char *target) +{ + static int proc_read; + FILE *fh; + char buf[512]; + struct interface *ife; + int err, procnetdev_vsn; + + if (proc_read) + return 0; + if (!target) + proc_read = 1; + + fh = fopen(_PATH_PROCNET_DEV, "r"); + if (!fh) { + bb_perror_msg(_("Warning: cannot open %s. Limited output."), _PATH_PROCNET_DEV); + return if_readconf(); + } + fgets(buf, sizeof buf, fh); /* eat line */ + fgets(buf, sizeof buf, fh); + + procnetdev_vsn = procnetdev_version(buf); + + err = 0; + while (fgets(buf, sizeof buf, fh)) { + char *s, name[128]; + + s = get_name(name, buf); + ife = add_interface(name); + get_dev_fields(s, ife, procnetdev_vsn); + ife->statistics_valid = 1; + if (target && !strcmp(target, name)) + break; + } + if (ferror(fh)) { + perror(_PATH_PROCNET_DEV); + err = -1; + proc_read = 0; + } + fclose(fh); + return err; +} + +static int if_readlist(void) +{ + int err = if_readlist_proc(NULL); + + if (!err) + err = if_readconf(); + return err; +} + +static int for_all_interfaces(int (*doit) (struct interface *, void *), + void *cookie) +{ + struct interface *ife; + + if (!int_list && (if_readlist() < 0)) + return -1; + for (ife = int_list; ife; ife = ife->next) { + int err = doit(ife, cookie); + + if (err) + return err; + } + return 0; +} + +/* Support for fetching an IPX address */ + +#if HAVE_AFIPX +static int ipx_getaddr(int sock, int ft, struct ifreq *ifr) +{ + ((struct sockaddr_ipx *) &ifr->ifr_addr)->sipx_type = ft; + return ioctl(sock, SIOCGIFADDR, ifr); +} +#endif + + +/* Fetch the interface configuration from the kernel. */ +static int if_fetch(struct interface *ife) +{ + struct ifreq ifr; + int fd; + char *ifname = ife->name; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) + return (-1); + ife->flags = ifr.ifr_flags; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) + memset(ife->hwaddr, 0, 32); + else + memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8); + + ife->type = ifr.ifr_hwaddr.sa_family; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0) + ife->metric = 0; + else + ife->metric = ifr.ifr_metric; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) + ife->mtu = 0; + else + ife->mtu = ifr.ifr_mtu; + +#ifdef HAVE_HWSLIP + if (ife->type == ARPHRD_SLIP || ife->type == ARPHRD_CSLIP || + ife->type == ARPHRD_SLIP6 || ife->type == ARPHRD_CSLIP6 || + ife->type == ARPHRD_ADAPT) { +#ifdef SIOCGOUTFILL + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGOUTFILL, &ifr) < 0) + ife->outfill = 0; + else + ife->outfill = (unsigned int) ifr.ifr_data; +#endif +#ifdef SIOCGKEEPALIVE + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGKEEPALIVE, &ifr) < 0) + ife->keepalive = 0; + else + ife->keepalive = (unsigned int) ifr.ifr_data; +#endif + } +#endif + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0) + memset(&ife->map, 0, sizeof(struct ifmap)); + else + memcpy(&ife->map, &ifr.ifr_map, sizeof(struct ifmap)); + + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0) + memset(&ife->map, 0, sizeof(struct ifmap)); + else + ife->map = ifr.ifr_map; + +#ifdef HAVE_TXQUEUELEN + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) < 0) + ife->tx_queue_len = -1; /* unknown value */ + else + ife->tx_queue_len = ifr.ifr_qlen; +#else + ife->tx_queue_len = -1; /* unknown value */ +#endif + +#if HAVE_AFINET + /* IPv4 address? */ + fd = get_socket_for_af(AF_INET); + if (fd >= 0) { + strcpy(ifr.ifr_name, ifname); + ifr.ifr_addr.sa_family = AF_INET; + if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { + ife->has_ip = 1; + ife->addr = ifr.ifr_addr; + strcpy(ifr.ifr_name, ifname); + if (ioctl(fd, SIOCGIFDSTADDR, &ifr) < 0) + memset(&ife->dstaddr, 0, sizeof(struct sockaddr)); + else + ife->dstaddr = ifr.ifr_dstaddr; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0) + memset(&ife->broadaddr, 0, sizeof(struct sockaddr)); + else + ife->broadaddr = ifr.ifr_broadaddr; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0) + memset(&ife->netmask, 0, sizeof(struct sockaddr)); + else + ife->netmask = ifr.ifr_netmask; + } else + memset(&ife->addr, 0, sizeof(struct sockaddr)); + } +#endif + +#if HAVE_AFATALK + /* DDP address maybe ? */ + fd = get_socket_for_af(AF_APPLETALK); + if (fd >= 0) { + strcpy(ifr.ifr_name, ifname); + if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { + ife->ddpaddr = ifr.ifr_addr; + ife->has_ddp = 1; + } + } +#endif + +#if HAVE_AFIPX + /* Look for IPX addresses with all framing types */ + fd = get_socket_for_af(AF_IPX); + if (fd >= 0) { + strcpy(ifr.ifr_name, ifname); + if (!ipx_getaddr(fd, IPX_FRAME_ETHERII, &ifr)) { + ife->has_ipx_bb = 1; + ife->ipxaddr_bb = ifr.ifr_addr; + } + strcpy(ifr.ifr_name, ifname); + if (!ipx_getaddr(fd, IPX_FRAME_SNAP, &ifr)) { + ife->has_ipx_sn = 1; + ife->ipxaddr_sn = ifr.ifr_addr; + } + strcpy(ifr.ifr_name, ifname); + if (!ipx_getaddr(fd, IPX_FRAME_8023, &ifr)) { + ife->has_ipx_e3 = 1; + ife->ipxaddr_e3 = ifr.ifr_addr; + } + strcpy(ifr.ifr_name, ifname); + if (!ipx_getaddr(fd, IPX_FRAME_8022, &ifr)) { + ife->has_ipx_e2 = 1; + ife->ipxaddr_e2 = ifr.ifr_addr; + } + } +#endif + +#if HAVE_AFECONET + /* Econet address maybe? */ + fd = get_socket_for_af(AF_ECONET); + if (fd >= 0) { + strcpy(ifr.ifr_name, ifname); + if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { + ife->ecaddr = ifr.ifr_addr; + ife->has_econet = 1; + } + } +#endif + + return 0; +} + + +static int do_if_fetch(struct interface *ife) +{ + if (if_fetch(ife) < 0) { + char *errmsg; + + if (errno == ENODEV) { + /* Give better error message for this case. */ + errmsg = _("Device not found"); + } else { + errmsg = strerror(errno); + } + bb_error_msg(_("%s: error fetching interface information: %s\n"), + ife->name, errmsg); + return -1; + } + return 0; +} + +/* This structure defines hardware protocols and their handlers. */ +struct hwtype { + const char *name; + const char *title; + int type; + int alen; + char *(*print) (unsigned char *); + int (*input) (char *, struct sockaddr *); + int (*activate) (int fd); + int suppress_null_addr; +}; + +static struct hwtype unspec_hwtype = { + "unspec", "UNSPEC", -1, 0, + UNSPEC_print, NULL, NULL +}; + +static struct hwtype loop_hwtype = { + "loop", "Local Loopback", ARPHRD_LOOPBACK, 0, + NULL, NULL, NULL +}; + +#if HAVE_HWETHER +#include + +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#else +#include +#endif + +/* Display an Ethernet address in readable format. */ +static char *pr_ether(unsigned char *ptr) +{ + static char buff[64]; + + snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X", + (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377), + (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377) + ); + return (buff); +} + +#ifdef KEEP_UNUSED +/* Input an Ethernet address and convert to binary. */ +static int in_ether(char *bufp, struct sockaddr *sap) +{ + unsigned char *ptr; + char c, *orig; + int i; + unsigned val; + + sap->sa_family = ether_hwtype.type; + ptr = sap->sa_data; + + i = 0; + orig = bufp; + while ((*bufp != '\0') && (i < ETH_ALEN)) { + val = 0; + c = *bufp++; + if (isdigit(c)) + val = c - '0'; + else if (c >= 'a' && c <= 'f') + val = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = c - 'A' + 10; + else { +#ifdef DEBUG + bb_error_msg(_("in_ether(%s): invalid ether address!\n"), orig); +#endif + errno = EINVAL; + return (-1); + } + val <<= 4; + c = *bufp; + if (isdigit(c)) + val |= c - '0'; + else if (c >= 'a' && c <= 'f') + val |= c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val |= c - 'A' + 10; + else if (c == ':' || c == 0) + val >>= 4; + else { +#ifdef DEBUG + bb_error_msg(_("in_ether(%s): invalid ether address!"), orig); +#endif + errno = EINVAL; + return (-1); + } + if (c != 0) + bufp++; + *ptr++ = (unsigned char) (val & 0377); + i++; + + /* We might get a semicolon here - not required. */ + if (*bufp == ':') { +#ifdef DEBUG + if (i == ETH_ALEN) { + bb_error_msg(_("in_ether(%s): trailing : ignored!"), orig); + } +#endif + bufp++; + } + } + +#ifdef DEBUG + /* That's it. Any trailing junk? */ + if ((i == ETH_ALEN) && (*bufp != '\0')) { + bb_error_msg(_("in_ether(%s): trailing junk!"), orig); + errno = EINVAL; + return (-1); + } + bb_error_msg("in_ether(%s): %s", orig, pr_ether(sap->sa_data)); +#endif + + return (0); +} +#endif /* KEEP_UNUSED */ + + +static struct hwtype ether_hwtype = { + "ether", "Ethernet", ARPHRD_ETHER, ETH_ALEN, + pr_ether, NULL /* UNUSED in_ether */ , NULL +}; + + +#endif /* HAVE_HWETHER */ + + +#if HAVE_HWPPP + +#include + +#ifdef KEEP_UNUSED +/* Start the PPP encapsulation on the file descriptor. */ +static int do_ppp(int fd) +{ + bb_error_msg(_("You cannot start PPP with this program.")); + return -1; +} +#endif /* KEEP_UNUSED */ + +static struct hwtype ppp_hwtype = { + "ppp", "Point-Point Protocol", ARPHRD_PPP, 0, + NULL, NULL, NULL /* UNUSED do_ppp */ , 0 +}; + + +#endif /* HAVE_PPP */ + +static struct hwtype *hwtypes[] = { + + &loop_hwtype, + +#if HAVE_HWSLIP + &slip_hwtype, + &cslip_hwtype, + &slip6_hwtype, + &cslip6_hwtype, + &adaptive_hwtype, +#endif +#if HAVE_HWSTRIP + &strip_hwtype, +#endif +#if HAVE_HWASH + &ash_hwtype, +#endif +#if HAVE_HWETHER + ð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 +#if 0 +static const char * const 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}, +}; +#else +static const char * const if_port_text[] = { + /* Keep in step with */ + "unknown", + "10base2", + "10baseT", + "AUI", + "100baseT", + "100baseTX", + "100baseFX", + NULL +}; +#endif +#endif + +/* Check our hardware type table for this type. */ +static struct hwtype *get_hwntype(int type) +{ + struct hwtype **hwp; + +#ifdef KEEP_UNUSED + if (!sVhwinit) + hwinit(); +#endif /* KEEP_UNUSED */ + + hwp = hwtypes; + while (*hwp != NULL) { + if ((*hwp)->type == type) + return (*hwp); + hwp++; + } + return (NULL); +} + +/* return 1 if address is all zeros */ +static int hw_null_address(struct hwtype *hw, void *ap) +{ + unsigned int i; + unsigned char *address = (unsigned char *) ap; + + for (i = 0; i < hw->alen; i++) + if (address[i]) + return 0; + return 1; +} + +static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti"; + +static void print_bytes_scaled(unsigned long long ull, const char *end) +{ + unsigned long long int_part; + const char *ext; + unsigned int frac_part; + int i; + + frac_part = 0; + ext = TRext; + int_part = ull; + i = 4; + do { +#if 0 + /* This does correct rounding and is a little larger. But it + * uses KiB as the smallest displayed unit. */ + if ((int_part < (1024*1024 - 51)) || !--i) { + i = 0; + int_part += 51; /* 1024*.05 = 51.2 */ + frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024; + } + int_part /= 1024; + ext += 3; /* KiB, MiB, GiB, TiB */ +#else + if (int_part >= 1024) { + frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024; + int_part /= 1024; + ext += 3; /* KiB, MiB, GiB, TiB */ + } + --i; +#endif + } while (i); + + printf("X bytes:%Lu (%Lu.%u %sB)%s", ull, int_part, frac_part, ext, end); +} + +static const char * const ife_print_flags_strs[] = { + "UP ", + "BROADCAST ", + "DEBUG ", + "LOOPBACK ", + "POINTOPOINT ", + "NOTRAILERS ", + "RUNNING ", + "NOARP ", + "PROMISC ", + "ALLMULTI ", + "SLAVE ", + "MASTER ", + "MULTICAST ", +#ifdef HAVE_DYNAMIC + "DYNAMIC " +#endif +}; + +static const unsigned short ife_print_flags_mask[] = { + IFF_UP, + IFF_BROADCAST, + IFF_DEBUG, + IFF_LOOPBACK, + IFF_POINTOPOINT, + IFF_NOTRAILERS, + IFF_RUNNING, + IFF_NOARP, + IFF_PROMISC, + IFF_ALLMULTI, + IFF_SLAVE, + IFF_MASTER, + IFF_MULTICAST, +#ifdef HAVE_DYNAMIC + IFF_DYNAMIC +#endif + 0 +}; + +static void ife_print(struct interface *ptr) +{ + struct aftype *ap; + struct hwtype *hw; + int hf; + int can_compress = 0; + +#if HAVE_AFIPX + static struct aftype *ipxtype = NULL; +#endif +#if HAVE_AFECONET + static struct aftype *ectype = NULL; +#endif +#if HAVE_AFATALK + static struct aftype *ddptype = NULL; +#endif +#if HAVE_AFINET6 + FILE *f; + char addr6[40], devname[20]; + struct sockaddr_in6 sap; + int plen, scope, dad_status, if_idx; + char addr6p[8][5]; +#endif + + ap = get_afntype(ptr->addr.sa_family); + if (ap == NULL) + ap = get_afntype(0); + + hf = ptr->type; + + if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6) + can_compress = 1; + + hw = get_hwntype(hf); + if (hw == NULL) + hw = get_hwntype(-1); + + printf(_("%-9.9s Link encap:%s "), ptr->name, _(hw->title)); + /* For some hardware types (eg Ash, ATM) we don't print the + hardware address if it's null. */ + if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) && + hw->suppress_null_addr))) + printf(_("HWaddr %s "), hw->print(ptr->hwaddr)); +#ifdef IFF_PORTSEL + if (ptr->flags & IFF_PORTSEL) { + printf(_("Media:%s"), if_port_text[ptr->map.port] /* [0] */); + if (ptr->flags & IFF_AUTOMEDIA) + printf(_("(auto)")); + } +#endif + printf("\n"); + +#if HAVE_AFINET + if (ptr->has_ip) { + printf(_(" %s addr:%s "), ap->name, + ap->sprint(&ptr->addr, 1)); + if (ptr->flags & IFF_POINTOPOINT) { + printf(_(" P-t-P:%s "), ap->sprint(&ptr->dstaddr, 1)); + } + if (ptr->flags & IFF_BROADCAST) { + printf(_(" Bcast:%s "), ap->sprint(&ptr->broadaddr, 1)); + } + printf(_(" Mask:%s\n"), ap->sprint(&ptr->netmask, 1)); + } +#endif + +#if HAVE_AFINET6 + +#define IPV6_ADDR_ANY 0x0000U + +#define IPV6_ADDR_UNICAST 0x0001U +#define IPV6_ADDR_MULTICAST 0x0002U +#define IPV6_ADDR_ANYCAST 0x0004U + +#define IPV6_ADDR_LOOPBACK 0x0010U +#define IPV6_ADDR_LINKLOCAL 0x0020U +#define IPV6_ADDR_SITELOCAL 0x0040U + +#define IPV6_ADDR_COMPATv4 0x0080U + +#define IPV6_ADDR_SCOPE_MASK 0x00f0U + +#define IPV6_ADDR_MAPPED 0x1000U +#define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */ + + if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) { + while (fscanf + (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n", + addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], + addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope, + &dad_status, devname) != EOF) { + if (!strcmp(devname, ptr->name)) { + sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", + addr6p[0], addr6p[1], addr6p[2], addr6p[3], + addr6p[4], addr6p[5], addr6p[6], addr6p[7]); + inet_pton(AF_INET6, addr6, + (struct sockaddr *) &sap.sin6_addr); + sap.sin6_family = AF_INET6; + printf(_(" inet6 addr: %s/%d"), + inet6_aftype.sprint((struct sockaddr *) &sap, 1), + plen); + printf(_(" Scope:")); + switch (scope & IPV6_ADDR_SCOPE_MASK) { + case 0: + printf(_("Global")); + break; + case IPV6_ADDR_LINKLOCAL: + printf(_("Link")); + break; + case IPV6_ADDR_SITELOCAL: + printf(_("Site")); + break; + case IPV6_ADDR_COMPATv4: + printf(_("Compat")); + break; + case IPV6_ADDR_LOOPBACK: + printf(_("Host")); + break; + default: + printf(_("Unknown")); + } + printf("\n"); + } + } + fclose(f); + } +#endif + +#if HAVE_AFIPX + if (ipxtype == NULL) + ipxtype = get_afntype(AF_IPX); + + if (ipxtype != NULL) { + if (ptr->has_ipx_bb) + printf(_(" IPX/Ethernet II addr:%s\n"), + ipxtype->sprint(&ptr->ipxaddr_bb, 1)); + if (ptr->has_ipx_sn) + printf(_(" IPX/Ethernet SNAP addr:%s\n"), + ipxtype->sprint(&ptr->ipxaddr_sn, 1)); + if (ptr->has_ipx_e2) + printf(_(" IPX/Ethernet 802.2 addr:%s\n"), + ipxtype->sprint(&ptr->ipxaddr_e2, 1)); + if (ptr->has_ipx_e3) + printf(_(" IPX/Ethernet 802.3 addr:%s\n"), + ipxtype->sprint(&ptr->ipxaddr_e3, 1)); + } +#endif + +#if HAVE_AFATALK + if (ddptype == NULL) + ddptype = get_afntype(AF_APPLETALK); + if (ddptype != NULL) { + if (ptr->has_ddp) + printf(_(" EtherTalk Phase 2 addr:%s\n"), + ddptype->sprint(&ptr->ddpaddr, 1)); + } +#endif + +#if HAVE_AFECONET + if (ectype == NULL) + ectype = get_afntype(AF_ECONET); + if (ectype != NULL) { + if (ptr->has_econet) + printf(_(" econet addr:%s\n"), + ectype->sprint(&ptr->ecaddr, 1)); + } +#endif + + printf(" "); + /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */ + + if (ptr->flags == 0) { + printf(_("[NO FLAGS] ")); + } else { + int i = 0; + do { + if (ptr->flags & ife_print_flags_mask[i]) { + printf(_(ife_print_flags_strs[i])); + } + } while (ife_print_flags_mask[++i]); + } + + /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */ + printf(_(" MTU:%d Metric:%d"), ptr->mtu, ptr->metric ? ptr->metric : 1); +#ifdef SIOCSKEEPALIVE + if (ptr->outfill || ptr->keepalive) + printf(_(" Outfill:%d Keepalive:%d"), ptr->outfill, ptr->keepalive); +#endif + printf("\n"); + + /* If needed, display the interface statistics. */ + + if (ptr->statistics_valid) { + /* XXX: statistics are currently only printed for the primary address, + * not for the aliases, although strictly speaking they're shared + * by all addresses. + */ + printf(" "); + + printf(_("RX packets:%Lu errors:%lu dropped:%lu overruns:%lu frame:%lu\n"), + ptr->stats.rx_packets, ptr->stats.rx_errors, + ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors, + ptr->stats.rx_frame_errors); + if (can_compress) + printf(_(" compressed:%lu\n"), + ptr->stats.rx_compressed); + printf(" "); + printf(_("TX packets:%Lu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n"), + ptr->stats.tx_packets, ptr->stats.tx_errors, + ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors, + ptr->stats.tx_carrier_errors); + printf(_(" collisions:%lu "), ptr->stats.collisions); + if (can_compress) + printf(_("compressed:%lu "), ptr->stats.tx_compressed); + if (ptr->tx_queue_len != -1) + printf(_("txqueuelen:%d "), ptr->tx_queue_len); + printf("\n R"); + print_bytes_scaled(ptr->stats.rx_bytes, " T"); + print_bytes_scaled(ptr->stats.tx_bytes, "\n"); + + } + + if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma || + ptr->map.base_addr)) { + printf(" "); + if (ptr->map.irq) + printf(_("Interrupt:%d "), ptr->map.irq); + if (ptr->map.base_addr >= 0x100) /* Only print devices using it for + I/O maps */ + printf(_("Base address:0x%lx "), + (unsigned long) ptr->map.base_addr); + if (ptr->map.mem_start) { + printf(_("Memory:%lx-%lx "), ptr->map.mem_start, + ptr->map.mem_end); + } + if (ptr->map.dma) + printf(_("DMA chan:%x "), ptr->map.dma); + printf("\n"); + } + printf("\n"); +} + + +static int do_if_print(struct interface *ife, void *cookie) +{ + int *opt_a = (int *) cookie; + int res; + + res = do_if_fetch(ife); + if (res >= 0) { + if ((ife->flags & IFF_UP) || *opt_a) + ife_print(ife); + } + return res; +} + +static struct interface *lookup_interface(char *name) +{ + struct interface *ife = NULL; + + if (if_readlist_proc(name) < 0) + return NULL; + ife = add_interface(name); + return ife; +} + +/* for ipv4 add/del modes */ +static int if_print(char *ifname) +{ + int res; + + if (!ifname) { + res = for_all_interfaces(do_if_print, &interface_opt_a); + } else { + struct interface *ife; + + ife = lookup_interface(ifname); + res = do_if_fetch(ife); + if (res >= 0) + ife_print(ife); + } + return res; +} + +int display_interfaces(char *ifname) +{ + int status; + + /* Create a channel to the NET kernel. */ + if ((skfd = sockets_open(0)) < 0) { + bb_perror_msg_and_die("socket"); + } + + /* Do we have to show the current setup? */ + status = if_print(ifname); + close(skfd); + exit(status < 0); +} diff --git a/busybox/libbb/isdirectory.c b/busybox/libbb/isdirectory.c new file mode 100644 index 000000000..7f8f7e415 --- /dev/null +++ b/busybox/libbb/isdirectory.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Based in part on code from sash, Copyright (c) 1999 by David I. Bell + * Permission has been granted to redistribute this code under the GPL. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "libbb.h" + +/* + * Return TRUE if a fileName is a directory. + * Nonexistent files return FALSE. + */ +int is_directory(const char *fileName, const int followLinks, struct stat *statBuf) +{ + int status; + struct stat astatBuf; + + if (statBuf == NULL) { + /* set from auto stack buffer */ + statBuf = &astatBuf; + } + + if (followLinks) + status = stat(fileName, statBuf); + else + status = lstat(fileName, statBuf); + + if (status < 0 || !(S_ISDIR(statBuf->st_mode))) { + status = FALSE; + } + else status = TRUE; + + return status; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/kernel_version.c b/busybox/libbb/kernel_version.c new file mode 100644 index 000000000..e01aafa25 --- /dev/null +++ b/busybox/libbb/kernel_version.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 /* 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) { + bb_perror_msg("cannot get system information"); + return (0); + } + + s = name.release; + r = 0; + for (i=0 ; i<3 ; i++) { + r = r * 256 + atoi(strtok(s, ".")); + s = NULL; + } + return r; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/last_char_is.c b/busybox/libbb/last_char_is.c new file mode 100644 index 000000000..9bd70996c --- /dev/null +++ b/busybox/libbb/last_char_is.c @@ -0,0 +1,38 @@ +/* + * 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 = (char *)s; + if (sret) { + sret = strrchr(sret, c); + if(sret != NULL && *(sret+1) != 0) + sret = NULL; + } + return sret; +} diff --git a/busybox/libbb/llist_add_to.c b/busybox/libbb/llist_add_to.c new file mode 100644 index 000000000..61e53f0c1 --- /dev/null +++ b/busybox/libbb/llist_add_to.c @@ -0,0 +1,15 @@ +#include +#include +#include "unarchive.h" +#include "libbb.h" + +extern llist_t *llist_add_to(llist_t *old_head, char *new_item) +{ + llist_t *new_head; + + new_head = xmalloc(sizeof(llist_t)); + new_head->data = new_item; + new_head->link = old_head; + + return(new_head); +} diff --git a/busybox/libbb/login.c b/busybox/libbb/login.c new file mode 100644 index 000000000..3f67a819a --- /dev/null +++ b/busybox/libbb/login.c @@ -0,0 +1,128 @@ +/* + * issue.c: issue printing code + * + * Copyright (C) 2003 Bastian Blank + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Optimize and correcting OCRNL by Vladimir Oleynik + */ + +#include /* MAXHOSTNAMELEN */ +#include +#include +#include "libbb.h" + +#include +#include + +#define LOGIN " login: " + +static const char fmtstr_d[] = "%A, %d %B %Y"; +static const char fmtstr_t[] = "%H:%M:%S"; + +void print_login_issue(const char *issue_file, const char *tty) +{ + FILE *fd; + int c; + char buf[256]; + const char *outbuf; + time_t t; + struct utsname uts; + + time(&t); + uname(&uts); + + puts("\r"); /* start a new line */ + + if ((fd = fopen(issue_file, "r"))) { + while ((c = fgetc(fd)) != EOF) { + outbuf = buf; + buf[0] = c; + if(c == '\n') { + buf[1] = '\r'; + buf[2] = 0; + } else { + buf[1] = 0; + } + if (c == '\\' || c == '%') { + c = fgetc(fd); + switch (c) { + case 's': + outbuf = uts.sysname; + break; + + case 'n': + outbuf = uts.nodename; + break; + + case 'r': + outbuf = uts.release; + break; + + case 'v': + outbuf = uts.version; + break; + + case 'm': + outbuf = uts.machine; + break; + + case 'D': + case 'o': + getdomainname(buf, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + break; + + case 'd': + strftime(buf, sizeof(buf), fmtstr_d, localtime(&t)); + break; + + case 't': + strftime(buf, sizeof(buf), fmtstr_t, localtime(&t)); + break; + + case 'h': + gethostname(buf, sizeof(buf) - 1); + break; + + case 'l': + outbuf = tty; + break; + + default: + buf[0] = c; + } + } + fputs(outbuf, stdout); + } + + fclose(fd); + + fflush(stdout); + } +} + +void print_login_prompt(void) +{ + char buf[MAXHOSTNAMELEN+1]; + + gethostname(buf, MAXHOSTNAMELEN); + fputs(buf, stdout); + + fputs(LOGIN, stdout); + fflush(stdout); +} + diff --git a/busybox/libbb/loop.c b/busybox/libbb/loop.c new file mode 100644 index 000000000..e2287b5ba --- /dev/null +++ b/busybox/libbb/loop.c @@ -0,0 +1,157 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +/* Grumble... The 2.6.x kernel breaks asm/posix_types.h + * so we get to try and cope as best we can... */ +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#define __bb_kernel_dev_t __kernel_old_dev_t +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#define __bb_kernel_dev_t __kernel_dev_t +#else +#define __bb_kernel_dev_t unsigned short +#endif + +/* Stuff stolen from linux/loop.h */ +#define LO_NAME_SIZE 64 +#define LO_KEY_SIZE 32 +#define LOOP_SET_FD 0x4C00 +#define LOOP_CLR_FD 0x4C01 +#define LOOP_SET_STATUS 0x4C02 +#define LOOP_GET_STATUS 0x4C03 +struct loop_info { + int lo_number; + __bb_kernel_dev_t lo_device; + unsigned long lo_inode; + __bb_kernel_dev_t lo_rdevice; + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; + int lo_flags; + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; + unsigned long lo_init[2]; + char reserved[4]; +}; + +extern int del_loop(const char *device) +{ + int fd; + + if ((fd = open(device, O_RDONLY)) < 0) { + bb_perror_msg("%s", device); + return (FALSE); + } + if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { + close(fd); + bb_perror_msg("ioctl: LOOP_CLR_FD"); + return (FALSE); + } + close(fd); + return (TRUE); +} + +extern int set_loop(const char *device, const char *file, int offset, + int *loopro) +{ + struct loop_info loopinfo; + int fd, ffd, mode; + + mode = *loopro ? O_RDONLY : O_RDWR; + if ((ffd = open(file, mode)) < 0 && !*loopro + && (errno != EROFS || (ffd = open(file, mode = O_RDONLY)) < 0)) { + bb_perror_msg("%s", file); + return 1; + } + if ((fd = open(device, mode)) < 0) { + close(ffd); + bb_perror_msg("%s", device); + return 1; + } + *loopro = (mode == O_RDONLY); + + memset(&loopinfo, 0, sizeof(loopinfo)); + safe_strncpy(loopinfo.lo_name, file, LO_NAME_SIZE); + + loopinfo.lo_offset = offset; + + loopinfo.lo_encrypt_key_size = 0; + if (ioctl(fd, LOOP_SET_FD, ffd) < 0) { + bb_perror_msg("ioctl: LOOP_SET_FD"); + close(fd); + close(ffd); + return 1; + } + if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) { + (void) ioctl(fd, LOOP_CLR_FD, 0); + bb_perror_msg("ioctl: LOOP_SET_STATUS"); + close(fd); + close(ffd); + return 1; + } + close(fd); + close(ffd); + return 0; +} + +extern char *find_unused_loop_device(void) +{ + char dev[20]; + int i, fd; + struct stat statbuf; + struct loop_info loopinfo; + + for (i = 0; i <= 7; i++) { + sprintf(dev, LOOP_FORMAT, i); + if (stat(dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) { + if ((fd = open(dev, O_RDONLY)) >= 0) { + if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) != 0) { + if (errno == ENXIO) { /* probably free */ + close(fd); + return strdup(dev); + } + } + close(fd); + } + } + } + return NULL; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/make_directory.c b/busybox/libbb/make_directory.c new file mode 100644 index 000000000..d96acf0d9 --- /dev/null +++ b/busybox/libbb/make_directory.c @@ -0,0 +1,117 @@ +/* vi: set sw=4 ts=4: */ +/* + * parse_mode implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Mar 5, 2003 Manuel Novoa III + * + * This is the main work function for the 'mkdir' applet. As such, it + * strives to be SUSv3 compliant in it's behaviour when recursively + * making missing parent dirs, and in it's mode setting of the final + * directory 'path'. + * + * To recursively build all missing intermediate directories, make + * sure that (flags & FILEUTILS_RECUR) is non-zero. Newly created + * intermediate directories will have at least u+wx perms. + * + * To set specific permissions on 'path', pass the appropriate 'mode' + * val. Otherwise, pass -1 to get default permissions. + */ + +#include +#include +#include +#include "libbb.h" + +int bb_make_directory (char *path, long mode, int flags) +{ + mode_t mask; + const char *fail_msg; + char *s = path; + char c; + struct stat st; + + mask = umask(0); + if (mode == -1) { + umask(mask); + mode = (S_IXUSR | S_IXGRP | S_IXOTH | + S_IWUSR | S_IWGRP | S_IWOTH | + S_IRUSR | S_IRGRP | S_IROTH) & ~mask; + } else { + umask(mask & ~0300); + } + + do { + c = 0; + + if (flags & FILEUTILS_RECUR) { /* Get the parent. */ + /* Bypass leading non-'/'s and then subsequent '/'s. */ + while (*s) { + if (*s == '/') { + do { + ++s; + } while (*s == '/'); + c = *s; /* Save the current char */ + *s = 0; /* and replace it with nul. */ + break; + } + ++s; + } + } + + if (mkdir(path, 0777) < 0) { + /* If we failed for any other reason than the directory + * already exists, output a diagnostic and return -1.*/ + if (errno != EEXIST + || !(flags & FILEUTILS_RECUR) + || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) { + fail_msg = "create"; + umask(mask); + break; + } + /* Since the directory exists, don't attempt to change + * permissions if it was the full target. Note that + * this is not an error conditon. */ + if (!c) { + umask(mask); + return 0; + } + } + + if (!c) { + /* Done. If necessary, updated perms on the newly + * created directory. Failure to update here _is_ + * an error.*/ + umask(mask); + if ((mode != -1) && (chmod(path, mode) < 0)){ + fail_msg = "set permissions of"; + break; + } + return 0; + } + + /* Remove any inserted nul from the path (recursive mode). */ + *s = c; + + } while (1); + + bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path); + return -1; +} diff --git a/busybox/libbb/messages.c b/busybox/libbb/messages.c new file mode 100644 index 000000000..671c452d2 --- /dev/null +++ b/busybox/libbb/messages.c @@ -0,0 +1,96 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 1999-2004 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 "busybox.h" +#include "libbb.h" + +#ifdef L_full_version + const char * const bb_msg_full_version = BB_BANNER " multi-call binary"; +#endif +#ifdef L_memory_exhausted + const char * const bb_msg_memory_exhausted = "memory exhausted"; +#endif +#ifdef L_invalid_date + const char * const bb_msg_invalid_date = "invalid date `%s'"; +#endif +#ifdef L_io_error + const char * const bb_msg_io_error = "%s: input/output error -- %m"; +#endif +#ifdef L_write_error + const char * const bb_msg_write_error = "Write Error"; +#endif +#ifdef L_name_longer_than_foo + const char * const bb_msg_name_longer_than_foo = "Names longer than %d chars not supported."; +#endif +#ifdef L_unknown + const char * const bb_msg_unknown = "(unknown)"; +#endif +#ifdef L_can_not_create_raw_socket + const char * const bb_msg_can_not_create_raw_socket = "can`t create raw socket"; +#endif +#ifdef L_perm_denied_are_you_root + const char * const bb_msg_perm_denied_are_you_root = "permission denied. (are you root?)"; +#endif +#ifdef L_msg_standard_input + const char * const bb_msg_standard_input = "standard input"; +#endif +#ifdef L_msg_standard_output + const char * const bb_msg_standard_output = "standard output"; +#endif + +#ifdef L_passwd_file +#define PASSWD_FILE "/etc/passwd" +const char * const bb_path_passwd_file = PASSWD_FILE; +#endif + +#ifdef L_shadow_file +#define SHADOW_FILE "/etc/shadow" +const char * const bb_path_shadow_file = SHADOW_FILE; +#endif + +#ifdef L_group_file +#define GROUP_FILE "/etc/group" +const char * const bb_path_group_file = GROUP_FILE; +#endif + +#ifdef L_gshadow_file +#define GSHADOW_FILE "/etc/gshadow" +const char * const bb_path_gshadow_file = GSHADOW_FILE; +#endif + +#ifdef L_nologin_file +#define NOLOGIN_FILE "/etc/nologin" +const char * const bb_path_nologin_file = NOLOGIN_FILE; +#endif + +#ifdef L_securetty_file +#define SECURETTY_FILE "/etc/securetty" +const char * const bb_path_securetty_file = SECURETTY_FILE; +#endif + +#ifdef L_motd_file +#define MOTD_FILE "/etc/motd" +const char * const bb_path_motd_file = MOTD_FILE; +#endif + +#ifdef L_shell_file +const char * const bb_default_login_shell = LIBBB_DEFAULT_LOGIN_SHELL; +#endif + diff --git a/busybox/libbb/mode_string.c b/busybox/libbb/mode_string.c new file mode 100644 index 000000000..83142ba8a --- /dev/null +++ b/busybox/libbb/mode_string.c @@ -0,0 +1,139 @@ +/* vi: set sw=4 ts=4: */ +/* + * mode_string implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Aug 13, 2003 + * Fix a bug reported by junkio@cox.net involving the mode_chars index. + */ + + +#include +#include + +#if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \ + || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \ + || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \ + || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 ) +#error permission bitflag value assumption(s) violated! +#endif + +#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \ + || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \ + || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \ + || ( S_IFIFO != 0010000 ) +#warning mode type bitflag value assumption(s) violated! falling back to larger version + +#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777 +#undef mode_t +#define mode_t unsigned short +#endif + +static const mode_t mode_flags[] = { + S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID, + S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID, + S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX +}; + +/* The static const char arrays below are duplicated for the two cases + * because moving them ahead of the mode_flags declaration cause a text + * size increase with the gcc version I'm using. */ + +/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', + * and 'B' types don't appear to be available on linux. So I removed them. */ +static const char type_chars[16] = "?pc?d?b?-?l?s???"; +/* 0123456789abcdef */ +static const char mode_chars[7] = "rwxSTst"; + +const char *bb_mode_string(int mode) +{ + static char buf[12]; + char *p = buf; + + int i, j, k; + + *p = type_chars[ (mode >> 12) & 0xf ]; + i = 0; + do { + j = k = 0; + do { + *++p = '-'; + if (mode & mode_flags[i+j]) { + *p = mode_chars[j]; + k = j; + } + } while (++j < 3); + if (mode & mode_flags[i+j]) { + *p = mode_chars[3 + (k & 2) + ((i&8) >> 3)]; + } + i += 4; + } while (i < 12); + + /* Note: We don't bother with nul termination because bss initialization + * should have taken care of that for us. If the user scribbled in buf + * memory, they deserve whatever happens. But we'll at least assert. */ + assert(buf[10] == 0); + + return buf; +} + +#else + +/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', + * and 'B' types don't appear to be available on linux. So I removed them. */ +static const char type_chars[16] = "?pc?d?b?-?l?s???"; +/* 0123456789abcdef */ +static const char mode_chars[7] = "rwxSTst"; + +const char *bb_mode_string(int mode) +{ + static char buf[12]; + char *p = buf; + + int i, j, k, m; + + *p = type_chars[ (mode >> 12) & 0xf ]; + i = 0; + m = 0400; + do { + j = k = 0; + do { + *++p = '-'; + if (mode & m) { + *p = mode_chars[j]; + k = j; + } + m >>= 1; + } while (++j < 3); + ++i; + if (mode & (010000 >> i)) { + *p = mode_chars[3 + (k & 2) + (i == 3)]; + } + } while (i < 3); + + /* Note: We don't bother with nul termination because bss initialization + * should have taken care of that for us. If the user scribbled in buf + * memory, they deserve whatever happens. But we'll at least assert. */ + assert(buf[10] == 0); + + return buf; +} + +#endif diff --git a/busybox/libbb/module_syscalls.c b/busybox/libbb/module_syscalls.c new file mode 100644 index 000000000..a2ff5284a --- /dev/null +++ b/busybox/libbb/module_syscalls.c @@ -0,0 +1,116 @@ +/* vi: set sw=4 ts=4: */ +/* + * some system calls possibly missing from libc + * + * Copyright (C) 1999-2004 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 "libbb.h" + +/* uClibc always supplies (possibly ENOSYS) versions of these functions. */ +#ifndef __UCLIBC__ + +/* These syscalls are not included in very old glibc versions */ +int delete_module(const char *name) +{ +#ifndef __NR_delete_module +#warning This kernel does not support the delete_module syscall +#warning -> The delete_module system call is being stubbed out... + errno=ENOSYS; + return -1; +#else + return(syscall(__NR_delete_module, name)); +#endif +} + +int get_kernel_syms(__ptr_t ks) +{ +#ifndef __NR_get_kernel_syms +#warning This kernel does not support the get_kernel_syms syscall +#warning -> The get_kernel_syms system call is being stubbed out... + errno=ENOSYS; + return -1; +#else + return(syscall(__NR_get_kernel_syms, ks)); +#endif +} + +/* This may have 5 arguments (for old 2.0 kernels) or 2 arguments + * (for 2.2 and 2.4 kernels). Use the greatest common denominator, + * and let the kernel cope with whatever it gets. Its good at that. */ +int init_module(void *first, void *second, void *third, void *fourth, void *fifth) +{ +#ifndef __NR_init_module +#warning This kernel does not support the init_module syscall +#warning -> The init_module system call is being stubbed out... + errno=ENOSYS; + return -1; +#else + return(syscall(__NR_init_module, first, second, third, fourth, fifth)); +#endif +} + +int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret) +{ +#ifndef __NR_query_module +#warning This kernel does not support the query_module syscall +#warning -> The query_module system call is being stubbed out... + bb_error_msg("\n\nTo make this application work, you will need to recompile\n" + "BusyBox with a kernel supporting the query_module system call.\n"); + errno=ENOSYS; + return -1; +#else + return(syscall(__NR_query_module, name, which, buf, bufsize, ret)); +#endif +} + +/* Jump through hoops to fixup error return codes */ +unsigned long create_module(const char *name, size_t size) +{ +#ifndef __NR_create_module +#warning This kernel does not support the create_module syscall +#warning -> The create_module system call is being stubbed out... + errno=ENOSYS; + return -1; +#else + long ret = syscall(__NR_create_module, name, size); + + if (ret == -1 && errno > 125) { + ret = -errno; + errno = 0; + } + return ret; +#endif +} + + +#endif /* __UCLIBC__ */ + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + diff --git a/busybox/libbb/mtab.c b/busybox/libbb/mtab.c new file mode 100644 index 000000000..b1f74c476 --- /dev/null +++ b/busybox/libbb/mtab.c @@ -0,0 +1,116 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +#define MTAB_MAX_ENTRIES 40 +static const int MS_RDONLY = 1; /* Mount read-only. */ + +void erase_mtab(const char *name) +{ + struct mntent entries[MTAB_MAX_ENTRIES]; + int count = 0; + FILE *mountTable = setmntent(bb_path_mtab_file, "r"); + struct mntent *m; + + /* Check if reading the mtab file failed */ + if (mountTable == 0 + /* Bummer. fall back on trying the /proc filesystem */ + && (mountTable = setmntent("/proc/mounts", "r")) == 0) { + bb_perror_msg(bb_path_mtab_file); + return; + } + + while (((m = getmntent(mountTable)) != 0) && (count < MTAB_MAX_ENTRIES)) + { + entries[count].mnt_fsname = strdup(m->mnt_fsname); + entries[count].mnt_dir = strdup(m->mnt_dir); + entries[count].mnt_type = strdup(m->mnt_type); + entries[count].mnt_opts = strdup(m->mnt_opts); + entries[count].mnt_freq = m->mnt_freq; + entries[count].mnt_passno = m->mnt_passno; + count++; + } + endmntent(mountTable); + if ((mountTable = setmntent(bb_path_mtab_file, "w"))) { + int i; + + for (i = 0; i < count; i++) { + int result = (strcmp(entries[i].mnt_fsname, name) == 0 + || strcmp(entries[i].mnt_dir, name) == 0); + + if (result) + continue; + else + addmntent(mountTable, &entries[i]); + } + endmntent(mountTable); + } else if (errno != EROFS) + bb_perror_msg(bb_path_mtab_file); +} + +void write_mtab(char *blockDevice, char *directory, + char *filesystemType, long flags, char *string_flags) +{ + FILE *mountTable = setmntent(bb_path_mtab_file, "a+"); + struct mntent m; + + if (mountTable == 0) { + bb_perror_msg(bb_path_mtab_file); + return; + } + if (mountTable) { + int length = strlen(directory); + + if (length > 1 && directory[length - 1] == '/') + directory[length - 1] = '\0'; + + if (filesystemType == 0) { + struct mntent *p = find_mount_point(blockDevice, "/proc/mounts"); + + if (p && p->mnt_type) + filesystemType = p->mnt_type; + } + m.mnt_fsname = blockDevice; + m.mnt_dir = directory; + m.mnt_type = filesystemType ? filesystemType : "default"; + + if (*string_flags) { + m.mnt_opts = string_flags; + } else { + if ((flags | MS_RDONLY) == flags) + m.mnt_opts = "ro"; + else + m.mnt_opts = "rw"; + } + + m.mnt_freq = 0; + m.mnt_passno = 0; + addmntent(mountTable, &m); + endmntent(mountTable); + } +} diff --git a/busybox/libbb/mtab_file.c b/busybox/libbb/mtab_file.c new file mode 100644 index 000000000..42504e81f --- /dev/null +++ b/busybox/libbb/mtab_file.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + + +/* Busybox mount uses either /proc/mounts or /etc/mtab to + * get the list of currently mounted filesystems */ +#if defined CONFIG_FEATURE_MTAB_SUPPORT +const char bb_path_mtab_file[] = CONFIG_FEATURE_MTAB_FILENAME; +#else +const char bb_path_mtab_file[] = "/proc/mounts"; +#endif + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/my_getgrgid.c b/busybox/libbb/my_getgrgid.c new file mode 100644 index 000000000..9ac14a34e --- /dev/null +++ b/busybox/libbb/my_getgrgid.c @@ -0,0 +1,57 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 + */ + + /* Hacked by Tito Ragusa (c) 2004 to make it more + * flexible : + * + * if bufsize is > 0 char *group cannot be set to NULL. + * On success groupname is written on static allocated buffer group + * (and a pointer to it is returned). + * On failure gid as string is written to static allocated buffer + * group and NULL is returned. + * if bufsize is = 0 char *group can be set to NULL. + * On success groupname is returned. + * On failure NULL is returned. + * if bufsize is < 0 char *group can be set to NULL. + * On success groupname is returned. + * On failure an error message is printed and the program exits. + */ + +#include "libbb.h" +#include "grp_.h" + +/* gets a groupname given a gid */ +char * my_getgrgid(char *group, long gid, int bufsize) +{ + struct group *mygroup = getgrgid(gid); + + return my_getug(group, (mygroup) ? mygroup->gr_name : (char *)mygroup, gid, bufsize, 'g'); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/my_getgrnam.c b/busybox/libbb/my_getgrnam.c new file mode 100644 index 000000000..22a617cc8 --- /dev/null +++ b/busybox/libbb/my_getgrnam.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" +#include "pwd_.h" +#include "grp_.h" + + +/* returns a gid given a group name */ +long my_getgrnam(const char *name) +{ + struct group *mygroup; + + mygroup = getgrnam(name); + if (mygroup==NULL) + bb_error_msg_and_die("unknown group name: %s", name); + + return (mygroup->gr_gid); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/my_getpwnam.c b/busybox/libbb/my_getpwnam.c new file mode 100644 index 000000000..a9fd0cd09 --- /dev/null +++ b/busybox/libbb/my_getpwnam.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" +#include "pwd_.h" +#include "grp_.h" + + +/* returns a uid given a username */ +long my_getpwnam(const char *name) +{ + struct passwd *myuser; + + myuser = getpwnam(name); + if (myuser==NULL) + bb_error_msg_and_die("unknown user name: %s", name); + + return myuser->pw_uid; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/my_getpwuid.c b/busybox/libbb/my_getpwuid.c new file mode 100644 index 000000000..7da360ac8 --- /dev/null +++ b/busybox/libbb/my_getpwuid.c @@ -0,0 +1,56 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 + */ + + /* Hacked by Tito Ragusa (c) 2004 to make it more + * flexible : + * + * if bufsize is > 0 char *name can not be set to NULL. + * On success username is written on the static allocated buffer name + * (and a pointer to it is returned). + * On failure uid as string is written to the static allocated buffer name + * and NULL is returned. + * if bufsize is = 0 char *name can be set to NULL. + * On success username is returned. + * On failure NULL is returned. + * if bufsize is < 0 char *name can be set to NULL + * On success username is returned. + * On failure an error message is printed and the program exits. + */ + +#include "libbb.h" +#include "pwd_.h" + +/* gets a username given a uid */ +char * my_getpwuid(char *name, long uid, int bufsize) +{ + struct passwd *myuser = getpwuid(uid); + + return my_getug(name, (myuser) ? myuser->pw_name : (char *)myuser , uid, bufsize, 'u'); +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/my_getug.c b/busybox/libbb/my_getug.c new file mode 100644 index 000000000..9b7292d13 --- /dev/null +++ b/busybox/libbb/my_getug.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2004 by Tito Ragusa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + /* + * + * if bufsize is > 0 char *buffer can not be set to NULL. + * If idname is not NULL it is written on the static allocated buffer + * (and a pointer to it is returned). + * if idname is NULL, id as string is written to the static allocated buffer + * and NULL is returned. + * if bufsize is = 0 char *buffer can be set to NULL. + * If idname exists a pointer to it is returned, + * else NULL is returned. + * if bufsize is < 0 char *buffer can be set to NULL. + * If idname exists a pointer to it is returned, + * else an error message is printed and the program exits. + */ + +#include +#include +#include "libbb.h" + + +/* internal function for my_getpwuid and my_getgrgid */ +char * my_getug(char *buffer, char *idname, long id, int bufsize, char prefix) +{ + if(bufsize > 0 ) { + assert(buffer!=NULL); + if(idname) { + return safe_strncpy(buffer, idname, bufsize); + } + snprintf(buffer, bufsize, "%ld", id); + } else if(bufsize < 0 && !idname) { + bb_error_msg_and_die("unknown %cid %ld", prefix, id); + } + return idname; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/obscure.c b/busybox/libbb/obscure.c new file mode 100644 index 000000000..aa15e4097 --- /dev/null +++ b/busybox/libbb/obscure.c @@ -0,0 +1,251 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1994, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This version of obscure.c contains modifications to support "cracklib" + * by Alec Muffet (alec.muffett@uk.sun.com). You must obtain the Cracklib + * library source code for this function to operate. + */ + +#include +#include +#include +#include +#include "libbb.h" + +/* + * can't be a palindrome - like `R A D A R' or `M A D A M' + */ + +static int palindrome(const char *newval) +{ + int i, j; + + i = strlen(newval); + + for (j = 0; j < i; j++) + if (newval[i - j - 1] != newval[j]) + return 0; + + return 1; +} + +/* + * more than half of the characters are different ones. + */ + +static int similiar(const char *old, const char *newval) +{ + int i, j; + + for (i = j = 0; newval[i] && old[i]; i++) + if (strchr(newval, old[i])) + j++; + + if (i >= j * 2) + return 0; + + return 1; +} + +/* + * a nice mix of characters. + */ + +static int simple(const char *newval) +{ + int digits = 0; + int uppers = 0; + int lowers = 0; + int others = 0; + int c; + int size; + int i; + + for (i = 0; (c = *newval++) != 0; i++) { + if (isdigit(c)) + digits = c; + else if (isupper(c)) + uppers = c; + else if (islower(c)) + lowers = c; + else + others = c; + } + + /* + * The scam is this - a password of only one character type + * must be 8 letters long. Two types, 7, and so on. + */ + + size = 9; + if (digits) + size--; + if (uppers) + size--; + if (lowers) + size--; + if (others) + size--; + + if (size <= i) + return 0; + + return 1; +} + +static char *str_lower(char *string) +{ + char *cp; + + for (cp = string; *cp; cp++) + *cp = tolower(*cp); + return string; +} + +static const char * +password_check(const char *old, const char *newval, const struct passwd *pwdp) +{ + const char *msg; + char *newmono, *wrapped; + int lenwrap; + + if (strcmp(newval, old) == 0) + return "no change"; + if (simple(newval)) + return "too simple"; + + msg = NULL; + newmono = str_lower(bb_xstrdup(newval)); + lenwrap = strlen(old); + wrapped = (char *) xmalloc(lenwrap * 2 + 1); + str_lower(strcpy(wrapped, old)); + + if (palindrome(newmono)) + msg = "a palindrome"; + + else if (strcmp(wrapped, newmono) == 0) + msg = "case changes only"; + + else if (similiar(wrapped, newmono)) + msg = "too similiar"; + + else { + safe_strncpy(wrapped + lenwrap, wrapped, lenwrap + 1); + if (strstr(wrapped, newmono)) + msg = "rotated"; + } + + bzero(newmono, strlen(newmono)); + bzero(wrapped, lenwrap * 2); + free(newmono); + free(wrapped); + + return msg; +} + +static const char * +obscure_msg(const char *old, const char *newval, const struct passwd *pwdp) +{ + int maxlen, oldlen, newlen; + char *new1, *old1; + const char *msg; + + oldlen = strlen(old); + newlen = strlen(newval); + +#if 0 /* why not check the password when set for the first time? --marekm */ + if (old[0] == '\0') + /* return (1); */ + return NULL; +#endif + + if (newlen < 5) + return "too short"; + + /* + * Remaining checks are optional. + */ + /* Not for us -- Sean + *if (!getdef_bool("OBSCURE_CHECKS_ENAB")) + * return NULL; + */ + msg = password_check(old, newval, pwdp); + if (msg) + return msg; + + /* The traditional crypt() truncates passwords to 8 chars. It is + possible to circumvent the above checks by choosing an easy + 8-char password and adding some random characters to it... + Example: "password$%^&*123". So check it again, this time + truncated to the maximum length. Idea from npasswd. --marekm */ + + maxlen = 8; + if (oldlen <= maxlen && newlen <= maxlen) + return NULL; + + new1 = (char *) bb_xstrdup(newval); + old1 = (char *) bb_xstrdup(old); + if (newlen > maxlen) + new1[maxlen] = '\0'; + if (oldlen > maxlen) + old1[maxlen] = '\0'; + + msg = password_check(old1, new1, pwdp); + + bzero(new1, newlen); + bzero(old1, oldlen); + free(new1); + free(old1); + + return msg; +} + +/* + * Obscure - see if password is obscure enough. + * + * The programmer is encouraged to add as much complexity to this + * routine as desired. Included are some of my favorite ways to + * check passwords. + */ + +extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) +{ + const char *msg = obscure_msg(old, newval, pwdp); + + /* if (msg) { */ + if (msg != NULL) { + printf("Bad password: %s.\n", msg); + /* return 0; */ + return 1; + } + /* return 1; */ + return 0; +} diff --git a/busybox/libbb/parse_mode.c b/busybox/libbb/parse_mode.c new file mode 100644 index 000000000..185957bc3 --- /dev/null +++ b/busybox/libbb/parse_mode.c @@ -0,0 +1,177 @@ +/* vi: set sw=4 ts=4: */ +/* + * parse_mode implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ + +#include +#include +#include +#include "libbb.h" + +#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) + +extern int bb_parse_mode(const char *s, mode_t *current_mode) +{ + static const mode_t who_mask[] = { + S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */ + S_ISUID | S_IRWXU, /* u */ + S_ISGID | S_IRWXG, /* g */ + S_IRWXO /* o */ + }; + + static const mode_t perm_mask[] = { + S_IRUSR | S_IRGRP | S_IROTH, /* r */ + S_IWUSR | S_IWGRP | S_IWOTH, /* w */ + S_IXUSR | S_IXGRP | S_IXOTH, /* x */ + S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */ + S_ISUID | S_ISGID, /* s */ + S_ISVTX /* t */ + }; + + static const char who_chars[] = "augo"; + static const char perm_chars[] = "rwxXst"; + + const char *p; + + mode_t wholist; + mode_t permlist; + mode_t mask; + mode_t new_mode; + char op; + + assert(s); + + if (((unsigned int)(*s - '0')) < 8) { + unsigned long tmp; + char *e; + + tmp = strtol(s, &e, 8); + if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */ + return 0; + } + *current_mode = tmp; + return 1; + } + + mask = umask(0); + umask(mask); + + new_mode = *current_mode; + + /* Note: We allow empty clauses, and hence empty modes. + * We treat an empty mode as no change to perms. */ + + while (*s) { /* Process clauses. */ + + if (*s == ',') { /* We allow empty clauses. */ + ++s; + continue; + } + + /* Get a wholist. */ + wholist = 0; + + WHO_LIST: + p = who_chars; + do { + if (*p == *s) { + wholist |= who_mask[(int)(p-who_chars)]; + if (!*++s) { + return 0; + } + goto WHO_LIST; + } + } while (*++p); + + do { /* Process action list. */ + if ((*s != '+') && (*s != '-')) { + if (*s != '=') { + return 0; + } + /* Since op is '=', clear all bits corresponding to the + * wholist, of all file bits if wholist is empty. */ + permlist = ~FILEMODEBITS; + if (wholist) { + permlist = ~wholist; + } + new_mode &= permlist; + } + op = *s++; + + /* Check for permcopy. */ + p = who_chars + 1; /* Skip 'a' entry. */ + do { + if (*p == *s) { + int i = 0; + permlist = who_mask[(int)(p-who_chars)] + & (S_IRWXU | S_IRWXG | S_IRWXO) + & new_mode; + do { + if (permlist & perm_mask[i]) { + permlist |= perm_mask[i]; + } + } while (++i < 3); + ++s; + goto GOT_ACTION; + } + } while (*++p); + + /* It was not a permcopy, so get a permlist. */ + permlist = 0; + + PERM_LIST: + p = perm_chars; + do { + if (*p == *s) { + if ((*p != 'X') + || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH)) + ) { + permlist |= perm_mask[(int)(p-perm_chars)]; + } + if (!*++s) { + break; + } + goto PERM_LIST; + } + } while (*++p); + + GOT_ACTION: + if (permlist) { /* The permlist was nonempty. */ + mode_t tmp = ~mask; + if (wholist) { + tmp = wholist; + } + permlist &= tmp; + + if (op == '-') { + new_mode &= ~permlist; + } else { + new_mode |= permlist; + } + } + } while (*s && (*s != ',')); + } + + *current_mode = new_mode; + + return 1; +} diff --git a/busybox/libbb/parse_number.c b/busybox/libbb/parse_number.c new file mode 100644 index 000000000..5262239ff --- /dev/null +++ b/busybox/libbb/parse_number.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_xparse_number implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 "libbb.h" + +extern +unsigned long bb_xparse_number(const char *numstr, + const struct suffix_mult *suffixes) +{ + unsigned long int r; + char *e; + int old_errno; + + /* Since this is a lib function, we're not allowed to reset errno to 0. + * Doing so could break an app that is deferring checking of errno. + * So, save the old value so that we can restore it if successful. */ + old_errno = errno; + errno = 0; + r = strtoul(numstr, &e, 10); + + if ((numstr != e) && !errno) { + errno = old_errno; /* Ok. So restore errno. */ + if (!*e) { + return r; + } + if (suffixes) { + assert(suffixes->suffix); /* No nul suffixes. */ + do { + if (strcmp(suffixes->suffix, e) == 0) { + if (ULONG_MAX / suffixes->mult < r) { /* Overflow! */ + break; + } + return r * suffixes->mult; + } + ++suffixes; + } while (suffixes->suffix); + } + } + bb_error_msg_and_die("invalid number `%s'", numstr); +} diff --git a/busybox/libbb/perror_msg.c b/busybox/libbb/perror_msg.c new file mode 100644 index 000000000..8ba053188 --- /dev/null +++ b/busybox/libbb/perror_msg.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_perror_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vperror_msg(s, p); + va_end(p); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/perror_msg_and_die.c b/busybox/libbb/perror_msg_and_die.c new file mode 100644 index 000000000..15bf0421e --- /dev/null +++ b/busybox/libbb/perror_msg_and_die.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_perror_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_vperror_msg(s, p); + va_end(p); + exit(bb_default_error_retval); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/perror_nomsg.c b/busybox/libbb/perror_nomsg.c new file mode 100644 index 000000000..464cb86c4 --- /dev/null +++ b/busybox/libbb/perror_nomsg.c @@ -0,0 +1,30 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_perror_nomsg implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 + +extern void bb_perror_nomsg(void) +{ + /* Ignore the gcc warning about a null format string. */ + bb_perror_msg(NULL); +} diff --git a/busybox/libbb/perror_nomsg_and_die.c b/busybox/libbb/perror_nomsg_and_die.c new file mode 100644 index 000000000..bab228455 --- /dev/null +++ b/busybox/libbb/perror_nomsg_and_die.c @@ -0,0 +1,30 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_perror_nomsg_and_die implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +extern void bb_perror_nomsg_and_die(void) +{ + /* Ignore the gcc warning about a null format string. */ + bb_perror_msg_and_die(NULL); +} diff --git a/busybox/libbb/print_file.c b/busybox/libbb/print_file.c new file mode 100644 index 000000000..963db1416 --- /dev/null +++ b/busybox/libbb/print_file.c @@ -0,0 +1,76 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_xprint_and_close_file(FILE *file) +{ + bb_xfflush_stdout(); + /* Note: Do not use STDOUT_FILENO here, as this is a lib routine + * and the calling code may have reassigned stdout. */ + if (bb_copyfd_eof(fileno(file), STDOUT_FILENO) == -1) { + /* bb_copyfd outputs any needed messages, so just die. */ + exit(bb_default_error_retval); + } + /* Note: Since we're reading, don't bother checking the return value + * of fclose(). The only possible failure is EINTR which + * should already have been taken care of. */ + fclose(file); +} + +/* Returns: + * 0 if successful + * -1 if 'filename' does not exist or is a directory + * exits with default error code if an error occurs + */ + +extern int bb_xprint_file_by_name(const char *filename) +{ + FILE *f; + +#if 0 + /* This check shouldn't be necessary for linux, but is left + * here disabled just in case. */ + struct stat statBuf; + + if(is_directory(filename, TRUE, &statBuf)) { + bb_error_msg("%s: Is directory", filename); + } else +#endif + if ((f = bb_wfopen(filename, "r")) != NULL) { + bb_xprint_and_close_file(f); + return 0; + } + + return -1; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/printf.c b/busybox/libbb/printf.c new file mode 100644 index 000000000..1156da911 --- /dev/null +++ b/busybox/libbb/printf.c @@ -0,0 +1,181 @@ +/* vi: set sw=4 ts=4: */ +/* + * *printf implementations for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Mar 12, 2003 Manuel Novoa III + * + * While fwrite(), fputc(), fputs(), etc. all set the stream error flag + * on failure, the *printf functions are unique in that they can fail + * for reasons not related to the actual output itself. Among the possible + * reasons for failure which don't set the streams error indicator, + * SUSv3 lists EILSEQ, EINVAL, and ENOMEM. + * + * In some cases, it would be desirable to have a group of *printf() + * functions available that _always_ set the stream error indicator on + * failure. That would allow us to defer error checking until applet + * exit. Unfortunately, there is no standard way of setting a streams + * error indicator... even though we can clear it with clearerr(). + * + * Therefore, we have to resort to implementation dependent code. Feel + * free to send patches for stdio implementations where the following + * fails. + * + * NOTE: None of this is thread safe. As busybox is a non-threaded app, + * that isn't currently an issue. + */ + +#include +#include +#include "libbb.h" + +#if defined(__UCLIBC__) + +# if defined(__FLAG_ERROR) +/* Using my newer stdio implementation. Unlocked macros are: + * #define __CLEARERR(stream) \ + ((stream)->modeflags &= ~(__FLAG_EOF|__FLAG_ERROR), (void)0) + * #define __FEOF(stream) ((stream)->modeflags & __FLAG_EOF) + * #define __FERROR(stream) ((stream)->modeflags & __FLAG_ERROR) + */ +# if defined(__MASK_READING) +# define SET_FERROR_UNLOCKED(S) ((S)->__modeflags |= __FLAG_ERROR) +# else +# define SET_FERROR_UNLOCKED(S) ((S)->modeflags |= __FLAG_ERROR) +# endif + +# elif defined(__MODE_ERR) +/* Using either the original stdio implementation (from dev86) or + * my original stdio rewrite. Macros were: + * #define ferror(fp) (((fp)->mode&__MODE_ERR) != 0) + * #define feof(fp) (((fp)->mode&__MODE_EOF) != 0) + * #define clearerr(fp) ((fp)->mode &= ~(__MODE_EOF|__MODE_ERR),0) + */ +#define SET_FERROR_UNLOCKED(S) ((S)->mode |= __MODE_ERR) + +# else +#error unknown uClibc stdio implemenation! +# endif + +#elif defined(__GLIBC__) + +# if defined(_STDIO_USES_IOSTREAM) +/* Apparently using the newer libio implementation, with associated defines: + * #define _IO_feof_unlocked(__fp) (((__fp)->_flags & _IO_EOF_SEEN) != 0) + * #define _IO_ferror_unlocked(__fp) (((__fp)->_flags & _IO_ERR_SEEN) != 0) + */ +#define SET_FERROR_UNLOCKED(S) ((S)->_flags |= _IO_ERR_SEEN) + +# else +/* Assume the older version of glibc which used a bitfield entry + * as a stream error flag. The associated defines were: + * #define __clearerr(stream) ((stream)->__error = (stream)->__eof = 0) + * #define feof_unlocked(stream) ((stream)->__eof != 0) + * #define ferror_unlocked(stream) ((stream)->__error != 0) + */ +#define SET_FERROR_UNLOCKED(S) ((S)->__error = 1) + +# endif + +#elif defined(__NEWLIB_H__) +/* I honestly don't know if there are different versions of stdio in + * newlibs history. Anyway, here's what's current. + * #define __sfeof(p) (((p)->_flags & __SEOF) != 0) + * #define __sferror(p) (((p)->_flags & __SERR) != 0) + * #define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF))) + */ +#define SET_FERROR_UNLOCKED(S) ((S)->_flags |= __SERR) + +#elif defined(__dietlibc__) +/* + * WARNING!!! dietlibc is quite buggy. WARNING!!! + * + * Some example bugs as of March 12, 2003... + * 1) fputc() doesn't set the error indicator on failure. + * 2) freopen() doesn't maintain the same stream object, contrary to + * standards. This makes it useless in its primary role of + * reassociating stdin/stdout/stderr. + * 3) printf() often fails to correctly format output when conversions + * involve padding. It is also practically useless for floating + * point output. + * + * But, if you're determined to use it anyway, (as of the current version) + * you can extract the information you need from dietstdio.h. See the + * other library implementations for examples. + */ +#error dietlibc is currently not supported. Please see the commented source. + +#else /* some other lib */ +/* Please see the comments for the above supported libraries for examples + * of what is required to support your stdio implementation. + */ +#error Your stdio library is currently not supported. Please see the commented source. +#endif + +#ifdef L_bb_vfprintf +extern int bb_vfprintf(FILE * __restrict stream, + const char * __restrict format, + va_list arg) +{ + int rv; + + if ((rv = vfprintf(stream, format, arg)) < 0) { + SET_FERROR_UNLOCKED(stream); + } + + return rv; +} +#endif + +#ifdef L_bb_vprintf +extern int bb_vprintf(const char * __restrict format, va_list arg) +{ + return bb_vfprintf(stdout, format, arg); +} +#endif + +#ifdef L_bb_fprintf +extern int bb_fprintf(FILE * __restrict stream, + const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = bb_vfprintf(stream, format, arg); + va_end(arg); + + return rv; +} +#endif + +#ifdef L_bb_printf +extern int bb_printf(const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = bb_vfprintf(stdout, format, arg); + va_end(arg); + + return rv; +} +#endif diff --git a/busybox/libbb/process_escape_sequence.c b/busybox/libbb/process_escape_sequence.c new file mode 100644 index 000000000..28b1e3697 --- /dev/null +++ b/busybox/libbb/process_escape_sequence.c @@ -0,0 +1,112 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) Manuel Novoa 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 +#include "libbb.h" + +#define WANT_HEX_ESCAPES 1 + +/* Usual "this only works for ascii compatible encodings" disclaimer. */ +#undef _tolower +#define _tolower(X) ((X)|((char) 0x20)) + +char bb_process_escape_sequence(const char **ptr) +{ + static const char charmap[] = { + 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', 0, + '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\\' }; + + const char *p; + const char *q; + unsigned int num_digits; + unsigned int r; + unsigned int n; + unsigned int d; + unsigned int base; + + num_digits = n = 0; + base = 8; + q = *ptr; + +#ifdef WANT_HEX_ESCAPES + if (*q == 'x') { + ++q; + base = 16; + ++num_digits; + } +#endif + + do { + d = (unsigned int)(*q - '0'); +#ifdef WANT_HEX_ESCAPES + if (d >= 10) { + d = ((unsigned int)(_tolower(*q) - 'a')) + 10; + } +#endif + + if (d >= base) { +#ifdef WANT_HEX_ESCAPES + if ((base == 16) && (!--num_digits)) { +/* return '\\'; */ + --q; + } +#endif + break; + } + + r = n * base + d; + if (r > UCHAR_MAX) { + break; + } + + n = r; + ++q; + } while (++num_digits < 3); + + if (num_digits == 0) { /* mnemonic escape sequence? */ + p = charmap; + do { + if (*p == *q) { + q++; + break; + } + } while (*++p); + n = *(p+(sizeof(charmap)/2)); + } + + *ptr = q; + + return (char) n; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/procps.c b/busybox/libbb/procps.c new file mode 100644 index 000000000..e405fb7ef --- /dev/null +++ b/busybox/libbb/procps.c @@ -0,0 +1,158 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright 1998 by Albert Cahalan; all rights reserved. + * Copyright (C) 2002 by Vladimir Oleynik + * GNU Library General Public License Version 2, or any later version + * + */ + +#include +#include +#include +#include +#include + +#include "libbb.h" + +extern procps_status_t * procps_scan(int save_user_arg0 +#ifdef CONFIG_SELINUX + , int use_selinux , security_id_t *sid +#endif + ) +{ + static DIR *dir; + struct dirent *entry; + static procps_status_t ret_status; + char *name; + int n; + char status[32]; + char buf[1024]; + FILE *fp; + procps_status_t curstatus; + int pid; + long tasknice; + struct stat sb; + + if (!dir) { + dir = opendir("/proc"); + if(!dir) + bb_error_msg_and_die("Can't open /proc"); + } + for(;;) { + if((entry = readdir(dir)) == NULL) { + closedir(dir); + dir = 0; + return 0; + } + name = entry->d_name; + if (!(*name >= '0' && *name <= '9')) + continue; + + memset(&curstatus, 0, sizeof(procps_status_t)); + pid = atoi(name); + curstatus.pid = pid; + + sprintf(status, "/proc/%d", pid); + if(stat(status, &sb)) + continue; + my_getpwuid(curstatus.user, sb.st_uid, sizeof(curstatus.user)); + + sprintf(status, "/proc/%d/stat", pid); + if((fp = fopen(status, "r")) == NULL) + continue; +#ifdef CONFIG_SELINUX + if(use_selinux) + { + if(fstat_secure(fileno(fp), &sb, sid)) + continue; + } + else +#endif + name = fgets(buf, sizeof(buf), fp); + fclose(fp); + if(name == NULL) + continue; + name = strrchr(buf, ')'); /* split into "PID (cmd" and "" */ + if(name == 0 || name[1] != ' ') + continue; + *name = 0; + sscanf(buf, "%*s (%15c", curstatus.short_cmd); + n = sscanf(name+2, + "%c %d " + "%*s %*s %*s %*s " /* pgrp, session, tty, tpgid */ + "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + "%lu %lu " +#else + "%*s %*s " +#endif + "%*s %*s %*s " /* cutime, cstime, priority */ + "%ld " + "%*s %*s %*s " /* timeout, it_real_value, start_time */ + "%*s " /* vsize */ + "%ld", + curstatus.state, &curstatus.ppid, +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + &curstatus.utime, &curstatus.stime, +#endif + &tasknice, + &curstatus.rss); +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + if(n != 6) +#else + if(n != 4) +#endif + continue; + + if (curstatus.rss == 0 && curstatus.state[0] != 'Z') + curstatus.state[1] = 'W'; + else + curstatus.state[1] = ' '; + if (tasknice < 0) + curstatus.state[2] = '<'; + else if (tasknice > 0) + curstatus.state[2] = 'N'; + else + curstatus.state[2] = ' '; + +#ifdef PAGE_SHIFT + curstatus.rss <<= (PAGE_SHIFT - 10); /* 2**10 = 1kb */ +#else + curstatus.rss *= (getpagesize() >> 10); /* 2**10 = 1kb */ +#endif + + if(save_user_arg0) { + sprintf(status, "/proc/%d/cmdline", pid); + if((fp = fopen(status, "r")) == NULL) + continue; + if((n=fread(buf, 1, sizeof(buf)-1, fp)) > 0) { + if(buf[n-1]=='\n') + buf[--n] = 0; + name = buf; + while(n) { + if(((unsigned char)*name) < ' ') + *name = ' '; + name++; + n--; + } + *name = 0; + if(buf[0]) + curstatus.cmd = strdup(buf); + /* if NULL it work true also */ + } + fclose(fp); + } + return memcpy(&ret_status, &curstatus, sizeof(procps_status_t)); + } +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/pw_encrypt.c b/busybox/libbb/pw_encrypt.c new file mode 100644 index 000000000..727149d0c --- /dev/null +++ b/busybox/libbb/pw_encrypt.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routine. + * + * Copyright (C) 1999-2004 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 "libbb.h" + + +extern char *pw_encrypt(const char *clear, const char *salt) +{ + static char cipher[128]; + char *cp; + +#ifdef CONFIG_FEATURE_SHA1_PASSWORDS + if (strncmp(salt, "$2$", 3) == 0) { + return sha1_crypt(clear); + } +#endif + cp = (char *) crypt(clear, salt); + /* if crypt (a nonstandard crypt) returns a string too large, + truncate it so we don't overrun buffers and hope there is + enough security in what's left */ + safe_strncpy(cipher, cp, sizeof(cipher)); + return cipher; +} + diff --git a/busybox/libbb/pwd2spwd.c b/busybox/libbb/pwd2spwd.c new file mode 100644 index 000000000..3dd625b27 --- /dev/null +++ b/busybox/libbb/pwd2spwd.c @@ -0,0 +1,74 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1994, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "libbb.h" +#include "shadow_.h" + +/* + * pwd_to_spwd - create entries for new spwd structure + * + * pwd_to_spwd() creates a new (struct spwd) containing the + * information in the pointed-to (struct passwd). + */ +#define DAY (24L*3600L) +#define WEEK (7*DAY) +#define SCALE DAY +struct spwd *pwd_to_spwd(const struct passwd *pw) +{ + static struct spwd sp; + + /* + * Nice, easy parts first. The name and passwd map directly + * from the old password structure to the new one. + */ + sp.sp_namp = pw->pw_name; + sp.sp_pwdp = pw->pw_passwd; + + /* + * Defaults used if there is no pw_age information. + */ + sp.sp_min = 0; + sp.sp_max = (10000L * DAY) / SCALE; + sp.sp_lstchg = time((time_t *) 0) / SCALE; + + /* + * These fields have no corresponding information in the password + * file. They are set to uninitialized values. + */ + sp.sp_warn = -1; + sp.sp_expire = -1; + sp.sp_inact = -1; + sp.sp_flag = -1; + + return &sp; +} + diff --git a/busybox/libbb/qmodule.c b/busybox/libbb/qmodule.c new file mode 100644 index 000000000..fd14d10fa --- /dev/null +++ b/busybox/libbb/qmodule.c @@ -0,0 +1,29 @@ +/* + Copyright (C) 2002 Tim Riker + everyone seems to claim it someplace. ;-) +*/ + +#include + +#include "libbb.h" + +int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret); + +int my_query_module(const char *name, int which, void **buf, + size_t *bufsize, size_t *ret) +{ + int my_ret; + + my_ret = query_module(name, which, *buf, *bufsize, ret); + + if (my_ret == -1 && errno == ENOSPC) { + *buf = xrealloc(*buf, *ret); + *bufsize = *ret; + + my_ret = query_module(name, which, *buf, *bufsize, ret); + } + + return my_ret; +} + + diff --git a/busybox/libbb/read_package_field.c b/busybox/libbb/read_package_field.c new file mode 100644 index 000000000..4292689ca --- /dev/null +++ b/busybox/libbb/read_package_field.c @@ -0,0 +1,114 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#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) { + /* Check that the names are valid */ + offset_value_end = offset; + name_length = offset_name_end - offset_name_start; + value_length = offset_value_end - offset_value_start; + if (name_length == 0) { + break; + } + if ((name_length > 0) && (value_length > 0)) { + break; + } + + /* If not valid, start fresh with next field */ + exit_flag = FALSE; + offset_name_start = offset + 1; + offset_name_end = 0; + offset_value_start = offset + 1; + offset_value_end = offset + 1; + offset++; + } + offset++; + } + if (name_length == 0) { + *field_name = NULL; + } else { + *field_name = bb_xstrndup(&package_buffer[offset_name_start], name_length); + } + if (value_length > 0) { + *field_value = bb_xstrndup(&package_buffer[offset_value_start], value_length); + } else { + *field_value = NULL; + } + return(next_offset); +} + diff --git a/busybox/libbb/recursive_action.c b/busybox/libbb/recursive_action.c new file mode 100644 index 000000000..d27629829 --- /dev/null +++ b/busybox/libbb/recursive_action.c @@ -0,0 +1,141 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 /* free() */ +#include "libbb.h" + +#undef DEBUG_RECURS_ACTION + + +/* + * Walk down all the directories under the specified + * location, and do something (something specified + * by the fileAction and dirAction function pointers). + * + * Unfortunately, while nftw(3) could replace this and reduce + * code size a bit, nftw() wasn't supported before GNU libc 2.1, + * and so isn't sufficiently portable to take over since glibc2.1 + * is so stinking huge. + */ +int recursive_action(const char *fileName, + int recurse, int followLinks, int depthFirst, + int (*fileAction) (const char *fileName, + struct stat * statbuf, + void* userData), + int (*dirAction) (const char *fileName, + struct stat * statbuf, + void* userData), + void* userData) +{ + int status; + struct stat statbuf; + struct dirent *next; + + if (followLinks) + status = stat(fileName, &statbuf); + else + status = lstat(fileName, &statbuf); + + if (status < 0) { +#ifdef DEBUG_RECURS_ACTION + bb_error_msg("status=%d followLinks=%d TRUE=%d", + status, followLinks, TRUE); +#endif + bb_perror_msg("%s", fileName); + return FALSE; + } + + if (! followLinks && (S_ISLNK(statbuf.st_mode))) { + if (fileAction == NULL) + return TRUE; + else + return fileAction(fileName, &statbuf, userData); + } + + if (! recurse) { + if (S_ISDIR(statbuf.st_mode)) { + if (dirAction != NULL) + return (dirAction(fileName, &statbuf, userData)); + else + return TRUE; + } + } + + if (S_ISDIR(statbuf.st_mode)) { + DIR *dir; + + if (dirAction != NULL && ! depthFirst) { + status = dirAction(fileName, &statbuf, userData); + if (! status) { + bb_perror_msg("%s", fileName); + return FALSE; + } else if (status == SKIP) + return TRUE; + } + dir = opendir(fileName); + if (!dir) { + bb_perror_msg("%s", fileName); + return FALSE; + } + status = TRUE; + while ((next = readdir(dir)) != NULL) { + char *nextFile; + + nextFile = concat_subpath_file(fileName, next->d_name); + if(nextFile == NULL) + continue; + if (! recursive_action(nextFile, TRUE, followLinks, depthFirst, + fileAction, dirAction, userData)) { + status = FALSE; + } + free(nextFile); + } + closedir(dir); + if (dirAction != NULL && depthFirst) { + if (! dirAction(fileName, &statbuf, userData)) { + bb_perror_msg("%s", fileName); + return FALSE; + } + } + if (! status) + return FALSE; + } else { + if (fileAction == NULL) + return TRUE; + else + return fileAction(fileName, &statbuf, userData); + } + return TRUE; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/remove_file.c b/busybox/libbb/remove_file.c new file mode 100644 index 000000000..8b45c58b8 --- /dev/null +++ b/busybox/libbb/remove_file.c @@ -0,0 +1,124 @@ +/* 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) { + bb_perror_msg("unable to stat `%s'", path); + return -1; + } + + path_exists = 0; + } + + if (!path_exists) { + if (!(flags & FILEUTILS_FORCE)) { + bb_perror_msg("cannot remove `%s'", path); + return -1; + } + return 0; + } + + if (S_ISDIR(path_stat.st_mode)) { + DIR *dp; + struct dirent *d; + int status = 0; + + if (!(flags & FILEUTILS_RECUR)) { + bb_error_msg("%s: is a directory", path); + return -1; + } + + if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && + isatty(0)) || + (flags & FILEUTILS_INTERACTIVE)) { + fprintf(stderr, "%s: descend into directory `%s'? ", bb_applet_name, + path); + if (!bb_ask_confirmation()) + return 0; + } + + if ((dp = opendir(path)) == NULL) { + bb_perror_msg("unable to open `%s'", path); + return -1; + } + + while ((d = readdir(dp)) != NULL) { + char *new_path; + + new_path = concat_subpath_file(path, d->d_name); + if(new_path == NULL) + continue; + if (remove_file(new_path, flags) < 0) + status = -1; + free(new_path); + } + + if (closedir(dp) < 0) { + bb_perror_msg("unable to close `%s'", path); + return -1; + } + + if (flags & FILEUTILS_INTERACTIVE) { + fprintf(stderr, "%s: remove directory `%s'? ", bb_applet_name, path); + if (!bb_ask_confirmation()) + return status; + } + + if (rmdir(path) < 0) { + bb_perror_msg("unable to remove `%s'", path); + return -1; + } + + return status; + } else { + if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && + !S_ISLNK(path_stat.st_mode) && + isatty(0)) || + (flags & FILEUTILS_INTERACTIVE)) { + fprintf(stderr, "%s: remove `%s'? ", bb_applet_name, path); + if (!bb_ask_confirmation()) + return 0; + } + + if (unlink(path) < 0) { + bb_perror_msg("unable to remove `%s'", path); + return -1; + } + + return 0; + } +} diff --git a/busybox/libbb/restricted_shell.c b/busybox/libbb/restricted_shell.c new file mode 100644 index 000000000..74a64140f --- /dev/null +++ b/busybox/libbb/restricted_shell.c @@ -0,0 +1,57 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "libbb.h" + + + +/* Return 1 if SHELL is a restricted shell (one not returned by + getusershell), else 0, meaning it is a standard shell. */ + +int restricted_shell ( const char *shell ) +{ + char *line; + + setusershell ( ); + while (( line = getusershell ( ))) { + if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 )) + break; + } + endusershell ( ); + return line ? 0 : 1; +} + diff --git a/busybox/libbb/run_parts.c b/busybox/libbb/run_parts.c new file mode 100644 index 000000000..4c8841fe5 --- /dev/null +++ b/busybox/libbb/run_parts.c @@ -0,0 +1,126 @@ +/* vi: set sw=4 ts=4: */ +/* + * run command from specified directory + * + * + * Copyright (C) 2001 by Emanuele Aina + * rewrite to vfork usage by + * Copyright (C) 2002 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. + * + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "libbb.h" + +/* valid_name */ +/* True or false? Is this a valid filename (upper/lower alpha, digits, + * underscores, and hyphens only?) + */ +static int valid_name(const struct dirent *d) +{ + char *c = d->d_name; + + while (*c) { + if (!isalnum(*c) && (*c != '_') && (*c != '-')) { + return 0; + } + ++c; + } + return 1; +} + +/* test mode = 1 is the same as official run_parts + * test_mode = 2 means to fail silently on missing directories + */ + +extern int run_parts(char **args, const unsigned char test_mode, char **env) +{ + struct dirent **namelist = 0; + struct stat st; + char *filename; + char *arg0 = args[0]; + int entries; + int i; + int exitstatus = 0; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &i; + (void) &exitstatus; +#endif + /* scandir() isn't POSIX, but it makes things easy. */ + entries = scandir(arg0, &namelist, valid_name, alphasort); + + if (entries == -1) { + if (test_mode & 2) { + return(2); + } + bb_perror_msg_and_die("failed to open directory %s", arg0); + } + + for (i = 0; i < entries; i++) { + + filename = concat_path_file(arg0, namelist[i]->d_name); + + if (stat(filename, &st) < 0) { + bb_perror_msg_and_die("failed to stat component %s", filename); + } + if (S_ISREG(st.st_mode) && !access(filename, X_OK)) { + if (test_mode) { + puts(filename); + } else { + /* exec_errno is common vfork variable */ + volatile int exec_errno = 0; + int result; + int pid; + + if ((pid = vfork()) < 0) { + bb_perror_msg_and_die("failed to fork"); + } else if (!pid) { + args[0] = filename; + execve(filename, args, env); + exec_errno = errno; + _exit(1); + } + + waitpid(pid, &result, 0); + if(exec_errno) { + errno = exec_errno; + bb_perror_msg("failed to exec %s", filename); + exitstatus = 1; + } + if (WIFEXITED(result) && WEXITSTATUS(result)) { + bb_perror_msg("%s exited with return code %d", filename, WEXITSTATUS(result)); + exitstatus = 1; + } else if (WIFSIGNALED(result)) { + bb_perror_msg("%s exited because of uncaught signal %d", filename, WTERMSIG(result)); + exitstatus = 1; + } + } + } + else if (!S_ISDIR(st.st_mode)) { + bb_error_msg("component %s is not an executable plain file", filename); + exitstatus = 1; + } + + free(namelist[i]); + free(filename); + } + free(namelist); + + return(exitstatus); +} diff --git a/busybox/libbb/run_shell.c b/busybox/libbb/run_shell.c new file mode 100644 index 000000000..993b4e711 --- /dev/null +++ b/busybox/libbb/run_shell.c @@ -0,0 +1,87 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "libbb.h" +#ifdef CONFIG_SELINUX +#include +#endif + +/* Run SHELL, or DEFAULT_SHELL if SHELL is empty. + If COMMAND is nonzero, pass it to the shell with the -c option. + If ADDITIONAL_ARGS is nonzero, pass it to the shell as more + arguments. */ + +void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args +#ifdef CONFIG_SELINUX + , security_id_t sid +#endif +) +{ + const char **args; + int argno = 1; + int additional_args_cnt = 0; + + for ( args = additional_args; args && *args; args++ ) + additional_args_cnt++; + + args = (const char **) xmalloc (sizeof (char *) * ( 4 + additional_args_cnt )); + + args [0] = bb_get_last_path_component ( bb_xstrdup ( shell )); + + if ( loginshell ) { + char *args0; + bb_xasprintf ( &args0, "-%s", args [0] ); + args [0] = args0; + } + + if ( command ) { + args [argno++] = "-c"; + args [argno++] = command; + } + if ( additional_args ) { + for ( ; *additional_args; ++additional_args ) + args [argno++] = *additional_args; + } + args [argno] = 0; +#ifdef CONFIG_SELINUX + if(sid) + execve_secure(shell, (char **) args, environ, sid); + else +#endif + execv ( shell, (char **) args ); + bb_perror_msg_and_die ( "cannot run %s", shell ); +} diff --git a/busybox/libbb/safe_read.c b/busybox/libbb/safe_read.c new file mode 100644 index 000000000..92e1d8a4b --- /dev/null +++ b/busybox/libbb/safe_read.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "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..2016e6b52 --- /dev/null +++ b/busybox/libbb/safe_strncpy.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + + + +/* Like strncpy but make sure the resulting string is always 0 terminated. */ +extern char * safe_strncpy(char *dst, const char *src, size_t size) +{ + dst[size-1] = '\0'; + return strncpy(dst, src, size-1); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/safe_strtol.c b/busybox/libbb/safe_strtol.c new file mode 100644 index 000000000..fcbdba878 --- /dev/null +++ b/busybox/libbb/safe_strtol.c @@ -0,0 +1,92 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +#ifdef L_safe_strtoi +extern +int safe_strtoi(char *arg, int* value) +{ + int error; + long lvalue = *value; + error = safe_strtol(arg, &lvalue); + *value = (int) lvalue; + return error; +} +#endif + +#ifdef L_safe_strtod +extern +int safe_strtod(char *arg, double* value) +{ + char *endptr; + int errno_save = errno; + + assert(arg!=NULL); + errno = 0; + *value = strtod(arg, &endptr); + if (errno != 0 || *endptr!='\0' || endptr==arg) { + return 1; + } + errno = errno_save; + return 0; +} +#endif + +#ifdef L_safe_strtol +extern +int safe_strtol(char *arg, long* value) +{ + char *endptr; + int errno_save = errno; + + assert(arg!=NULL); + errno = 0; + *value = strtol(arg, &endptr, 0); + if (errno != 0 || *endptr!='\0' || endptr==arg) { + return 1; + } + errno = errno_save; + return 0; +} +#endif + +#ifdef L_safe_strtoul +extern +int safe_strtoul(char *arg, unsigned long* value) +{ + char *endptr; + int errno_save = errno; + + assert(arg!=NULL); + errno = 0; + *value = strtoul(arg, &endptr, 0); + if (errno != 0 || *endptr!='\0' || endptr==arg) { + return 1; + } + errno = errno_save; + return 0; +} +#endif + diff --git a/busybox/libbb/safe_write.c b/busybox/libbb/safe_write.c new file mode 100644 index 000000000..201ea1cd3 --- /dev/null +++ b/busybox/libbb/safe_write.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + + + +ssize_t safe_write(int fd, const void *buf, size_t count) +{ + ssize_t n; + + do { + n = write(fd, buf, count); + } while (n < 0 && errno == EINTR); + + return n; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/setup_environment.c b/busybox/libbb/setup_environment.c new file mode 100644 index 000000000..aeb285a53 --- /dev/null +++ b/busybox/libbb/setup_environment.c @@ -0,0 +1,93 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "libbb.h" + + + +#define DEFAULT_LOGIN_PATH "/bin:/usr/bin" +#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin" + +static void xsetenv ( const char *key, const char *value ) +{ + if ( setenv ( key, value, 1 )) + bb_error_msg_and_die (bb_msg_memory_exhausted); +} + +void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ) +{ + if ( loginshell ) { + const char *term; + + /* Change the current working directory to be the home directory + * of the user. It is a fatal error for this process to be unable + * to change to that directory. There is no "default" home + * directory. + * Some systems default to HOME=/ + */ + if ( chdir ( pw-> pw_dir )) { + if ( chdir ( "/" )) { + syslog ( LOG_WARNING, "unable to cd to %s' for user %s'\n", pw-> pw_dir, pw-> pw_name ); + bb_error_msg_and_die ( "cannot cd to home directory or /" ); + } + fputs ( "warning: cannot change to home directory\n", stderr ); + } + + /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. + Unset all other environment variables. */ + term = getenv ("TERM"); + clearenv ( ); + if ( term ) + xsetenv ( "TERM", term ); + xsetenv ( "HOME", pw-> pw_dir ); + xsetenv ( "SHELL", shell ); + xsetenv ( "USER", pw-> pw_name ); + xsetenv ( "LOGNAME", pw-> pw_name ); + xsetenv ( "PATH", ( pw-> pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH )); + } + else if ( changeenv ) { + /* Set HOME, SHELL, and if not becoming a super-user, + USER and LOGNAME. */ + xsetenv ( "HOME", pw-> pw_dir ); + xsetenv ( "SHELL", shell ); + if ( pw-> pw_uid ) { + xsetenv ( "USER", pw-> pw_name ); + xsetenv ( "LOGNAME", pw-> pw_name ); + } + } +} + diff --git a/busybox/libbb/simplify_path.c b/busybox/libbb/simplify_path.c new file mode 100644 index 000000000..743133cd1 --- /dev/null +++ b/busybox/libbb/simplify_path.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_simplify_path implementation for busybox + * + * Copyright (C) 2001 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +char *bb_simplify_path(const char *path) +{ + char *s, *start, *p; + + if (path[0] == '/') + start = bb_xstrdup(path); + else { + s = xgetcwd(NULL); + start = concat_path_file(s, path); + free(s); + } + p = s = start; + + do { + if (*p == '/') { + if (*s == '/') { /* skip duplicate (or initial) slash */ + continue; + } else if (*s == '.') { + if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */ + continue; + } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) { + ++s; + if (p > start) { + while (*--p != '/'); /* omit previous dir */ + } + continue; + } + } + } + *++p = *s; + } while (*++s); + + if ((p == start) || (*p != '/')) { /* not a trailing slash */ + ++p; /* so keep last character */ + } + *p = 0; + + return start; +} diff --git a/busybox/libbb/skip_whitespace.c b/busybox/libbb/skip_whitespace.c new file mode 100644 index 000000000..bf049a2d2 --- /dev/null +++ b/busybox/libbb/skip_whitespace.c @@ -0,0 +1,33 @@ +/* vi: set sw=4 ts=4: */ +/* + * skip_whitespace implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +extern const char *bb_skip_whitespace(const char *s) +{ + while (isspace(*s)) { + ++s; + } + + return s; +} diff --git a/busybox/libbb/speed_table.c b/busybox/libbb/speed_table.c new file mode 100644 index 000000000..b04429e91 --- /dev/null +++ b/busybox/libbb/speed_table.c @@ -0,0 +1,130 @@ +/* vi: set sw=4 ts=4: */ +/* + * compact speed_t <-> speed functions for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +struct speed_map { + unsigned short speed; + unsigned short value; +}; + +static const struct speed_map speeds[] = { + {B0, 0}, + {B50, 50}, + {B75, 75}, + {B110, 110}, + {B134, 134}, + {B150, 150}, + {B200, 200}, + {B300, 300}, + {B600, 600}, + {B1200, 1200}, + {B1800, 1800}, + {B2400, 2400}, + {B4800, 4800}, + {B9600, 9600}, +#ifdef B19200 + {B19200, 19200}, +#elif defined(EXTA) + {EXTA, 19200}, +#endif +#ifdef B38400 + {B38400, 38400/256 + 0x8000U}, +#elif defined(EXTB) + {EXTB, 38400/256 + 0x8000U}, +#endif +#ifdef B57600 + {B57600, 57600/256 + 0x8000U}, +#endif +#ifdef B115200 + {B115200, 115200/256 + 0x8000U}, +#endif +#ifdef B230400 + {B230400, 230400/256 + 0x8000U}, +#endif +#ifdef B460800 + {B460800, 460800/256 + 0x8000U}, +#endif +}; + +static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map)); + +unsigned long bb_baud_to_value(speed_t speed) +{ + int i = 0; + + do { + if (speed == speeds[i].speed) { + if (speeds[i].value & 0x8000U) { + return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256; + } + return speeds[i].value; + } + } while (++i < NUM_SPEEDS); + + return 0; +} + +speed_t bb_value_to_baud(unsigned long value) +{ + int i = 0; + + do { + if (value == bb_baud_to_value(speeds[i].speed)) { + return speeds[i].speed; + } + } while (++i < NUM_SPEEDS); + + return (speed_t) - 1; +} + +#if 0 +/* testing code */ +#include + +int main(void) +{ + unsigned long v; + speed_t s; + + for (v = 0 ; v < 500000 ; v++) { + s = bb_value_to_baud(v); + if (s == (speed_t) -1) { + continue; + } + printf("v = %lu -- s = %0lo\n", v, (unsigned long) s); + } + + printf("-------------------------------\n"); + + for (s = 0 ; s < 010017+1 ; s++) { + v = bb_baud_to_value(s); + if (!v) { + continue; + } + printf("v = %lu -- s = %0lo\n", v, (unsigned long) s); + } + + return 0; +} +#endif diff --git a/busybox/libbb/syscalls.c b/busybox/libbb/syscalls.c new file mode 100644 index 000000000..9e89dbd39 --- /dev/null +++ b/busybox/libbb/syscalls.c @@ -0,0 +1,105 @@ +/* vi: set sw=4 ts=4: */ +/* + * some system calls possibly missing from libc + * + * Copyright (C) 1999-2004 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 +#include "libbb.h" + +int sysfs( int option, unsigned int fs_index, char * buf) +{ + return(syscall(__NR_sysfs, option, fs_index, buf)); +} + +int pivot_root(const char * new_root,const char * put_old) +{ +#ifndef __NR_pivot_root +#warning This kernel does not support the pivot_root syscall +#warning -> The pivot_root system call is being stubbed out... + /* BusyBox was compiled against a kernel that did not support + * the pivot_root system call. To make this application work, + * you will need to recompile with a kernel supporting the + * pivot_root system call. + */ + bb_error_msg("\n\nTo make this application work, you will need to recompile\n" + "BusyBox with a kernel supporting the pivot_root system call.\n"); + errno=ENOSYS; + return -1; +#else + return(syscall(__NR_pivot_root, new_root, put_old)); +#endif +} + + + +/* These syscalls are not included in ancient glibc versions */ +#if ((__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)) + +int bdflush(int func, int data) +{ + return(syscall(__NR_bdflush, func, data)); +} + +#ifndef __alpha__ +# define __NR_klogctl __NR_syslog +int klogctl(int type, char *b, int len) +{ + return(syscall(__NR_klogctl, type, b, len)); +} +#endif + + +int umount2(const char * special_file, int flags) +{ +#ifndef __NR_pivot_root +#warning This kernel does not support the umount2 syscall +#warning -> The umount2 system call is being stubbed out... + /* BusyBox was compiled against a kernel that did not support + * the umount2 system call. To make this application work, + * you will need to recompile with a kernel supporting the + * umount2 system call. + */ + bb_error_msg("\n\nTo make this application work, you will need to recompile\n" + "BusyBox with a kernel supporting the umount2 system call.\n"); + errno=ENOSYS; + return -1; +#else + return(syscall(__NR_umount2, special_file, flags)); +#endif +} + + +#endif + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/syslog_msg_with_name.c b/busybox/libbb/syslog_msg_with_name.c new file mode 100644 index 000000000..e69de29bb diff --git a/busybox/libbb/trim.c b/busybox/libbb/trim.c new file mode 100644 index 000000000..38aa28231 --- /dev/null +++ b/busybox/libbb/trim.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#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..be444a97b --- /dev/null +++ b/busybox/libbb/u_signal_names.c @@ -0,0 +1,189 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#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/vdprintf.c b/busybox/libbb/vdprintf.c new file mode 100644 index 000000000..53fdbd37a --- /dev/null +++ b/busybox/libbb/vdprintf.c @@ -0,0 +1,47 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "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..07b37e4ad --- /dev/null +++ b/busybox/libbb/verror_msg.c @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_verror_msg(const char *s, va_list p) +{ + fflush(stdout); + fprintf(stderr, "%s: ", bb_applet_name); + vfprintf(stderr, s, p); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/vfork_daemon_rexec.c b/busybox/libbb/vfork_daemon_rexec.c new file mode 100644 index 000000000..80022b390 --- /dev/null +++ b/busybox/libbb/vfork_daemon_rexec.c @@ -0,0 +1,78 @@ +/* + * Rexec program for system have fork() as vfork() with foreground option + * + * Copyright (C) Vladimir N. Oleynik + * Copyright (C) 2003 Russ Dill + * + * daemon() portion taken from uClibc: + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Modified for uClibc by Erik Andersen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + + +#if defined(__uClinux__) +void vfork_daemon_rexec(int nochdir, int noclose, + int argc, char **argv, char *foreground_opt) +{ + int fd; + char **vfork_args; + int a = 0; + + setsid(); + + if (!nochdir) + chdir("/"); + + if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > 2) + close(fd); + } + + vfork_args = xcalloc(sizeof(char *), argc + 3); + vfork_args[a++] = "/bin/busybox"; + while(*argv) { + vfork_args[a++] = *argv; + argv++; + } + vfork_args[a] = foreground_opt; + switch (vfork()) { + case 0: /* child */ + /* Make certain we are not a session leader, or else we + * might reacquire a controlling terminal */ + if (vfork()) + _exit(0); + execv(vfork_args[0], vfork_args); + bb_perror_msg_and_die("execv %s", vfork_args[0]); + case -1: /* error */ + bb_perror_msg_and_die("vfork"); + default: /* parent */ + exit(0); + } +} +#endif /* uClinux */ diff --git a/busybox/libbb/vherror_msg.c b/busybox/libbb/vherror_msg.c new file mode 100644 index 000000000..1560eb595 --- /dev/null +++ b/busybox/libbb/vherror_msg.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + + +extern void bb_vherror_msg(const char *s, va_list p) +{ + if(s == 0) + s = ""; + bb_verror_msg(s, p); + if (*s) + fputs(": ", stderr); + herror(""); +} diff --git a/busybox/libbb/vperror_msg.c b/busybox/libbb/vperror_msg.c new file mode 100644 index 000000000..5c446967a --- /dev/null +++ b/busybox/libbb/vperror_msg.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +extern void bb_vperror_msg(const char *s, va_list p) +{ + int err=errno; + if(s == 0) s = ""; + bb_verror_msg(s, p); + if (*s) s = ": "; + fprintf(stderr, "%s%s\n", s, strerror(err)); +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/warn_ignoring_args.c b/busybox/libbb/warn_ignoring_args.c new file mode 100644 index 000000000..a1fa528f4 --- /dev/null +++ b/busybox/libbb/warn_ignoring_args.c @@ -0,0 +1,30 @@ +/* vi: set sw=4 ts=4: */ +/* + * warn_ignoring_args implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 void bb_warn_ignoring_args(int n) +{ + if (n) { + bb_perror_msg("ignoring all arguments"); + } +} diff --git a/busybox/libbb/wfopen.c b/busybox/libbb/wfopen.c new file mode 100644 index 000000000..ab77cb19e --- /dev/null +++ b/busybox/libbb/wfopen.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + +FILE *bb_wfopen(const char *path, const char *mode) +{ + FILE *fp; + if ((fp = fopen(path, mode)) == NULL) { + bb_perror_msg("%s", path); + errno = 0; + } + return fp; +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/wfopen_input.c b/busybox/libbb/wfopen_input.c new file mode 100644 index 000000000..bff6606b5 --- /dev/null +++ b/busybox/libbb/wfopen_input.c @@ -0,0 +1,54 @@ +/* vi: set sw=4 ts=4: */ +/* + * wfopen_input implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* A number of applets need to open a file for reading, where the filename + * is a command line arg. Since often that arg is '-' (meaning stdin), + * we avoid testing everywhere by consolidating things in this routine. + * + * Note: We also consider "" to main stdin (for 'cmp' at least). + */ + +#include +#include +#include + +FILE *bb_wfopen_input(const char *filename) +{ + FILE *fp = stdin; + + if ((filename != bb_msg_standard_input) + && filename[0] && ((filename[0] != '-') || filename[1]) + ) { +#if 0 + /* This check shouldn't be necessary for linux, but is left + * here disabled just in case. */ + struct stat stat_buf; + if (is_directory(filename, 1, &stat_buf)) { + bb_error_msg("%s: Is a directory", filename); + return NULL; + } +#endif + fp = bb_wfopen(filename, "r"); + } + + return fp; +} diff --git a/busybox/libbb/xconnect.c b/busybox/libbb/xconnect.c new file mode 100644 index 000000000..09a1daad1 --- /dev/null +++ b/busybox/libbb/xconnect.c @@ -0,0 +1,71 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Connect to host at port using address resolution from getaddrinfo + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libbb.h" + +/* Return network byte ordered port number for a service. + * If "port" is a number use it as the port. + * If "port" is a name it is looked up in /etc/services, if it isnt found return + * default_port + */ +unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port) +{ + unsigned short port_nr = htons(default_port); + if (port) { + char *endptr; + int old_errno; + long port_long; + + /* Since this is a lib function, we're not allowed to reset errno to 0. + * Doing so could break an app that is deferring checking of errno. */ + old_errno = errno; + errno = 0; + port_long = strtol(port, &endptr, 10); + if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) { + struct servent *tserv = getservbyname(port, protocol); + if (tserv) { + port_nr = tserv->s_port; + } + } else { + port_nr = htons(port_long); + } + errno = old_errno; + } + return port_nr; +} + +void bb_lookup_host(struct sockaddr_in *s_in, const char *host) +{ + struct hostent *he; + + memset(s_in, 0, sizeof(struct sockaddr_in)); + s_in->sin_family = AF_INET; + he = xgethostbyname(host); + memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length); +} + +int xconnect(struct sockaddr_in *s_addr) +{ + int s = socket(AF_INET, SOCK_STREAM, 0); + if (connect(s, (struct sockaddr_in *)s_addr, sizeof(struct sockaddr_in)) < 0) + { + bb_perror_msg_and_die("Unable to connect to remote host (%s)", + inet_ntoa(s_addr->sin_addr)); + } + return s; +} diff --git a/busybox/libbb/xfuncs.c b/busybox/libbb/xfuncs.c new file mode 100644 index 000000000..01b2f87bc --- /dev/null +++ b/busybox/libbb/xfuncs.c @@ -0,0 +1,197 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 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 "libbb.h" + + +#ifndef DMALLOC +#ifdef L_xmalloc +extern void *xmalloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL && size != 0) + bb_error_msg_and_die(bb_msg_memory_exhausted); + return ptr; +} +#endif + +#ifdef L_xrealloc +extern void *xrealloc(void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + if (ptr == NULL && size != 0) + bb_error_msg_and_die(bb_msg_memory_exhausted); + return ptr; +} +#endif + +#ifdef L_xcalloc +extern void *xcalloc(size_t nmemb, size_t size) +{ + void *ptr = calloc(nmemb, size); + if (ptr == NULL && nmemb != 0 && size != 0) + bb_error_msg_and_die(bb_msg_memory_exhausted); + return ptr; +} +#endif +#endif /* DMALLOC */ + +#ifdef L_xstrdup +extern char * bb_xstrdup (const char *s) { + char *t; + + if (s == NULL) + return NULL; + + t = strdup (s); + + if (t == NULL) + bb_error_msg_and_die(bb_msg_memory_exhausted); + + return t; +} +#endif + +#ifdef L_xstrndup +extern char * bb_xstrndup (const char *s, int n) { + char *t; + + if (s == NULL) + bb_error_msg_and_die("bb_xstrndup bug"); + + t = xmalloc(++n); + + return safe_strncpy(t,s,n); +} +#endif + +#ifdef L_xfopen +FILE *bb_xfopen(const char *path, const char *mode) +{ + FILE *fp; + if ((fp = fopen(path, mode)) == NULL) + bb_perror_msg_and_die("%s", path); + return fp; +} +#endif + +#ifdef L_xopen +extern int bb_xopen(const char *pathname, int flags) +{ + int ret; + + ret = open(pathname, flags, 0777); + if (ret == -1) { + bb_perror_msg_and_die("%s", pathname); + } + return ret; +} +#endif + +#ifdef L_xread +extern ssize_t bb_xread(int fd, void *buf, size_t count) +{ + ssize_t size; + + size = read(fd, buf, count); + if (size == -1) { + bb_perror_msg_and_die("Read error"); + } + return(size); +} +#endif + +#ifdef L_xread_all +extern void bb_xread_all(int fd, void *buf, size_t count) +{ + ssize_t size; + + while (count) { + if ((size = bb_xread(fd, buf, count)) == 0) { /* EOF */ + bb_error_msg_and_die("Short read"); + } + count -= size; + buf = ((char *) buf) + size; + } + return; +} +#endif + +#ifdef L_xread_char +extern unsigned char bb_xread_char(int fd) +{ + char tmp; + + bb_xread_all(fd, &tmp, 1); + + return(tmp); +} +#endif + +#ifdef L_xferror +extern void bb_xferror(FILE *fp, const char *fn) +{ + if (ferror(fp)) { + bb_error_msg_and_die("%s", fn); + } +} +#endif + +#ifdef L_xferror_stdout +extern void bb_xferror_stdout(void) +{ + bb_xferror(stdout, bb_msg_standard_output); +} +#endif + +#ifdef L_xfflush_stdout +extern void bb_xfflush_stdout(void) +{ + if (fflush(stdout)) { + bb_perror_msg_and_die(bb_msg_standard_output); + } +} +#endif + +#ifdef L_strlen +/* Stupid gcc always includes its own builtin strlen()... */ +#undef strlen +size_t bb_strlen(const char *string) +{ + return(strlen(string)); +} +#endif + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libbb/xgetcwd.c b/busybox/libbb/xgetcwd.c new file mode 100644 index 000000000..1fcdba198 --- /dev/null +++ b/busybox/libbb/xgetcwd.c @@ -0,0 +1,48 @@ +/* + * 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 +#include "libbb.h" + +/* Amount to increase buffer size by in each try. */ +#define PATH_INCR 32 + +/* Return the current directory, newly allocated, arbitrarily long. + Return NULL and set errno on error. + If argument is not NULL (previous usage allocate memory), call free() +*/ + +char * +xgetcwd (char *cwd) +{ + char *ret; + unsigned path_max; + + path_max = (unsigned) PATH_MAX; + path_max += 2; /* The getcwd docs say to do this. */ + + if(cwd==0) + cwd = xmalloc (path_max); + + while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) { + path_max += PATH_INCR; + cwd = xrealloc (cwd, path_max); + } + + if (ret == NULL) { + free (cwd); + bb_perror_msg("getcwd()"); + return NULL; + } + + return cwd; +} diff --git a/busybox/libbb/xgethostbyname.c b/busybox/libbb/xgethostbyname.c new file mode 100644 index 000000000..6b2dff711 --- /dev/null +++ b/busybox/libbb/xgethostbyname.c @@ -0,0 +1,35 @@ +/* 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 +#include "libbb.h" + + +struct hostent *xgethostbyname(const char *name) +{ + struct hostent *retval; + + if ((retval = gethostbyname(name)) == NULL) + bb_herror_msg_and_die("%s", name); + + return retval; +} diff --git a/busybox/libbb/xgethostbyname2.c b/busybox/libbb/xgethostbyname2.c new file mode 100644 index 000000000..3a16ae4dc --- /dev/null +++ b/busybox/libbb/xgethostbyname2.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini xgethostbyname2 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 +#include "libbb.h" + + +#ifdef CONFIG_FEATURE_IPV6 +struct hostent *xgethostbyname2(const char *name, int af) +{ + struct hostent *retval; + + if ((retval = gethostbyname2(name, af)) == NULL) + bb_herror_msg_and_die("%s", name); + + return retval; +} +#endif diff --git a/busybox/libbb/xgetlarg.c b/busybox/libbb/xgetlarg.c new file mode 100644 index 000000000..56fb60e82 --- /dev/null +++ b/busybox/libbb/xgetlarg.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003-2004 Erik Andersen + */ + + +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +extern long bb_xgetlarg(const char *arg, int base, long lower, long upper) +{ + long result; + char *endptr; + int errno_save = errno; + + assert(arg!=NULL); + + /* Don't allow leading whitespace. */ + if ((isspace)(*arg)) { /* Use an actual funciton call for minimal size. */ + bb_show_usage(); + } + + errno = 0; + result = strtol(arg, &endptr, base); + if (errno != 0 || *endptr!='\0' || endptr==arg || result < lower || result > upper) + bb_show_usage(); + errno = errno_save; + return result; +} diff --git a/busybox/libbb/xgetularg.c b/busybox/libbb/xgetularg.c new file mode 100644 index 000000000..e90085446 --- /dev/null +++ b/busybox/libbb/xgetularg.c @@ -0,0 +1,160 @@ +/* vi: set sw=4 ts=4: */ +/* + * xgetularg* implementations for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +#ifdef L_xgetularg_bnd_sfx +extern +unsigned long bb_xgetularg_bnd_sfx(const char *arg, int base, + unsigned long lower, + unsigned long upper, + const struct suffix_mult *suffixes) +{ + unsigned long r; + int old_errno; + char *e; + + assert(arg); + + /* Disallow '-' and any leading whitespace. Speed isn't critical here + * since we're parsing commandline args. So make sure we get the + * actual isspace function rather than a larger macro implementaion. */ + if ((*arg == '-') || (isspace)(*arg)) { + bb_show_usage(); + } + + /* Since this is a lib function, we're not allowed to reset errno to 0. + * Doing so could break an app that is deferring checking of errno. + * So, save the old value so that we can restore it if successful. */ + old_errno = errno; + errno = 0; + r = strtoul(arg, &e, base); + /* Do the initial validity check. Note: The standards do not + * guarantee that errno is set if no digits were found. So we + * must test for this explicitly. */ + if (errno || (arg == e)) { /* error or no digits */ + bb_show_usage(); + } + errno = old_errno; /* Ok. So restore errno. */ + + /* Do optional suffix parsing. Allow 'empty' suffix tables. + * Note that we also all nul suffixes with associated multipliers, + * to allow for scaling of the arg by some default multiplier. */ + + if (suffixes) { + while (suffixes->suffix) { + if (strcmp(suffixes->suffix, e) == 0) { + if (ULONG_MAX / suffixes->mult < r) { /* Overflow! */ + bb_show_usage(); + } + ++e; + r *= suffixes->mult; + break; + } + ++suffixes; + } + } + + /* Finally, check for illegal trailing chars and range limits. */ + /* Note: although we allow leading space (via stroul), trailing space + * is an error. It would be easy enough to allow though if desired. */ + if (*e || (r < lower) || (r > upper)) { + bb_show_usage(); + } + + return r; +} +#endif + +#ifdef L_xgetlarg_bnd_sfx +extern +long bb_xgetlarg_bnd_sfx(const char *arg, int base, + long lower, + long upper, + const struct suffix_mult *suffixes) +{ + unsigned long u = LONG_MAX; + long r; + const char *p = arg; + + if ((*p == '-') && (p[1] != '+')) { + ++p; +#if LONG_MAX == (-(LONG_MIN + 1)) + ++u; /* two's complement */ +#endif + } + + r = bb_xgetularg_bnd_sfx(p, base, 0, u, suffixes); + + if (*arg == '-') { + r = -r; + } + + if ((r < lower) || (r > upper)) { + bb_show_usage(); + } + + return r; +} +#endif + +#ifdef L_getlarg10_sfx +extern +long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes) +{ + return bb_xgetlarg_bnd_sfx(arg, 10, LONG_MIN, LONG_MAX, suffixes); +} +#endif + +#ifdef L_xgetularg_bnd +extern +unsigned long bb_xgetularg_bnd(const char *arg, int base, + unsigned long lower, + unsigned long upper) +{ + return bb_xgetularg_bnd_sfx(arg, base, lower, upper, NULL); +} +#endif + +#ifdef L_xgetularg10_bnd +extern +unsigned long bb_xgetularg10_bnd(const char *arg, + unsigned long lower, + unsigned long upper) +{ + return bb_xgetularg_bnd(arg, 10, lower, upper); +} +#endif + +#ifdef L_xgetularg10 +extern +unsigned long bb_xgetularg10(const char *arg) +{ + return bb_xgetularg10_bnd(arg, 0, ULONG_MAX); +} +#endif diff --git a/busybox/libbb/xreadlink.c b/busybox/libbb/xreadlink.c new file mode 100644 index 000000000..49823fa7f --- /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) { + bb_perror_msg("%s", path); + free(buf); + return NULL; + } + } + while (bufsize < readsize + 1); + + buf[readsize] = '\0'; + + return buf; +} diff --git a/busybox/libbb/xregcomp.c b/busybox/libbb/xregcomp.c new file mode 100644 index 000000000..fa6c0fa2b --- /dev/null +++ b/busybox/libbb/xregcomp.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) many different people. + * If you wrote this, please acknowledge your work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#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); + bb_error_msg_and_die("xregcomp: %s", errmsg); + } +} + + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/libpwdgrp/Makefile b/busybox/libpwdgrp/Makefile new file mode 100644 index 000000000..c833550bf --- /dev/null +++ b/busybox/libpwdgrp/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/libpwgrp +LIBPWDGRP_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/libpwdgrp/Makefile.in b/busybox/libpwdgrp/Makefile.in new file mode 100644 index 000000000..9bdfc10d8 --- /dev/null +++ b/busybox/libpwdgrp/Makefile.in @@ -0,0 +1,53 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +LIBPWDGRP_AR:=libpwdgrp.a +ifndef $(LIBPWDGRP_DIR) +LIBPWDGRP_DIR:=$(top_builddir)/libpwdgrp/ +endif +srcdir=$(top_srcdir)/libpwdgrp + + +LIBPWDGRP_MSRC0:=$(srcdir)/pwd_grp.c +LIBPWDGRP_MOBJ0-$(CONFIG_USE_BB_PWD_GRP):= fgetpwent_r.o fgetgrent_r.o \ + fgetpwent.o fgetgrent.o getpwnam_r.o getgrnam_r.o getpwuid_r.o \ + getgrgid_r.o getpwuid.o getgrgid.o getpwnam.o getgrnam.o getpw.o \ + getpwent_r.o getgrent_r.o getpwent.o getgrent.o \ + initgroups.o putpwent.o putgrent.o +LIBPWDGRP_MOBJS0=$(patsubst %,$(LIBPWDGRP_DIR)%, $(LIBPWDGRP_MOBJ0-y)) + +LIBPWDGRP_MSRC1:=$(srcdir)/pwd_grp.c +LIBPWDGRP_MOBJ1-$(CONFIG_USE_BB_PWD_GRP):= __parsepwent.o __parsegrent.o \ + __pgsreader.o fgetspent_r.o fgetspent.o sgetspent_r.o getspnam_r.o \ + getspnam.o getspent_r.o getspent.o sgetspent.o \ + putspent.o __parsespent.o # getspuid_r.o getspuid.o +LIBPWDGRP_MOBJS1=$(patsubst %,$(LIBPWDGRP_DIR)%, $(LIBPWDGRP_MOBJ1-y)) + +libraries-y+=$(LIBPWDGRP_DIR)$(LIBPWDGRP_AR) + +$(LIBPWDGRP_DIR)$(LIBPWDGRP_AR): $(LIBPWDGRP_MOBJS0) $(LIBPWDGRP_MOBJS1) + $(AR) -ro $@ $(LIBPWDGRP_MOBJS0) $(LIBPWDGRP_MOBJS1) + +$(LIBPWDGRP_MOBJS0): $(LIBPWDGRP_MSRC0) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@ + +$(LIBPWDGRP_MOBJS1): $(LIBPWDGRP_MSRC1) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@ + + diff --git a/busybox/libpwdgrp/pwd_grp.c b/busybox/libpwdgrp/pwd_grp.c new file mode 100644 index 000000000..9412faeb4 --- /dev/null +++ b/busybox/libpwdgrp/pwd_grp.c @@ -0,0 +1,1116 @@ +/* Copyright (C) 2003 Manuel Novoa III + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Nov 6, 2003 Initial version. + * + * NOTE: This implementation is quite strict about requiring all + * field seperators. It also does not allow leading whitespace + * except when processing the numeric fields. glibc is more + * lenient. See the various glibc difference comments below. + * + * TODO: + * Move to dynamic allocation of (currently staticly allocated) + * buffers; especially for the group-related functions since + * large group member lists will cause error returns. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "pwd_.h" +#include "grp_.h" +#include "shadow_.h" + +#ifndef _PATH_SHADOW +#define _PATH_SHADOW "/etc/shadow" +#endif +#ifndef _PATH_PASSWD +#define _PATH_PASSWD "/etc/passwd" +#endif +#ifndef _PATH_GROUP +#define _PATH_GROUP "/etc/group" +#endif + +/**********************************************************************/ +/* Sizes for staticly allocated buffers. */ + +/* If you change these values, also change _SC_GETPW_R_SIZE_MAX and + * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */ +#define PWD_BUFFER_SIZE 256 +#define GRP_BUFFER_SIZE 256 + +/**********************************************************************/ +/* Prototypes for internal functions. */ + +extern int __parsepwent(void *pw, char *line); +extern int __parsegrent(void *gr, char *line); +extern int __parsespent(void *sp, char *line); + +extern int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data, + char *__restrict line_buff, size_t buflen, FILE *f); + +/**********************************************************************/ +/* For the various fget??ent_r funcs, return + * + * 0: success + * ENOENT: end-of-file encountered + * ERANGE: buflen too small + * other error values possible. See __pgsreader. + * + * Also, *result == resultbuf on success and NULL on failure. + * + * NOTE: glibc difference - For the ENOENT case, glibc also sets errno. + * We do not, as it really isn't an error if we reach the end-of-file. + * Doing so is analogous to having fgetc() set errno on EOF. + */ +/**********************************************************************/ +#ifdef L_fgetpwent_r + +int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf, + char *__restrict buffer, size_t buflen, + struct passwd **__restrict result) +{ + int rv; + + *result = NULL; + + if (!(rv = __pgsreader(__parsepwent, resultbuf, buffer, buflen, stream))) { + *result = resultbuf; + } + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_fgetgrent_r + +int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf, + char *__restrict buffer, size_t buflen, + struct group **__restrict result) +{ + int rv; + + *result = NULL; + + if (!(rv = __pgsreader(__parsegrent, resultbuf, buffer, buflen, stream))) { + *result = resultbuf; + } + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_fgetspent_r + +int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf, + char *__restrict buffer, size_t buflen, + struct spwd **__restrict result) +{ + int rv; + + *result = NULL; + + if (!(rv = __pgsreader(__parsespent, resultbuf, buffer, buflen, stream))) { + *result = resultbuf; + } + + return rv; +} + +#endif +/**********************************************************************/ +/* For the various fget??ent funcs, return NULL on failure and a + * pointer to the appropriate struct (staticly allocated) on success. + */ +/**********************************************************************/ +#ifdef L_fgetpwent + +struct passwd *fgetpwent(FILE *stream) +{ + static char buffer[PWD_BUFFER_SIZE]; + static struct passwd resultbuf; + struct passwd *result; + + fgetpwent_r(stream, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_fgetgrent + +struct group *fgetgrent(FILE *stream) +{ + static char buffer[GRP_BUFFER_SIZE]; + static struct group resultbuf; + struct group *result; + + fgetgrent_r(stream, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_fgetspent + +extern int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf, + char *__restrict buffer, size_t buflen, + struct spwd **__restrict result); +struct spwd *fgetspent(FILE *stream) +{ + static char buffer[PWD_BUFFER_SIZE]; + static struct spwd resultbuf; + struct spwd *result; + + fgetspent_r(stream, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_sgetspent_r + +int sgetspent_r(const char *string, struct spwd *result_buf, + char *buffer, size_t buflen, struct spwd **result) +{ + int rv = ERANGE; + + *result = NULL; + + if (buflen < PWD_BUFFER_SIZE) { + DO_ERANGE: + errno=rv; + goto DONE; + } + + if (string != buffer) { + if (strlen(string) >= buflen) { + goto DO_ERANGE; + } + strcpy(buffer, string); + } + + if (!(rv = __parsespent(result_buf, buffer))) { + *result = result_buf; + } + + DONE: + return rv; +} + +#endif +/**********************************************************************/ + +#ifdef GETXXKEY_R_FUNC +#error GETXXKEY_R_FUNC is already defined! +#endif + +#ifdef L_getpwnam_r +#define GETXXKEY_R_FUNC getpwnam_r +#define GETXXKEY_R_PARSER __parsepwent +#define GETXXKEY_R_ENTTYPE struct passwd +#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key)) +#define DO_GETXXKEY_R_KEYTYPE const char *__restrict +#define DO_GETXXKEY_R_PATHNAME _PATH_PASSWD +#endif + +#ifdef L_getgrnam_r +#define GETXXKEY_R_FUNC getgrnam_r +#define GETXXKEY_R_PARSER __parsegrent +#define GETXXKEY_R_ENTTYPE struct group +#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key)) +#define DO_GETXXKEY_R_KEYTYPE const char *__restrict +#define DO_GETXXKEY_R_PATHNAME _PATH_GROUP +#endif + +#ifdef L_getspnam_r +#define GETXXKEY_R_FUNC getspnam_r +#define GETXXKEY_R_PARSER __parsespent +#define GETXXKEY_R_ENTTYPE struct spwd +#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key)) +#define DO_GETXXKEY_R_KEYTYPE const char *__restrict +#define DO_GETXXKEY_R_PATHNAME _PATH_SHADOW +#endif + +#ifdef L_getpwuid_r +#define GETXXKEY_R_FUNC getpwuid_r +#define GETXXKEY_R_PARSER __parsepwent +#define GETXXKEY_R_ENTTYPE struct passwd +#define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key) +#define DO_GETXXKEY_R_KEYTYPE uid_t +#define DO_GETXXKEY_R_PATHNAME _PATH_PASSWD +#endif + +#ifdef L_getgrgid_r +#define GETXXKEY_R_FUNC getgrgid_r +#define GETXXKEY_R_PARSER __parsegrent +#define GETXXKEY_R_ENTTYPE struct group +#define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key) +#define DO_GETXXKEY_R_KEYTYPE gid_t +#define DO_GETXXKEY_R_PATHNAME _PATH_GROUP +#endif + +/**********************************************************************/ +#ifdef GETXXKEY_R_FUNC + +int GETXXKEY_R_FUNC(DO_GETXXKEY_R_KEYTYPE key, + GETXXKEY_R_ENTTYPE *__restrict resultbuf, + char *__restrict buffer, size_t buflen, + GETXXKEY_R_ENTTYPE **__restrict result) +{ + FILE *stream; + int rv; + + *result = NULL; + + if (!(stream = fopen(DO_GETXXKEY_R_PATHNAME, "r"))) { + rv = errno; + } else { + do { + if (!(rv = __pgsreader(GETXXKEY_R_PARSER, resultbuf, + buffer, buflen, stream)) + ) { + if (GETXXKEY_R_TEST(resultbuf)) { /* Found key? */ + *result = resultbuf; + break; + } + } else { + if (rv == ENOENT) { /* end-of-file encountered. */ + rv = 0; + } + break; + } + } while (1); + fclose(stream); + } + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_getpwuid + +struct passwd *getpwuid(uid_t uid) +{ + static char buffer[PWD_BUFFER_SIZE]; + static struct passwd resultbuf; + struct passwd *result; + + getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_getgrgid + +struct group *getgrgid(gid_t gid) +{ + static char buffer[GRP_BUFFER_SIZE]; + static struct group resultbuf; + struct group *result; + + getgrgid_r(gid, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_getspuid_r + +/* This function is non-standard and is currently not built. It seems + * to have been created as a reentrant version of the non-standard + * functions getspuid. Why getspuid was added, I do not know. */ + +int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf, + char *__restrict buffer, size_t buflen, + struct spwd **__restrict result) +{ + int rv; + struct passwd *pp; + struct passwd password; + char pwd_buff[PWD_BUFFER_SIZE]; + + *result = NULL; + if (!(rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp))) { + rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result); + } + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_getspuid + +/* This function is non-standard and is currently not built. + * Why it was added, I do not know. */ + +struct spwd *getspuid(uid_t uid) +{ + static char buffer[PWD_BUFFER_SIZE]; + static struct spwd resultbuf; + struct spwd *result; + + getspuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_getpwnam + +struct passwd *getpwnam(const char *name) +{ + static char buffer[PWD_BUFFER_SIZE]; + static struct passwd resultbuf; + struct passwd *result; + + getpwnam_r(name, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_getgrnam + +struct group *getgrnam(const char *name) +{ + static char buffer[GRP_BUFFER_SIZE]; + static struct group resultbuf; + struct group *result; + + getgrnam_r(name, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_getspnam + +struct spwd *getspnam(const char *name) +{ + static char buffer[PWD_BUFFER_SIZE]; + static struct spwd resultbuf; + struct spwd *result; + + getspnam_r(name, &resultbuf, buffer, sizeof(buffer), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_getpw + +int getpw(uid_t uid, char *buf) +{ + struct passwd resultbuf; + struct passwd *result; + char buffer[PWD_BUFFER_SIZE]; + + if (!buf) { + errno=EINVAL; + } else if (!getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result)) { + if (sprintf(buf, "%s:%s:%lu:%lu:%s:%s:%s\n", + resultbuf.pw_name, resultbuf.pw_passwd, + (unsigned long)(resultbuf.pw_uid), + (unsigned long)(resultbuf.pw_gid), + resultbuf.pw_gecos, resultbuf.pw_dir, + resultbuf.pw_shell) >= 0 + ) { + return 0; + } + } + + return -1; +} + +#endif +/**********************************************************************/ +#ifdef L_getpwent_r + +static FILE *pwf /*= NULL*/; +void setpwent(void) +{ + if (pwf) { + rewind(pwf); + } +} + +void endpwent(void) +{ + if (pwf) { + fclose(pwf); + pwf = NULL; + } +} + + +int getpwent_r(struct passwd *__restrict resultbuf, + char *__restrict buffer, size_t buflen, + struct passwd **__restrict result) +{ + int rv; + + *result = NULL; /* In case of error... */ + + if (!pwf) { + if (!(pwf = fopen(_PATH_PASSWD, "r"))) { + rv = errno; + goto ERR; + } + } + + if (!(rv = __pgsreader(__parsepwent, resultbuf, + buffer, buflen, pwf))) { + *result = resultbuf; + } + + ERR: + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_getgrent_r + +static FILE *grf /*= NULL*/; +void setgrent(void) +{ + if (grf) { + rewind(grf); + } +} + +void endgrent(void) +{ + if (grf) { + fclose(grf); + grf = NULL; + } +} + +int getgrent_r(struct group *__restrict resultbuf, + char *__restrict buffer, size_t buflen, + struct group **__restrict result) +{ + int rv; + + *result = NULL; /* In case of error... */ + + if (!grf) { + if (!(grf = fopen(_PATH_GROUP, "r"))) { + rv = errno; + goto ERR; + } + } + + if (!(rv = __pgsreader(__parsegrent, resultbuf, + buffer, buflen, grf))) { + *result = resultbuf; + } + + ERR: + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_getspent_r + +static FILE *spf /*= NULL*/; +void setspent(void) +{ + if (spf) { + rewind(spf); + } +} + +void endspent(void) +{ + if (spf) { + fclose(spf); + spf = NULL; + } +} + +int getspent_r(struct spwd *resultbuf, char *buffer, + size_t buflen, struct spwd **result) +{ + int rv; + + *result = NULL; /* In case of error... */ + + if (!spf) { + if (!(spf = fopen(_PATH_SHADOW, "r"))) { + rv = errno; + goto ERR; + } + } + + if (!(rv = __pgsreader(__parsespent, resultbuf, + buffer, buflen, spf))) { + *result = resultbuf; + } + + ERR: + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_getpwent + +struct passwd *getpwent(void) +{ + static char line_buff[PWD_BUFFER_SIZE]; + static struct passwd pwd; + struct passwd *result; + + getpwent_r(&pwd, line_buff, sizeof(line_buff), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_getgrent + +struct group *getgrent(void) +{ + static char line_buff[GRP_BUFFER_SIZE]; + static struct group gr; + struct group *result; + + getgrent_r(&gr, line_buff, sizeof(line_buff), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_getspent + +struct spwd *getspent(void) +{ + static char line_buff[PWD_BUFFER_SIZE]; + static struct spwd spwd; + struct spwd *result; + + getspent_r(&spwd, line_buff, sizeof(line_buff), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_sgetspent + +struct spwd *sgetspent(const char *string) +{ + static char line_buff[PWD_BUFFER_SIZE]; + static struct spwd spwd; + struct spwd *result; + + sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result); + return result; +} + +#endif +/**********************************************************************/ +#ifdef L_initgroups + +int initgroups(const char *user, gid_t gid) +{ + FILE *grf; + gid_t *group_list; + int num_groups, rv; + char **m; + struct group group; + char buff[PWD_BUFFER_SIZE]; + + rv = -1; + + /* We alloc space for 8 gids at a time. */ + if (((group_list = (gid_t *) malloc(8*sizeof(gid_t *))) != NULL) + && ((grf = fopen(_PATH_GROUP, "r")) != NULL) + ) { + + *group_list = gid; + num_groups = 1; + + while (!__pgsreader(__parsegrent, &group, buff, sizeof(buff), grf)) { + assert(group.gr_mem); /* Must have at least a NULL terminator. */ + if (group.gr_gid != gid) { + for (m=group.gr_mem ; *m ; m++) { + if (!strcmp(*m, user)) { + if (!(num_groups & 7)) { + gid_t *tmp = (gid_t *) + realloc(group_list, + (num_groups+8) * sizeof(gid_t *)); + if (!tmp) { + rv = -1; + goto DO_CLOSE; + } + group_list = tmp; + } + group_list[num_groups++] = group.gr_gid; + break; + } + } + } + } + + rv = setgroups(num_groups, group_list); + DO_CLOSE: + fclose(grf); + } + + /* group_list will be NULL if initial malloc failed, which may trigger + * warnings from various malloc debuggers. */ + free(group_list); + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_putpwent + +int putpwent(const struct passwd *__restrict p, FILE *__restrict f) +{ + int rv = -1; + + if (!p || !f) { + errno=EINVAL; + } else { + /* No extra thread locking is needed above what fprintf does. */ + if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n", + p->pw_name, p->pw_passwd, + (unsigned long)(p->pw_uid), + (unsigned long)(p->pw_gid), + p->pw_gecos, p->pw_dir, p->pw_shell) >= 0 + ) { + rv = 0; + } + } + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_putgrent + +int putgrent(const struct group *__restrict p, FILE *__restrict f) +{ + static const char format[] = ",%s"; + char **m; + const char *fmt; + int rv = -1; + + if (!p || !f) { /* Sigh... glibc checks. */ + errno=EINVAL; + } else { + if (fprintf(f, "%s:%s:%lu:", + p->gr_name, p->gr_passwd, + (unsigned long)(p->gr_gid)) >= 0 + ) { + + fmt = format + 1; + + assert(p->gr_mem); + m = p->gr_mem; + + do { + if (!*m) { + if (fputc_unlocked('\n', f) >= 0) { + rv = 0; + } + break; + } + if (fprintf(f, fmt, *m) < 0) { + break; + } + ++m; + fmt = format; + } while (1); + + } + + } + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_putspent + +static const unsigned char sp_off[] = { + offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */ + offsetof(struct spwd, sp_min), /* 3 - not a char ptr */ + offsetof(struct spwd, sp_max), /* 4 - not a char ptr */ + offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */ + offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */ + offsetof(struct spwd, sp_expire), /* 7 - not a char ptr */ +}; + +int putspent(const struct spwd *p, FILE *stream) +{ + static const char ld_format[] = "%ld:"; + const char *f; + long int x; + int i; + int rv = -1; + + /* Unlike putpwent and putgrent, glibc does not check the args. */ + if (fprintf(stream, "%s:%s:", p->sp_namp, + (p->sp_pwdp ? p->sp_pwdp : "")) < 0 + ) { + goto DO_UNLOCK; + } + + for (i=0 ; i < sizeof(sp_off) ; i++) { + f = ld_format; + if ((x = *(const long int *)(((const char *) p) + sp_off[i])) == -1) { + f += 3; + } + if (fprintf(stream, f, x) < 0) { + goto DO_UNLOCK; + } + } + + if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) { + goto DO_UNLOCK; + } + + if (fputc_unlocked('\n', stream) > 0) { + rv = 0; + } + +DO_UNLOCK: + return rv; +} + +#endif +/**********************************************************************/ +/* Internal uClibc functions. */ +/**********************************************************************/ +#ifdef L___parsepwent + +static const unsigned char pw_off[] = { + offsetof(struct passwd, pw_name), /* 0 */ + offsetof(struct passwd, pw_passwd), /* 1 */ + offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */ + offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */ + offsetof(struct passwd, pw_gecos), /* 4 */ + offsetof(struct passwd, pw_dir), /* 5 */ + offsetof(struct passwd, pw_shell) /* 6 */ +}; + +int __parsepwent(void *data, char *line) +{ + char *endptr; + char *p; + int i; + + i = 0; + do { + p = ((char *) ((struct passwd *) data)) + pw_off[i]; + + if ((i & 6) ^ 2) { /* i!=2 and i!=3 */ + *((char **) p) = line; + if (i==6) { + return 0; + } + /* NOTE: glibc difference - glibc allows omission of + * ':' seperators after the gid field if all remaining + * entries are empty. We require all separators. */ + if (!(line = strchr(line, ':'))) { + break; + } + } else { + unsigned long t = strtoul(line, &endptr, 10); + /* Make sure we had at least one digit, and that the + * failing char is the next field seperator ':'. See + * glibc difference note above. */ + /* TODO: Also check for leading whitespace? */ + if ((endptr == line) || (*endptr != ':')) { + break; + } + line = endptr; + if (i & 1) { /* i == 3 -- gid */ + *((gid_t *) p) = t; + } else { /* i == 2 -- uid */ + *((uid_t *) p) = t; + } + } + + *line++ = 0; + ++i; + } while (1); + + return -1; +} + +#endif +/**********************************************************************/ +#ifdef L___parsegrent + +static const unsigned char gr_off[] = { + offsetof(struct group, gr_name), /* 0 */ + offsetof(struct group, gr_passwd), /* 1 */ + offsetof(struct group, gr_gid) /* 2 - not a char ptr */ +}; + +int __parsegrent(void *data, char *line) +{ + char *endptr; + char *p; + int i; + char **members; + char *end_of_buf; + + end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */ + i = 0; + do { + p = ((char *) ((struct group *) data)) + gr_off[i]; + + if (i < 2) { + *((char **) p) = line; + if (!(line = strchr(line, ':'))) { + break; + } + *line++ = 0; + ++i; + } else { + *((gid_t *) p) = strtoul(line, &endptr, 10); + + /* NOTE: glibc difference - glibc allows omission of the + * trailing colon when there is no member list. We treat + * this as an error. */ + + /* Make sure we had at least one digit, and that the + * failing char is the next field seperator ':'. See + * glibc difference note above. */ + if ((endptr == line) || (*endptr != ':')) { + break; + } + + i = 1; /* Count terminating NULL ptr. */ + p = endptr; + + if (p[1]) { /* We have a member list to process. */ + /* Overwrite the last ':' with a ',' before counting. + * This allows us to test for initial ',' and adds + * one ',' so that the ',' count equals the member + * count. */ + *p = ','; + do { + /* NOTE: glibc difference - glibc allows and trims leading + * (but not trailing) space. We treat this as an error. */ + /* NOTE: glibc difference - glibc allows consecutive and + * trailing commas, and ignores "empty string" users. We + * treat this as an error. */ + if (*p == ',') { + ++i; + *p = 0; /* nul-terminate each member string. */ + if (!*++p || (*p == ',') || isspace(*p)) { + goto ERR; + } + } + } while (*++p); + } + + /* Now align (p+1), rounding up. */ + /* Assumes sizeof(char **) is a power of 2. */ + members = (char **)( (((intptr_t) p) + sizeof(char **)) + & ~((intptr_t)(sizeof(char **) - 1)) ); + + if (((char *)(members + i)) > end_of_buf) { /* No space. */ + break; + } + + ((struct group *) data)->gr_mem = members; + + if (--i) { + p = endptr; /* Pointing to char prior to first member. */ + do { + *members++ = ++p; + if (!--i) break; + while (*++p) {} + } while (1); + } + *members = NULL; + + return 0; + } + } while (1); + + ERR: + return -1; +} + +#endif +/**********************************************************************/ +#ifdef L___parsespent + +static const unsigned char sp_off[] = { + offsetof(struct spwd, sp_namp), /* 0 */ + offsetof(struct spwd, sp_pwdp), /* 1 */ + offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */ + offsetof(struct spwd, sp_min), /* 3 - not a char ptr */ + offsetof(struct spwd, sp_max), /* 4 - not a char ptr */ + offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */ + offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */ + offsetof(struct spwd, sp_expire), /* 7 - not a char ptr */ + offsetof(struct spwd, sp_flag) /* 8 - not a char ptr */ +}; + +int __parsespent(void *data, char * line) +{ + char *endptr; + char *p; + int i; + + i = 0; + do { + p = ((char *) ((struct spwd *) data)) + sp_off[i]; + if (i < 2) { + *((char **) p) = line; + if (!(line = strchr(line, ':'))) { + break; + } + } else { +#if 0 + if (i==5) { /* Support for old format. */ + while (isspace(*line)) ++line; /* glibc eats space here. */ + if (!*line) { + ((struct spwd *) data)->sp_warn = -1; + ((struct spwd *) data)->sp_inact = -1; + ((struct spwd *) data)->sp_expire = -1; + ((struct spwd *) data)->sp_flag = ~0UL; + return 0; + } + } +#endif + + *((long *) p) = (long) strtoul(line, &endptr, 10); + + if (endptr == line) { + *((long *) p) = ((i != 8) ? -1L : ((long)(~0UL))); + } + + line = endptr; + + if (i == 8) { + if (!*endptr) { + return 0; + } + break; + } + + if (*endptr != ':') { + break; + } + + } + + *line++ = 0; + ++i; + } while (1); + + return EINVAL; +} + +#endif +/**********************************************************************/ +#ifdef L___pgsreader + +/* Reads until if EOF, or until if finds a line which fits in the buffer + * and for which the parser function succeeds. + * + * Returns 0 on success and ENOENT for end-of-file (glibc concession). + */ + +int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data, + char *__restrict line_buff, size_t buflen, FILE *f) +{ + int line_len; + int skip; + int rv = ERANGE; + + if (buflen < PWD_BUFFER_SIZE) { + errno=rv; + } else { + skip = 0; + do { + if (!fgets_unlocked(line_buff, buflen, f)) { + if (feof_unlocked(f)) { + rv = ENOENT; + } + break; + } + + line_len = strlen(line_buff) - 1; /* strlen() must be > 0. */ + if (line_buff[line_len] == '\n') { + line_buff[line_len] = 0; + } else if (line_len + 2 == buflen) { /* line too long */ + ++skip; + continue; + } + + if (skip) { + --skip; + continue; + } + + /* NOTE: glibc difference - glibc strips leading whitespace from + * records. We do not allow leading whitespace. */ + + /* Skip empty lines, comment lines, and lines with leading + * whitespace. */ + if (*line_buff && (*line_buff != '#') && !isspace(*line_buff)) { + if (__parserfunc == __parsegrent) { /* Do evil group hack. */ + /* The group entry parsing function needs to know where + * the end of the buffer is so that it can construct the + * group member ptr table. */ + ((struct group *) data)->gr_name = line_buff + buflen; + } + + if (!__parserfunc(data, line_buff)) { + rv = 0; + break; + } + } + } while (1); + + } + + return rv; +} + +#endif +/**********************************************************************/ diff --git a/busybox/loginutils/Config.in b/busybox/loginutils/Config.in new file mode 100644 index 000000000..5619aa9af --- /dev/null +++ b/busybox/loginutils/Config.in @@ -0,0 +1,161 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Login/Password Management Utilities" + +config CONFIG_USE_BB_PWD_GRP + bool "Use internal password and group functions rather than system functions" + default n + help + If you leave this disabled, busybox will use the system's password + and group functions. And if you are using the GNU C library + (glibc), you will then need to install the /etc/nsswitch.conf + configuration file and the required /lib/libnss_* libraries in + order for the password and group functions to work. This generally + makes your embedded system quite a bit larger. + + Enabling this option will cause busybox to directly access the + system's /etc/password, /etc/group files (and your system will be + smaller, and I will get fewer emails asking about how glibc NSS + works). When this option is enabled, you will not be able to use + PAM to access remote LDAP password servers and whatnot. And if you + want hostname resolution to work with glibc, you still need the + /lib/libnss_* libraries. + + If you enable this option, it will add about 1.5k to busybox. + + +config CONFIG_ADDGROUP + bool "addgroup" + default n + help + Utility for creating a new group account. + +config CONFIG_DELGROUP + bool "delgroup" + default n + help + Utility for deleting a group account. + +config CONFIG_ADDUSER + bool "adduser" + default n + help + Utility for creating a new user account. + +config CONFIG_DELUSER + bool "deluser" + default n + help + Utility for deleting a user account. + +config CONFIG_GETTY + bool "getty" + default n + help + getty lets you log in on a tty, it is normally invoked by init. + +config CONFIG_FEATURE_U_W_TMP + bool " Support utmp and wtmp files" + depends on CONFIG_GETTY || CONFIG_LOGIN || CONFIG_SU || CONFIG_WHO || CONFIG_LAST + default n + help + The files /var/run/utmp and /var/run/wtmp can be used to track when + user's have logged into and logged out of the system, allowing programs + such as 'who' and 'last' to list who is currently logged in. + +config CONFIG_LOGIN + bool "login" + default n + select CONFIG_FEATURE_SUID + help + login is used when signing onto a system. + + Note that Busybox binary must be setuid root for this applet to + work properly. + +config CONFIG_FEATURE_SECURETTY + bool " Support for /etc/securetty" + default y + depends on CONFIG_LOGIN + help + The file /etc/securetty is used by (some versions of) login(1). + The file contains the device names of tty lines (one per line, + without leading /dev/) on which root is allowed to login. + +config CONFIG_PASSWD + bool "passwd" + default n + select CONFIG_FEATURE_SUID + help + passwd changes passwords for user and group accounts. A normal user + may only change the password for his/her own account, the super user + may change the password for any account. The administrator of a group + may change the password for the group. + + Note that Busybox binary must be setuid root for this applet to + work properly. + +config CONFIG_SU + bool "su" + default n + select CONFIG_FEATURE_SUID + help + su is used to become another user during a login session. + Invoked without a username, su defaults to becoming the super user. + + Note that Busybox binary must be setuid root for this applet to + work properly. + +config CONFIG_SULOGIN + bool "sulogin" + default n + help + sulogin is invoked when the system goes into single user + mode (this is done through an entry in inittab). + +config CONFIG_VLOCK + bool "vlock" + default n + select CONFIG_FEATURE_SUID + help + Build the "vlock" applet which allows you to lock (virtual) terminals. + + Note that Busybox binary must be setuid root for this applet to + work properly. + +comment "Common options for adduser, deluser, login, su" + depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU + +config CONFIG_FEATURE_SHADOWPASSWDS + bool "Support for shadow passwords" + default n + depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU + help + Build support for shadow password in /etc/shadow. This file is only + readable by root and thus the encrypted passwords are no longer + publicly readable. + +config CONFIG_USE_BB_SHADOW + bool " Use busybox shadow password functions" + default n + depends on CONFIG_USE_BB_PWD_GRP && CONFIG_FEATURE_SHADOWPASSWDS + help + If you leave this disabled, busybox will use the system's shadow + password handling functions. And if you are using the GNU C library + (glibc), you will then need to install the /etc/nsswitch.conf + configuration file and the required /lib/libnss_* libraries in + order for the shadow password functions to work. This generally + makes your embedded system quite a bit larger. + + Enabling this option will cause busybox to directly access the + system's /etc/shadow file when handling shadow passwords. This + makes your system smaller and I will get fewer emails asking about + how glibc NSS works). When this option is enabled, you will not be + able to use PAM to access shadow passwords from remote LDAP + password servers and whatnot. + +endmenu + diff --git a/busybox/loginutils/Makefile b/busybox/loginutils/Makefile new file mode 100644 index 000000000..98226ae81 --- /dev/null +++ b/busybox/loginutils/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/loginutils +LOGINUTILS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/loginutils/Makefile.in b/busybox/loginutils/Makefile.in new file mode 100644 index 000000000..96a61e60f --- /dev/null +++ b/busybox/loginutils/Makefile.in @@ -0,0 +1,57 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +LOGINUTILS_AR:=loginutils.a +ifndef LOGINUTILS_DIR +LOGINUTILS_DIR:=$(top_builddir)/loginutils/ +endif +srcdir=$(top_srcdir)/loginutils + +LOGINUTILS-y:= +LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o +LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o +LOGINUTILS-$(CONFIG_GETTY) += getty.o +LOGINUTILS-$(CONFIG_LOGIN) += login.o +LOGINUTILS-$(CONFIG_PASSWD) += passwd.o +LOGINUTILS-$(CONFIG_SU) += su.o +LOGINUTILS-$(CONFIG_SULOGIN) += sulogin.o +LOGINUTILS-$(CONFIG_VLOCK) += vlock.o +LOGINUTILS-$(CONFIG_DELUSER) += deluser.o +LOGINUTILS-$(CONFIG_DELGROUP) += delgroup.o + +libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR) + +needcrypt-y:= +needcrypt-$(CONFIG_LOGIN) := y +needcrypt-$(CONFIG_PASSWD) := y +needcrypt-$(CONFIG_SU) := y +needcrypt-$(CONFIG_SULOGIN) := y +needcrypt-$(CONFIG_VLOCK) := y + + +ifeq ($(needcrypt-y),y) + LIBRARIES += -lcrypt +endif + +$(LOGINUTILS_DIR)$(LOGINUTILS_AR): $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y)) + $(AR) -ro $@ $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y)) + +$(LOGINUTILS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/loginutils/addgroup.c b/busybox/loginutils/addgroup.c new file mode 100644 index 000000000..804d6961c --- /dev/null +++ b/busybox/loginutils/addgroup.c @@ -0,0 +1,171 @@ +/* vi: set sw=4 ts=4: */ +/* + * addgroup - add users to /etc/passwd and /etc/shadow + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" +#include "pwd_.h" +#include "grp_.h" + + +/* structs __________________________ */ + +/* data _____________________________ */ + +/* defaults : should this be in an external file? */ +static const char default_passwd[] = "x"; + + +/* make sure gr_name isn't taken, make sure gid is kosher + * return 1 on failure */ +static int group_study(const char *filename, struct group *g) +{ + FILE *etc_group; + gid_t desired; + + struct group *grp; + const int max = 65000; + + etc_group = bb_xfopen(filename, "r"); + + /* make sure gr_name isn't taken, make sure gid is kosher */ + desired = g->gr_gid; + while ((grp = fgetgrent(etc_group))) { + if ((strcmp(grp->gr_name, g->gr_name)) == 0) { + bb_error_msg_and_die("%s: group already in use\n", g->gr_name); + } + if ((desired) && grp->gr_gid == desired) { + bb_error_msg_and_die("%d: gid has already been allocated\n", + desired); + } + if ((grp->gr_gid > g->gr_gid) && (grp->gr_gid < max)) { + g->gr_gid = grp->gr_gid; + } + } + fclose(etc_group); + + /* gid */ + if (desired) { + g->gr_gid = desired; + } else { + g->gr_gid++; + } + /* return 1; */ + return 0; +} + +/* append a new user to the passwd file */ +static int addgroup(const char *filename, char *group, gid_t gid, const char *user) +{ + FILE *etc_group; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + FILE *etc_gshadow; +#endif + + struct group gr; + + /* group:passwd:gid:userlist */ + static const char entryfmt[] = "%s:%s:%d:%s\n"; + + /* make sure gid and group haven't already been allocated */ + gr.gr_gid = gid; + gr.gr_name = group; + if (group_study(filename, &gr)) + return 1; + + /* add entry to group */ + etc_group = bb_xfopen(filename, "a"); + + fprintf(etc_group, entryfmt, group, default_passwd, gr.gr_gid, user); + fclose(etc_group); + + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + /* add entry to gshadow if necessary */ + if (access(bb_path_gshadow_file, F_OK|W_OK) == 0) { + etc_gshadow = bb_xfopen(bb_path_gshadow_file, "a"); + fprintf(etc_gshadow, "%s:!::\n", group); + fclose(etc_gshadow); + } +#endif + + /* return 1; */ + return 0; +} + +#ifndef CONFIG_ADDUSER +static inline void if_i_am_not_root(void) +{ + if (geteuid()) { + bb_error_msg_and_die( "Only root may add a user or group to the system."); + } +} +#else +extern void if_i_am_not_root(void); +#endif + +/* + * addgroup will take a login_name as its first parameter. + * + * gid + * + * can be customized via command-line parameters. + * ________________________________________________________________________ */ +int addgroup_main(int argc, char **argv) +{ + char *group; + char *user; + gid_t gid = 0; + + /* get remaining args */ + if(bb_getopt_ulflags(argc, argv, "g:", &group)) { + gid = bb_xgetlarg(group, 10, 0, LONG_MAX); + } + + if (optind < argc) { + group = argv[optind]; + optind++; + } else { + bb_show_usage(); + } + + if (optind < argc) { + user = argv[optind]; + } else { + user = ""; + } + + if_i_am_not_root(); + + /* werk */ + return addgroup(bb_path_group_file, group, gid, user); +} diff --git a/busybox/loginutils/adduser.c b/busybox/loginutils/adduser.c new file mode 100644 index 000000000..7fa05a013 --- /dev/null +++ b/busybox/loginutils/adduser.c @@ -0,0 +1,314 @@ +/* vi: set sw=4 ts=4: */ +/* + * adduser - add users to /etc/passwd and /etc/shadow + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + + +/* structs __________________________ */ + +typedef struct { + uid_t u; + gid_t g; +} Id; + +/* data _____________________________ */ + +/* defaults : should this be in an external file? */ +static const char default_passwd[] = "x"; +static const char default_gecos[] = "Linux User,,,"; +static const char default_home_prefix[] = "/home"; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS +/* shadow in use? */ +static int shadow_enabled = 0; +#endif + +/* remix */ +/* EDR recoded such that the uid may be passed in *p */ +static int passwd_study(const char *filename, struct passwd *p) +{ + struct passwd *pw; + FILE *passwd; + + const int min = 500; + const int max = 65000; + + passwd = bb_wfopen(filename, "r"); + if (!passwd) + return 4; + + /* EDR if uid is out of bounds, set to min */ + if ((p->pw_uid > max) || (p->pw_uid < min)) + p->pw_uid = min; + + /* stuff to do: + * make sure login isn't taken; + * find free uid and gid; + */ + while ((pw = fgetpwent(passwd))) { + if (strcmp(pw->pw_name, p->pw_name) == 0) { + /* return 0; */ + return 1; + } + if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max) + && (pw->pw_uid >= min)) { + p->pw_uid = pw->pw_uid + 1; + } + } + + if (p->pw_gid == 0) { + /* EDR check for an already existing gid */ + while (getgrgid(p->pw_uid) != NULL) + p->pw_uid++; + + /* EDR also check for an existing group definition */ + if (getgrnam(p->pw_name) != NULL) + return 3; + + /* EDR create new gid always = uid */ + p->pw_gid = p->pw_uid; + } + + /* EDR bounds check */ + if ((p->pw_uid > max) || (p->pw_uid < min)) + return 2; + + /* return 1; */ + return 0; +} + +static void addgroup_wrapper(const char *login, gid_t gid) +{ + char *cmd; + + bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login); + system(cmd); + free(cmd); +} + +static void passwd_wrapper(const char *login) __attribute__ ((noreturn)); + +static void passwd_wrapper(const char *login) +{ + static const char prog[] = "passwd"; + execlp(prog, prog, login, NULL); + bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login); +} + +/* putpwent(3) remix */ +static int adduser(const char *filename, struct passwd *p, int makehome, int setpass) +{ + FILE *passwd; + int r; +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + FILE *shadow; + struct spwd *sp; +#endif + int new_group = 1; + + /* if using a pre-existing group, don't create one */ + if (p->pw_gid != 0) + new_group = 0; + + /* make sure everything is kosher and setup uid && gid */ + passwd = bb_wfopen(filename, "a"); + if (passwd == NULL) { + return 1; + } + fseek(passwd, 0, SEEK_END); + + /* if (passwd_study(filename, p) == 0) { */ + r = passwd_study(filename, p); + if (r) { + if (r == 1) + bb_error_msg("%s: login already in use", p->pw_name); + else if (r == 2) + bb_error_msg("illegal uid or no uids left"); + else if (r == 3) + bb_error_msg("group name %s already in use", p->pw_name); + else + bb_error_msg("generic error."); + return 1; + } + + /* add to passwd */ + if (putpwent(p, passwd) == -1) { + return 1; + } + fclose(passwd); + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + /* add to shadow if necessary */ + if (shadow_enabled) { + shadow = bb_wfopen(bb_path_shadow_file, "a"); + if (shadow == NULL) { + return 1; + } + fseek(shadow, 0, SEEK_END); + sp = pwd_to_spwd(p); + sp->sp_max = 99999; /* debianish */ + sp->sp_warn = 7; + fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n", + sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max, + sp->sp_warn); + fclose(shadow); + } +#endif + + if (new_group) { + /* add to group */ + /* addgroup should be responsible for dealing w/ gshadow */ + addgroup_wrapper(p->pw_name, p->pw_gid); + } + + /* Clear the umask for this process so it doesn't + * * screw up the permissions on the mkdir and chown. */ + umask(0); + + if (makehome) { + /* mkdir */ + if (mkdir(p->pw_dir, 0755)) { + bb_perror_msg("%s", p->pw_dir); + } + /* Set the owner and group so it is owned by the new user. */ + if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) { + bb_perror_msg("%s", p->pw_dir); + } + /* Now fix up the permissions to 2755. Can't do it before now + * since chown will clear the setgid bit */ + if (chmod(p->pw_dir, 02755)) { + bb_perror_msg("%s", p->pw_dir); + } + } + + if (setpass) { + /* interactively set passwd */ + passwd_wrapper(p->pw_name); + } + + return 0; +} + + +/* return current uid (root is always uid == 0, right?) */ +#ifndef CONFIG_ADDGROUP +static inline void if_i_am_not_root(void) +#else +void if_i_am_not_root(void) +#endif +{ + if (geteuid()) { + bb_error_msg_and_die( "Only root may add a user or group to the system."); + } +} + +#define SETPASS (1 << 4) +#define MAKEHOME (1 << 6) + +/* + * adduser will take a login_name as its first parameter. + * + * home + * shell + * gecos + * + * can be customized via command-line parameters. + * ________________________________________________________________________ */ +int adduser_main(int argc, char **argv) +{ + struct passwd pw; + const char *login; + const char *gecos = default_gecos; + const char *home = NULL; + const char *shell = DEFAULT_SHELL; + const char *usegroup = NULL; + int flags; + int setpass = 1; + int makehome = 1; + + /* init */ + if (argc < 2) { + bb_show_usage(); + } + /* get args */ + flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &home, &gecos, &shell, &usegroup); + + if (flags & SETPASS) { + setpass = 0; + } + if (flags & MAKEHOME) { + makehome = 0; + } + + /* got root? */ + if_i_am_not_root(); + + /* get login */ + if (optind >= argc) { + bb_error_msg_and_die( "no user specified"); + } + login = argv[optind]; + + /* create string for $HOME if not specified already */ + if (!home) { + home = concat_path_file(default_home_prefix, login); + } +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + /* is /etc/shadow in use? */ + shadow_enabled = (0 == access(bb_path_shadow_file, F_OK)); +#endif + + /* create a passwd struct */ + pw.pw_name = (char *)login; + pw.pw_passwd = (char *)default_passwd; + pw.pw_uid = 0; + pw.pw_gid = 0; + pw.pw_gecos = (char *)gecos; + pw.pw_dir = (char *)home; + pw.pw_shell = (char *)shell; + + if (usegroup) { + /* Add user to a group that already exists */ + pw.pw_gid = my_getgrnam(usegroup); + /* exits on error */ + } + + /* grand finale */ + return adduser(bb_path_passwd_file, &pw, makehome, setpass); +} diff --git a/busybox/loginutils/delgroup.c b/busybox/loginutils/delgroup.c new file mode 100644 index 000000000..91edf2989 --- /dev/null +++ b/busybox/loginutils/delgroup.c @@ -0,0 +1,62 @@ +/* vi: set sw=4 ts=4: */ +/* + * deluser (remove lusers from the system ;) for TinyLogin + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + + +#if ! defined CONFIG_DELUSER +#include "delline.c" +#else +extern int del_line_matching(const char *login, const char *filename); +#endif + +int delgroup_main(int argc, char **argv) +{ + /* int successful; */ + int failure; + + if (argc != 2) { + bb_show_usage(); + } else { + + failure = del_line_matching(argv[1], bb_path_group_file); +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + if (access(bb_path_gshadow_file, W_OK) == 0) { + /* EDR the |= works if the error is not 0, so he had it wrong */ + failure |= del_line_matching(argv[1], bb_path_gshadow_file); + } +#endif + if (failure) { + bb_error_msg_and_die("%s: Group could not be removed\n", argv[1]); + } + + } + return (EXIT_SUCCESS); +} + +/* $Id: delgroup.c,v 1.2 2003/07/14 21:50:51 andersen Exp $ */ diff --git a/busybox/loginutils/delline.c b/busybox/loginutils/delline.c new file mode 100644 index 000000000..8d534c861 --- /dev/null +++ b/busybox/loginutils/delline.c @@ -0,0 +1,113 @@ +/* vi: set sw=4 ts=4: */ +/* + * deluser (remove lusers from the system ;) for TinyLogin + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + + + +/* where to start and stop deletion */ +typedef struct { + size_t start; + size_t stop; +} Bounds; + +/* An interesting side-effect of boundary()'s + * implementation is that the first user (typically root) + * cannot be removed. Let's call it a feature. */ +static inline Bounds boundary(const char *buffer, const char *login) +{ + char needle[256]; + char *start; + char *stop; + Bounds b; + + snprintf(needle, 256, "\n%s:", login); + needle[255] = 0; + start = strstr(buffer, needle); + if (!start) { + b.start = 0; + b.stop = 0; + return b; + } + start++; + + stop = index(start, '\n'); /* index is a BSD-ism */ + b.start = start - buffer; + b.stop = stop - buffer; + return b; +} + +/* grep -v ^login (except it only deletes the first match) */ +/* ...in fact, I think I'm going to simplify this later */ +int del_line_matching(const char *login, const char *filename) +{ + char *buffer; + FILE *passwd; + size_t len; + Bounds b; + struct stat statbuf; + + /* load into buffer */ + passwd = fopen(filename, "r"); + if (!passwd) { + return 1; + } + stat(filename, &statbuf); + len = statbuf.st_size; + buffer = (char *) malloc(len * sizeof(char)); + + if (!buffer) { + fclose(passwd); + return 1; + } + fread(buffer, len, sizeof(char), passwd); + + fclose(passwd); + + /* find the user to remove */ + b = boundary(buffer, login); + if (b.stop == 0) { + free(buffer); + return 1; + } + + /* write the file w/o the user */ + passwd = fopen(filename, "w"); + if (!passwd) { + return 1; + } + fwrite(buffer, (b.start - 1), sizeof(char), passwd); + fwrite(&buffer[b.stop], (len - b.stop), sizeof(char), passwd); + + fclose(passwd); + + return 0; +} + + +/* $Id: delline.c,v 1.2 2003/07/14 21:50:51 andersen Exp $ */ diff --git a/busybox/loginutils/deluser.c b/busybox/loginutils/deluser.c new file mode 100644 index 000000000..1cd2b01e3 --- /dev/null +++ b/busybox/loginutils/deluser.c @@ -0,0 +1,68 @@ +/* vi: set sw=4 ts=4: */ +/* + * deluser (remove lusers from the system ;) for TinyLogin + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + + +#include "delline.c" + +static const char deluser_format[]="%s: User could not be removed from %s"; + +int deluser_main(int argc, char **argv) +{ + /* int successful; */ + int failure; + + if (argc != 2) { + bb_show_usage(); + } else { + + failure = del_line_matching(argv[1], bb_path_passwd_file); + if (failure) { + bb_error_msg_and_die(deluser_format, argv[1], bb_path_passwd_file); + } +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + failure = del_line_matching(argv[1], bb_path_shadow_file); + if (failure) { + bb_error_msg_and_die(deluser_format, argv[1], bb_path_shadow_file); + } + failure = del_line_matching(argv[1], bb_path_gshadow_file); + if (failure) { + bb_error_msg_and_die(deluser_format, argv[1], bb_path_gshadow_file); + } +#endif + failure = del_line_matching(argv[1], bb_path_group_file); + if (failure) { + bb_error_msg_and_die(deluser_format, argv[1], bb_path_group_file); + } + + } + return (EXIT_SUCCESS); +} + +/* $Id: deluser.c,v 1.4 2003/07/14 20:20:45 andersen Exp $ */ diff --git a/busybox/loginutils/getty.c b/busybox/loginutils/getty.c new file mode 100644 index 000000000..923432ba1 --- /dev/null +++ b/busybox/loginutils/getty.c @@ -0,0 +1,1020 @@ +/* vi: set sw=4 ts=4: */ +/* agetty.c - another getty program for Linux. By W. Z. Venema 1989 + Ported to Linux by Peter Orbaek + This program is freely distributable. The entire man-page used to + be here. Now read the real man-page agetty.8 instead. + + -f option added by Eric Rasmussen - 12/28/95 + + 1999-02-22 Arkadiusz Mi¶kiewicz + - added Native Language Support + + 1999-05-05 Thorsten Kranzkowski + - enable hardware flow control before displaying /etc/issue + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define _PATH_LOGIN "/bin/login" + + /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */ +#ifdef CONFIG_SYSLOGD +#include +#define USE_SYSLOG +#include +#endif + + + /* + * Some heuristics to find out what environment we are in: if it is not + * System V, assume it is SunOS 4. + */ + +#ifdef LOGIN_PROCESS /* defined in System V utmp.h */ +#define SYSV_STYLE /* select System V style getty */ +#ifdef CONFIG_FEATURE_U_W_TMP +extern void updwtmp(const char *filename, const struct utmp *ut); +#endif +#endif /* LOGIN_PROCESS */ + + /* + * Things you may want to modify. + * + * If ISSUE is not defined, agetty will never display the contents of the + * /etc/issue file. You will not want to spit out large "issue" files at the + * wrong baud rate. Relevant for System V only. + * + * You may disagree with the default line-editing etc. characters defined + * below. Note, however, that DEL cannot be used for interrupt generation + * and for line editing at the same time. + */ + +#ifdef SYSV_STYLE +#define ISSUE "/etc/issue" /* displayed before the login prompt */ +#include +#include +#endif + +/* Some shorthands for control characters. */ + +#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */ +#define CR CTL('M') /* carriage return */ +#define NL CTL('J') /* line feed */ +#define BS CTL('H') /* back space */ +#define DEL CTL('?') /* delete */ + +/* Defaults for line-editing etc. characters; you may want to change this. */ + +#define DEF_ERASE DEL /* default erase character */ +#define DEF_INTR CTL('C') /* default interrupt character */ +#define DEF_QUIT CTL('\\') /* default quit char */ +#define DEF_KILL CTL('U') /* default kill char */ +#define DEF_EOF CTL('D') /* default EOF char */ +#define DEF_EOL 0 +#define DEF_SWITCH 0 /* default switch char */ + + /* + * SunOS 4.1.1 termio is broken. We must use the termios stuff instead, + * because the termio -> termios translation does not clear the termios + * CIBAUD bits. Therefore, the tty driver would sometimes report that input + * baud rate != output baud rate. I did not notice that problem with SunOS + * 4.1. We will use termios where available, and termio otherwise. + */ + +/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set + properly, but all is well if we use termios?! */ + +#ifdef TCGETS +#undef TCGETA +#undef TCSETA +#undef TCSETAW +#define termio termios +#define TCGETA TCGETS +#define TCSETA TCSETS +#define TCSETAW TCSETSW +#endif + + /* + * This program tries to not use the standard-i/o library. This keeps the + * executable small on systems that do not have shared libraries (System V + * Release <3). + */ +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + + /* + * When multiple baud rates are specified on the command line, the first one + * we will try is the first one specified. + */ + +#define FIRST_SPEED 0 + +/* Storage for command-line options. */ + +#define MAX_SPEED 10 /* max. nr. of baud rates */ + +struct options { + int flags; /* toggle switches, see below */ + int timeout; /* time-out period */ + char *login; /* login program */ + char *tty; /* name of tty */ + char *initstring; /* modem init string */ + char *issue; /* alternative issue file */ + int numspeed; /* number of baud rates to try */ + int speeds[MAX_SPEED]; /* baud rates to be tried */ +}; + +#define F_PARSE (1<<0) /* process modem status messages */ +#define F_ISSUE (1<<1) /* display /etc/issue */ +#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ +#define F_LOCAL (1<<3) /* force local */ +#define F_INITSTRING (1<<4) /* initstring is set */ +#define F_WAITCRLF (1<<5) /* wait for CR or LF */ +#define F_CUSTISSUE (1<<6) /* give alternative issue file */ +#define F_NOPROMPT (1<<7) /* don't ask for login name! */ + +/* Storage for things detected while the login name was read. */ + +struct chardata { + int erase; /* erase character */ + int kill; /* kill character */ + int eol; /* end-of-line character */ + int parity; /* what parity did we see */ + int capslock; /* upper case without lower case */ +}; + +/* Initial values for the above. */ + +struct chardata init_chardata = { + DEF_ERASE, /* default erase character */ + DEF_KILL, /* default kill character */ + 13, /* default eol char */ + 0, /* space parity */ + 0, /* no capslock */ +}; + +#if 0 +struct Speedtab { + long speed; + int code; +}; + +static struct Speedtab speedtab[] = { + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, +#ifdef B19200 + {19200, B19200}, +#endif +#ifdef B38400 + {38400, B38400}, +#endif +#ifdef EXTA + {19200, EXTA}, +#endif +#ifdef EXTB + {38400, EXTB}, +#endif +#ifdef B57600 + {57600, B57600}, +#endif +#ifdef B115200 + {115200, B115200}, +#endif +#ifdef B230400 + {230400, B230400}, +#endif + {0, 0}, +}; +#endif + +static void parse_args(int argc, char **argv, struct options *op); +static void parse_speeds(struct options *op, char *arg); +static void open_tty(char *tty, struct termio *tp, int local); +static void termio_init(struct termio *tp, int speed, struct options *op); +static void auto_baud(struct termio *tp); +static void do_prompt(struct options *op, struct termio *tp); +static void next_speed(struct termio *tp, struct options *op); +static char *get_logname(struct options *op, struct chardata *cp, + + struct termio *tp); +static void termio_final(struct options *op, struct termio *tp, + + struct chardata *cp); +static int caps_lock(const char *s); +static int bcode(char *s); +static void error(const char *fmt, ...) __attribute__ ((noreturn)); + +#ifdef CONFIG_FEATURE_U_W_TMP +static void update_utmp(char *line); +#endif + +/* The following is used for understandable diagnostics. */ + +/* Fake hostname for ut_host specified on command line. */ +static char *fakehost = NULL; + +/* ... */ +#ifdef DEBUGGING +#define debug(s) fprintf(dbf,s); fflush(dbf) +#define DEBUGTERM "/dev/ttyp0" +FILE *dbf; +#else +#define debug(s) /* nothing */ +#endif + +int getty_main(int argc, char **argv) +{ + char *logname = NULL; /* login name, given to /bin/login */ + struct chardata chardata; /* set by get_logname() */ + struct termio termio; /* terminal mode bits */ + static struct options options = { + F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ + 0, /* no timeout */ + _PATH_LOGIN, /* default login program */ + "tty1", /* default tty line */ + "", /* modem init string */ + ISSUE, /* default issue file */ + 0, /* no baud rates known yet */ + }; + +#ifdef DEBUGGING + dbf = bb_xfopen(DEBUGTERM, "w"); + + { + int i; + + for (i = 1; i < argc; i++) { + debug(argv[i]); + debug("\n"); + } + } +#endif + + /* Parse command-line arguments. */ + + parse_args(argc, argv, &options); + +#ifdef __linux__ + setsid(); +#endif + + /* Update the utmp file. */ + + +#ifdef SYSV_STYLE +#ifdef CONFIG_FEATURE_U_W_TMP + update_utmp(options.tty); +#endif +#endif + + debug("calling open_tty\n"); + /* Open the tty as standard { input, output, error }. */ + open_tty(options.tty, &termio, options.flags & F_LOCAL); + +#ifdef __linux__ + { + int iv; + + iv = getpid(); + ioctl(0, TIOCSPGRP, &iv); + } +#endif + /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */ + debug("calling termio_init\n"); + termio_init(&termio, options.speeds[FIRST_SPEED], &options); + + /* write the modem init string and DON'T flush the buffers */ + if (options.flags & F_INITSTRING) { + debug("writing init string\n"); + write(1, options.initstring, strlen(options.initstring)); + } + + if (!(options.flags & F_LOCAL)) { + /* go to blocking write mode unless -L is specified */ + fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK); + } + + /* Optionally detect the baud rate from the modem status message. */ + debug("before autobaud\n"); + if (options.flags & F_PARSE) + auto_baud(&termio); + + /* Set the optional timer. */ + if (options.timeout) + (void) alarm((unsigned) options.timeout); + + /* optionally wait for CR or LF before writing /etc/issue */ + if (options.flags & F_WAITCRLF) { + char ch; + + debug("waiting for cr-lf\n"); + while (read(0, &ch, 1) == 1) { + ch &= 0x7f; /* strip "parity bit" */ +#ifdef DEBUGGING + fprintf(dbf, "read %c\n", ch); +#endif + if (ch == '\n' || ch == '\r') + break; + } + } + + chardata = init_chardata; + if (!(options.flags & F_NOPROMPT)) { + /* Read the login name. */ + debug("reading login name\n"); + /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */ + while ((logname = get_logname(&options, &chardata, &termio)) == + NULL) next_speed(&termio, &options); + } + + /* Disable timer. */ + + if (options.timeout) + (void) alarm(0); + + /* Finalize the termio settings. */ + + termio_final(&options, &termio, &chardata); + + /* Now the newline character should be properly written. */ + + (void) write(1, "\n", 1); + + /* Let the login program take care of password validation. */ + + (void) execl(options.login, options.login, "--", logname, (char *) 0); + error("%s: can't exec %s: %m", options.tty, options.login); +} + +/* parse-args - parse command-line arguments */ + +static void parse_args(int argc, char **argv, struct options *op) +{ + extern char *optarg; /* getopt */ + extern int optind; /* getopt */ + int c; + + while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) { + switch (c) { + case 'I': + if (!(op->initstring = strdup(optarg))) + error(bb_msg_memory_exhausted); + + { + const char *p; + char *q; + + /* copy optarg into op->initstring decoding \ddd + octal codes into chars */ + q = op->initstring; + p = optarg; + while (*p) { + if (*p == '\\') { + p++; + *q++ = bb_process_escape_sequence(&p); + } else { + *q++ = *p++; + } + } + *q = '\0'; + } + op->flags |= F_INITSTRING; + break; + + case 'L': /* force local */ + op->flags |= F_LOCAL; + break; + case 'H': /* fake login host */ + fakehost = optarg; + break; + case 'f': /* custom issue file */ + op->flags |= F_CUSTISSUE; + op->issue = optarg; + break; + case 'h': /* enable h/w flow control */ + op->flags |= F_RTSCTS; + break; + case 'i': /* do not show /etc/issue */ + op->flags &= ~F_ISSUE; + break; + case 'l': + op->login = optarg; /* non-default login program */ + break; + case 'm': /* parse modem status message */ + op->flags |= F_PARSE; + break; + case 'n': + op->flags |= F_NOPROMPT; + break; + case 't': /* time out */ + if ((op->timeout = atoi(optarg)) <= 0) + error("bad timeout value: %s", optarg); + break; + case 'w': + op->flags |= F_WAITCRLF; + break; + default: + bb_show_usage(); + } + } + debug("after getopt loop\n"); + if (argc < optind + 2) /* check parameter count */ + bb_show_usage(); + + /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ + if ('0' <= argv[optind][0] && argv[optind][0] <= '9') { + /* a number first, assume it's a speed (BSD style) */ + parse_speeds(op, argv[optind++]); /* baud rate(s) */ + op->tty = argv[optind]; /* tty name */ + } else { + op->tty = argv[optind++]; /* tty name */ + parse_speeds(op, argv[optind]); /* baud rate(s) */ + } + + optind++; + if (argc > optind && argv[optind]) + setenv("TERM", argv[optind], 1); + + debug("exiting parseargs\n"); +} + +/* parse_speeds - parse alternate baud rates */ + +static void parse_speeds(struct options *op, char *arg) +{ + char *cp; + + debug("entered parse_speeds\n"); + for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { + if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) + error("bad speed: %s", cp); + if (op->numspeed > MAX_SPEED) + error("too many alternate speeds"); + } + debug("exiting parsespeeds\n"); +} + +#ifdef SYSV_STYLE +#ifdef CONFIG_FEATURE_U_W_TMP + +/* update_utmp - update our utmp entry */ +static void update_utmp(char *line) +{ + struct utmp ut; + struct utmp *utp; + time_t t; + int mypid = getpid(); +#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)) + struct flock lock; +#endif + + /* + * The utmp file holds miscellaneous information about things started by + * /sbin/init and other system-related events. Our purpose is to update + * the utmp entry for the current process, in particular the process type + * and the tty line we are listening to. Return successfully only if the + * utmp file can be opened for update, and if we are able to find our + * entry in the utmp file. + */ + if (access(_PATH_UTMP, R_OK|W_OK) == -1) { + close(creat(_PATH_UTMP, 0664)); + } + utmpname(_PATH_UTMP); + setutent(); + while ((utp = getutent()) + && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */ + ; + + if (utp) { + memcpy(&ut, utp, sizeof(ut)); + } else { + /* some inits don't initialize utmp... */ + memset(&ut, 0, sizeof(ut)); + strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); + } + /*endutent(); */ + + strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user)); + strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + if (fakehost) + strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); + time(&t); + ut.ut_time = t; + ut.ut_type = LOGIN_PROCESS; + ut.ut_pid = mypid; + + pututline(&ut); + endutent(); + + { + if (access(_PATH_WTMP, R_OK|W_OK) == -1) { + close(creat(_PATH_WTMP, 0664)); + } + updwtmp(_PATH_WTMP, &ut); + } +} + +#endif /* CONFIG_FEATURE_U_W_TMP */ +#endif /* SYSV_STYLE */ + +/* open_tty - set up tty as standard { input, output, error } */ +static void open_tty(char *tty, struct termio *tp, int local) +{ + /* Get rid of the present standard { output, error} if any. */ + + (void) close(1); + (void) close(2); + errno = 0; /* ignore above errors */ + + /* Set up new standard input, unless we are given an already opened port. */ + + if (strcmp(tty, "-")) { + struct stat st; + + /* Sanity checks... */ + + if (chdir("/dev")) + error("/dev: chdir() failed: %m"); + if (stat(tty, &st) < 0) + error("/dev/%s: %m", tty); + if ((st.st_mode & S_IFMT) != S_IFCHR) + error("/dev/%s: not a character device", tty); + + /* Open the tty as standard input. */ + + (void) close(0); + errno = 0; /* ignore close(2) errors */ + + debug("open(2)\n"); + if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0) + error("/dev/%s: cannot open as standard input: %m", tty); + + } else { + + /* + * Standard input should already be connected to an open port. Make + * sure it is open for read/write. + */ + + if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR) + error("%s: not open for read/write", tty); + } + + /* Set up standard output and standard error file descriptors. */ + debug("duping\n"); + if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */ + error("%s: dup problem: %m", tty); /* we have a problem */ + + /* + * The following ioctl will fail if stdin is not a tty, but also when + * there is noise on the modem control lines. In the latter case, the + * common course of action is (1) fix your cables (2) give the modem more + * time to properly reset after hanging up. SunOS users can achieve (2) + * by patching the SunOS kernel variable "zsadtrlow" to a larger value; + * 5 seconds seems to be a good value. + */ + + if (ioctl(0, TCGETA, tp) < 0) + error("%s: ioctl: %m", tty); + + /* + * It seems to be a terminal. Set proper protections and ownership. Mode + * 0622 is suitable for SYSV <4 because /bin/login does not change + * protections. SunOS 4 login will change the protections to 0620 (write + * access for group tty) after the login has succeeded. + */ + +#ifdef DEBIAN + { + /* tty to root.dialout 660 */ + struct group *gr; + int id; + + id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0; + chown(tty, 0, id); + chmod(tty, 0660); + + /* vcs,vcsa to root.sys 600 */ + if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) { + char *vcs, *vcsa; + + if (!(vcs = strdup(tty))) + error("Can't malloc for vcs"); + if (!(vcsa = malloc(strlen(tty) + 2))) + error("Can't malloc for vcsa"); + strcpy(vcs, "vcs"); + strcpy(vcs + 3, tty + 3); + strcpy(vcsa, "vcsa"); + strcpy(vcsa + 4, tty + 3); + + id = (gr = getgrnam("sys")) ? gr->gr_gid : 0; + chown(vcs, 0, id); + chmod(vcs, 0600); + chown(vcsa, 0, id); + chmod(vcs, 0600); + + free(vcs); + free(vcsa); + } + } +#else + (void) chown(tty, 0, 0); /* root, sys */ + (void) chmod(tty, 0622); /* crw--w--w- */ + errno = 0; /* ignore above errors */ +#endif +} + +/* termio_init - initialize termio settings */ + +static void termio_init(struct termio *tp, int speed, struct options *op) +{ + + /* + * Initial termio settings: 8-bit characters, raw-mode, blocking i/o. + * Special characters are set after we have read the login name; all + * reads will be done in raw mode anyway. Errors will be dealt with + * lateron. + */ +#ifdef __linux__ + /* flush input and output queues, important for modems! */ + (void) ioctl(0, TCFLSH, TCIOFLUSH); +#endif + + tp->c_cflag = CS8 | HUPCL | CREAD | speed; + if (op->flags & F_LOCAL) { + tp->c_cflag |= CLOCAL; + } + + tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0; + tp->c_cc[VMIN] = 1; + tp->c_cc[VTIME] = 0; + + /* Optionally enable hardware flow control */ + +#ifdef CRTSCTS + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; +#endif + + (void) ioctl(0, TCSETA, tp); + + /* go to blocking input even in local mode */ + fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK); + + debug("term_io 2\n"); +} + +/* auto_baud - extract baud rate from modem status message */ +static void auto_baud(struct termio *tp) +{ + int speed; + int vmin; + unsigned iflag; + char buf[BUFSIZ]; + char *bp; + int nread; + + /* + * This works only if the modem produces its status code AFTER raising + * the DCD line, and if the computer is fast enough to set the proper + * baud rate before the message has gone by. We expect a message of the + * following format: + * + * + * + * The number is interpreted as the baud rate of the incoming call. If the + * modem does not tell us the baud rate within one second, we will keep + * using the current baud rate. It is advisable to enable BREAK + * processing (comma-separated list of baud rates) if the processing of + * modem status messages is enabled. + */ + + /* + * Use 7-bit characters, don't block if input queue is empty. Errors will + * be dealt with lateron. + */ + + iflag = tp->c_iflag; + tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ + vmin = tp->c_cc[VMIN]; + tp->c_cc[VMIN] = 0; /* don't block if queue empty */ + (void) ioctl(0, TCSETA, tp); + + /* + * Wait for a while, then read everything the modem has said so far and + * try to extract the speed of the dial-in call. + */ + + (void) sleep(1); + if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) { + buf[nread] = '\0'; + for (bp = buf; bp < buf + nread; bp++) { + if (isascii(*bp) && isdigit(*bp)) { + if ((speed = bcode(bp))) { + tp->c_cflag &= ~CBAUD; + tp->c_cflag |= speed; + } + break; + } + } + } + /* Restore terminal settings. Errors will be dealt with lateron. */ + + tp->c_iflag = iflag; + tp->c_cc[VMIN] = vmin; + (void) ioctl(0, TCSETA, tp); +} + +/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ +static void do_prompt(struct options *op, struct termio *tp) +{ +#ifdef ISSUE /* optional: show /etc/issue */ + print_login_issue(op->issue, op->tty); +#endif + print_login_prompt(); +} + +/* next_speed - select next baud rate */ +static void next_speed(struct termio *tp, struct options *op) +{ + static int baud_index = FIRST_SPEED; /* current speed index */ + + baud_index = (baud_index + 1) % op->numspeed; + tp->c_cflag &= ~CBAUD; + tp->c_cflag |= op->speeds[baud_index]; + (void) ioctl(0, TCSETA, tp); +} + +/* get_logname - get user name, establish parity, speed, erase, kill, eol */ +/* return NULL on failure, logname on success */ +static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp) +{ + static char logname[BUFSIZ]; + char *bp; + char c; /* input character, full eight bits */ + char ascval; /* low 7 bits of input character */ + int bits; /* # of "1" bits per character */ + int mask; /* mask with 1 bit up */ + static char *erase[] = { /* backspace-space-backspace */ + "\010\040\010", /* space parity */ + "\010\040\010", /* odd parity */ + "\210\240\210", /* even parity */ + "\210\240\210", /* no parity */ + }; + + /* Initialize kill, erase, parity etc. (also after switching speeds). */ + + *cp = init_chardata; + + /* Flush pending input (esp. after parsing or switching the baud rate). */ + + (void) sleep(1); + (void) ioctl(0, TCFLSH, TCIFLUSH); + + /* Prompt for and read a login name. */ + + for (*logname = 0; *logname == 0; /* void */ ) { + + /* Write issue file and prompt, with "parity" bit == 0. */ + + do_prompt(op, tp); + + /* Read name, watch for break, parity, erase, kill, end-of-line. */ + + for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) { + + /* Do not report trivial EINTR/EIO errors. */ + + if (read(0, &c, 1) < 1) { + if (errno == EINTR || errno == EIO) + exit(0); + error("%s: read: %m", op->tty); + } + /* Do BREAK handling elsewhere. */ + + if ((c == 0) && op->numspeed > 1) + /* return (0); */ + return NULL; + + /* Do parity bit handling. */ + + if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */ + for (bits = 1, mask = 1; mask & 0177; mask <<= 1) + if (mask & ascval) + bits++; /* count "1" bits */ + cp->parity |= ((bits & 1) ? 1 : 2); + } + /* Do erase, kill and end-of-line processing. */ + + switch (ascval) { + case CR: + case NL: + *bp = 0; /* terminate logname */ + cp->eol = ascval; /* set end-of-line char */ + break; + case BS: + case DEL: + case '#': + cp->erase = ascval; /* set erase character */ + if (bp > logname) { + (void) write(1, erase[cp->parity], 3); + bp--; + } + break; + case CTL('U'): + case '@': + cp->kill = ascval; /* set kill character */ + while (bp > logname) { + (void) write(1, erase[cp->parity], 3); + bp--; + } + break; + case CTL('D'): + exit(0); + default: + if (!isascii(ascval) || !isprint(ascval)) { + /* ignore garbage characters */ ; + } else if (bp - logname >= sizeof(logname) - 1) { + error("%s: input overrun", op->tty); + } else { + (void) write(1, &c, 1); /* echo the character */ + *bp++ = ascval; /* and store it */ + } + break; + } + } + } + /* Handle names with upper case and no lower case. */ + + if ((cp->capslock = caps_lock(logname))) { + for (bp = logname; *bp; bp++) + if (isupper(*bp)) + *bp = tolower(*bp); /* map name to lower case */ + } + return (logname); +} + +/* termio_final - set the final tty mode bits */ +static void termio_final(struct options *op, struct termio *tp, struct chardata *cp) +{ + /* General terminal-independent stuff. */ + + tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ + tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; + /* no longer| ECHOCTL | ECHOPRT */ + tp->c_oflag |= OPOST; + /* tp->c_cflag = 0; */ + tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */ + tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */ + tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */ + tp->c_cc[VEOL] = DEF_EOL; + tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ + + /* Account for special characters seen in input. */ + + if (cp->eol == CR) { + tp->c_iflag |= ICRNL; /* map CR in input to NL */ + tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ + } + tp->c_cc[VERASE] = cp->erase; /* set erase character */ + tp->c_cc[VKILL] = cp->kill; /* set kill character */ + + /* Account for the presence or absence of parity bits in input. */ + + switch (cp->parity) { + case 0: /* space (always 0) parity */ + break; + case 1: /* odd parity */ + tp->c_cflag |= PARODD; + /* FALLTHROUGH */ + case 2: /* even parity */ + tp->c_cflag |= PARENB; + tp->c_iflag |= INPCK | ISTRIP; + /* FALLTHROUGH */ + case (1 | 2): /* no parity bit */ + tp->c_cflag &= ~CSIZE; + tp->c_cflag |= CS7; + break; + } + /* Account for upper case without lower case. */ + + if (cp->capslock) { + tp->c_iflag |= IUCLC; + tp->c_lflag |= XCASE; + tp->c_oflag |= OLCUC; + } + /* Optionally enable hardware flow control */ + +#ifdef CRTSCTS + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; +#endif + + /* Finally, make the new settings effective */ + + if (ioctl(0, TCSETA, tp) < 0) + error("%s: ioctl: TCSETA: %m", op->tty); +} + +/* caps_lock - string contains upper case without lower case */ +/* returns 1 if true, 0 if false */ +static int caps_lock(const char *s) +{ + int capslock; + + for (capslock = 0; *s; s++) { + if (islower(*s)) + return (0); + if (capslock == 0) + capslock = isupper(*s); + } + return (capslock); +} + +/* bcode - convert speed string to speed code; return 0 on failure */ +static int bcode(char *s) +{ + int r; + unsigned long value; + if (safe_strtoul(s, &value)) { + return -1; + } + if ((r = bb_value_to_baud(value)) > 0) { + return r; + } + return 0; +} + +/* error - report errors to console or syslog; only understands %s and %m */ + +#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2) + +/* + * output error messages + */ +static void error(const char *fmt, ...) +{ + va_list va_alist; + char buf[256], *bp; + +#ifndef USE_SYSLOG + int fd; +#endif + +#ifdef USE_SYSLOG + buf[0] = '\0'; + bp = buf; +#else + strncpy(buf, bb_applet_name, 256); + strncat(buf, ": ", 256); + buf[255] = 0; + bp = buf + strlen(buf); +#endif + + va_start(va_alist, fmt); + vsnprintf(bp, 256 - strlen(buf), fmt, va_alist); + buf[255] = 0; + va_end(va_alist); + +#ifdef USE_SYSLOG + openlog(bb_applet_name, 0, LOG_AUTH); + syslog(LOG_ERR, "%s", buf); + closelog(); +#else + strncat(bp, "\r\n", 256 - strlen(buf)); + buf[255] = 0; + if ((fd = open("/dev/console", 1)) >= 0) { + write(fd, buf, strlen(buf)); + close(fd); + } +#endif + (void) sleep((unsigned) 10); /* be kind to init(8) */ + exit(1); +} diff --git a/busybox/loginutils/login.c b/busybox/loginutils/login.c new file mode 100644 index 000000000..f3630f102 --- /dev/null +++ b/busybox/loginutils/login.c @@ -0,0 +1,486 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" +#ifdef CONFIG_SELINUX +#include +#include +#include +#include +#endif + +#ifdef CONFIG_FEATURE_U_W_TMP +// import from utmp.c +static void checkutmp(int picky); +static void setutmp(const char *name, const char *line); +/* Stuff global to this file */ +struct utmp utent; +#endif + +// login defines +#define TIMEOUT 60 +#define EMPTY_USERNAME_COUNT 10 +#define USERNAME_SIZE 32 + + +static int check_nologin ( int amroot ); + +#if defined CONFIG_FEATURE_SECURETTY +static int check_tty ( const char *tty ); + +#else +static inline int check_tty ( const char *tty ) { return 1; } + +#endif + +static int is_my_tty ( const char *tty ); +static int login_prompt ( char *buf_name ); +static void motd ( void ); + + +static void alarm_handler ( int sig ) +{ + fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT ); + exit ( EXIT_SUCCESS ); +} + + +extern int login_main(int argc, char **argv) +{ + char tty[BUFSIZ]; + char full_tty[200]; + char fromhost[512]; + char username[USERNAME_SIZE]; + const char *tmp; + int amroot; + int flag; + int failed; + int count=0; + struct passwd *pw, pw_copy; +#ifdef CONFIG_WHEEL_GROUP + struct group *grp; +#endif + int opt_preserve = 0; + int opt_fflag = 0; + char *opt_host = 0; + int alarmstarted = 0; +#ifdef CONFIG_SELINUX + int flask_enabled = is_flask_enabled(); + security_id_t sid = 0, old_tty_sid, new_tty_sid; +#endif + + username[0]=0; + amroot = ( getuid ( ) == 0 ); + signal ( SIGALRM, alarm_handler ); + alarm ( TIMEOUT ); + alarmstarted = 1; + + while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) { + switch ( flag ) { + case 'p': + opt_preserve = 1; + break; + case 'f': + /* + * username must be a separate token + * (-f root, *NOT* -froot). --marekm + */ + if ( optarg != argv[optind-1] ) + bb_show_usage( ); + + if ( !amroot ) /* Auth bypass only if real UID is zero */ + bb_error_msg_and_die ( "-f permission denied" ); + + safe_strncpy(username, optarg, USERNAME_SIZE); + opt_fflag = 1; + break; + case 'h': + opt_host = optarg; + break; + default: + bb_show_usage( ); + } + } + + if (optind < argc) // user from command line (getty) + safe_strncpy(username, argv[optind], USERNAME_SIZE); + + if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 )) + return EXIT_FAILURE; /* Must be a terminal */ + +#ifdef CONFIG_FEATURE_U_W_TMP + checkutmp ( !amroot ); +#endif + + tmp = ttyname ( 0 ); + if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 )) + safe_strncpy ( tty, tmp + 5, sizeof( tty )); + else if ( tmp && *tmp == '/' ) + safe_strncpy ( tty, tmp, sizeof( tty )); + else + safe_strncpy ( tty, "UNKNOWN", sizeof( tty )); + +#ifdef CONFIG_FEATURE_U_W_TMP + if ( amroot ) + memset ( utent.ut_host, 0, sizeof utent.ut_host ); +#endif + + if ( opt_host ) { +#ifdef CONFIG_FEATURE_U_W_TMP + safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host )); +#endif + snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host ); + } + else + snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty ); + + setpgrp(); + + openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH ); + + while ( 1 ) { + failed = 0; + + if ( !username[0] ) + if(!login_prompt ( username )) + return EXIT_FAILURE; + + if ( !alarmstarted && ( TIMEOUT > 0 )) { + alarm ( TIMEOUT ); + alarmstarted = 1; + } + + if (!( pw = getpwnam ( username ))) { + pw_copy.pw_name = "UNKNOWN"; + pw_copy.pw_passwd = "!"; + opt_fflag = 0; + failed = 1; + } else + pw_copy = *pw; + + pw = &pw_copy; + + if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' )) + failed = 1; + + if ( opt_fflag ) { + opt_fflag = 0; + goto auth_ok; + } + + if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty ))) + failed = 1; + + /* Don't check the password if password entry is empty (!) */ + if ( !pw-> pw_passwd[0] ) + goto auth_ok; + + /* authorization takes place here */ + if ( correct_password ( pw )) + goto auth_ok; + + failed = 1; + +auth_ok: + if ( !failed) + break; + + { // delay next try + time_t start, now; + + time ( &start ); + now = start; + while ( difftime ( now, start ) < FAIL_DELAY) { + sleep ( FAIL_DELAY ); + time ( &now ); + } + } + + puts("Login incorrect"); + username[0] = 0; + if ( ++count == 3 ) { + syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost); + return EXIT_FAILURE; + } + } + + alarm ( 0 ); + if ( check_nologin ( pw-> pw_uid == 0 )) + return EXIT_FAILURE; + +#ifdef CONFIG_FEATURE_U_W_TMP + setutmp ( username, tty ); +#endif +#ifdef CONFIG_SELINUX + if (flask_enabled) + { + struct stat st; + + if (get_default_sid(username, 0, &sid)) + { + fprintf(stderr, "Unable to get SID for %s\n", username); + exit(1); + } + if (stat_secure(tty, &st, &old_tty_sid)) + { + fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno)); + return EXIT_FAILURE; + } + if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0) + { + fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno)); + return EXIT_FAILURE; + } + if(chsid(tty, new_tty_sid) != 0) + { + fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno)); + return EXIT_FAILURE; + } + } + else + sid = 0; +#endif + + if ( *tty != '/' ) + snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty); + else + safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 ); + + if ( !is_my_tty ( full_tty )) + syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty ); + + /* Try these, but don't complain if they fail + * (for example when the root fs is read only) */ + chown ( full_tty, pw-> pw_uid, pw-> pw_gid ); + chmod ( full_tty, 0600 ); + + change_identity ( pw ); + tmp = pw-> pw_shell; + if(!tmp || !*tmp) + tmp = DEFAULT_SHELL; + setup_environment ( tmp, 1, !opt_preserve, pw ); + + motd ( ); + signal ( SIGALRM, SIG_DFL ); /* default alarm signal */ + + if ( pw-> pw_uid == 0 ) + syslog ( LOG_INFO, "root login %s\n", fromhost ); + run_shell ( tmp, 1, 0, 0 +#ifdef CONFIG_SELINUX + , sid +#endif + ); /* exec the shell finally. */ + + return EXIT_FAILURE; +} + + + +static int login_prompt ( char *buf_name ) +{ + char buf [1024]; + char *sp, *ep; + int i; + + for(i=0; i= 0; --i ) { + if ( !isspace ( buf[i] )) + break; + } + buf[++i] = '\0'; + if (( buf [0] == '\0' ) || ( buf [0] == '#' )) + continue; + + if ( strcmp ( buf, tty ) == 0 ) { + fclose ( fp ); + return 1; + } + } + fclose(fp); + return 0; + } + /* A missing securetty file is not an error. */ + return 1; +} + +#endif + +/* returns 1 if true */ +static int is_my_tty ( const char *tty ) +{ + struct stat by_name, by_fd; + + if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd )) + return 0; + + if ( by_name. st_rdev != by_fd. st_rdev ) + return 0; + else + return 1; +} + + +static void motd ( ) +{ + FILE *fp; + register int c; + + if (( fp = fopen ( bb_path_motd_file, "r" ))) { + while (( c = getc ( fp )) != EOF ) + putchar ( c ); + fclose ( fp ); + } +} + + +#ifdef CONFIG_FEATURE_U_W_TMP +// vv Taken from tinylogin utmp.c vv + +#define NO_UTENT \ + "No utmp entry. You must exec \"login\" from the lowest level \"sh\"" +#define NO_TTY \ + "Unable to determine your tty name." + +/* + * checkutmp - see if utmp file is correct for this process + * + * System V is very picky about the contents of the utmp file + * and requires that a slot for the current process exist. + * The utmp file is scanned for an entry with the same process + * ID. If no entry exists the process exits with a message. + * + * The "picky" flag is for network and other logins that may + * use special flags. It allows the pid checks to be overridden. + * This means that getty should never invoke login with any + * command line flags. + */ + +static void checkutmp(int picky) +{ + char *line; + struct utmp *ut; + pid_t pid = getpid(); + + setutent(); + + /* First, try to find a valid utmp entry for this process. */ + while ((ut = getutent())) + if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] && + (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS)) + break; + + /* If there is one, just use it, otherwise create a new one. */ + if (ut) { + utent = *ut; + } else { + if (picky) { + puts(NO_UTENT); + exit(1); + } + line = ttyname(0); + if (!line) { + puts(NO_TTY); + exit(1); + } + if (strncmp(line, "/dev/", 5) == 0) + line += 5; + memset((void *) &utent, 0, sizeof utent); + utent.ut_type = LOGIN_PROCESS; + utent.ut_pid = pid; + strncpy(utent.ut_line, line, sizeof utent.ut_line); + /* XXX - assumes /dev/tty?? */ + strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id); + strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user); + time(&utent.ut_time); + } +} + +/* + * setutmp - put a USER_PROCESS entry in the utmp file + * + * setutmp changes the type of the current utmp entry to + * USER_PROCESS. the wtmp file will be updated as well. + */ + +static void setutmp(const char *name, const char *line) +{ + utent.ut_type = USER_PROCESS; + strncpy(utent.ut_user, name, sizeof utent.ut_user); + time(&utent.ut_time); + /* other fields already filled in by checkutmp above */ + setutent(); + pututline(&utent); + endutent(); + if (access(_PATH_WTMP, R_OK|W_OK) == -1) { + close(creat(_PATH_WTMP, 0664)); + } + updwtmp(_PATH_WTMP, &utent); +} +#endif /* CONFIG_FEATURE_U_W_TMP */ diff --git a/busybox/loginutils/passwd.c b/busybox/loginutils/passwd.c new file mode 100644 index 000000000..9c4b4ddfb --- /dev/null +++ b/busybox/loginutils/passwd.c @@ -0,0 +1,400 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +static char crypt_passwd[128]; + +static int create_backup(const char *backup, FILE * fp); +static int new_password(const struct passwd *pw, int amroot, int algo); +static void set_filesize_limit(int blocks); + + +int get_algo(char *a) +{ + int x = 1; /* standard: MD5 */ + + if (strcasecmp(a, "des") == 0) + x = 0; + return x; +} + + +extern int update_passwd(const struct passwd *pw, char *crypt_pw) +{ + char filename[1024]; + char buf[1025]; + char buffer[80]; + char username[32]; + char *pw_rest; + int mask; + int continued; + FILE *fp; + FILE *out_fp; + struct stat sb; + struct flock lock; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + if (access(bb_path_shadow_file, F_OK) == 0) { + snprintf(filename, sizeof filename, "%s", bb_path_shadow_file); + } else +#endif + { + snprintf(filename, sizeof filename, "%s", bb_path_passwd_file); + } + + if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) { + /* return 0; */ + return 1; + } + + /* Lock the password file before updating */ + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if (fcntl(fileno(fp), F_SETLK, &lock) < 0) { + fprintf(stderr, "%s: %s\n", filename, strerror(errno)); + return 1; + } + lock.l_type = F_UNLCK; + + snprintf(buf, sizeof buf, "%s-", filename); + if (create_backup(buf, fp)) { + fcntl(fileno(fp), F_SETLK, &lock); + fclose(fp); + return 1; + } + snprintf(buf, sizeof buf, "%s+", filename); + mask = umask(0777); + out_fp = fopen(buf, "w"); + umask(mask); + if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777)) + || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) { + fcntl(fileno(fp), F_SETLK, &lock); + fclose(fp); + fclose(out_fp); + return 1; + } + + continued = 0; + snprintf(username, sizeof username, "%s:", pw->pw_name); + rewind(fp); + while (!feof(fp)) { + fgets(buffer, sizeof buffer, fp); + if (!continued) { // Check to see if we're updating this line. + if (strncmp(username, buffer, strlen(username)) == 0) { // we have a match. + pw_rest = strchr(buffer, ':'); + *pw_rest++ = '\0'; + pw_rest = strchr(pw_rest, ':'); + fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest); + } else { + fputs(buffer, out_fp); + } + } else { + fputs(buffer, out_fp); + } + if (buffer[strlen(buffer) - 1] == '\n') { + continued = 0; + } else { + continued = 1; + } + bzero(buffer, sizeof buffer); + } + + if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) { + unlink(buf); + fcntl(fileno(fp), F_SETLK, &lock); + fclose(fp); + return 1; + } + if (rename(buf, filename) < 0) { + fcntl(fileno(fp), F_SETLK, &lock); + fclose(fp); + return 1; + } else { + fcntl(fileno(fp), F_SETLK, &lock); + fclose(fp); + return 0; + } +} + + +extern int passwd_main(int argc, char **argv) +{ + int amroot; + char *cp; + char *np; + char *name; + char *myname; + int flag; + int algo = 1; /* -a - password algorithm */ + int lflg = 0; /* -l - lock account */ + int uflg = 0; /* -u - unlock account */ + int dflg = 0; /* -d - delete password */ + const struct passwd *pw; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + const struct spwd *sp; +#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ + amroot = (getuid() == 0); + openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); + while ((flag = getopt(argc, argv, "a:dlu")) != EOF) { + switch (flag) { + case 'a': + algo = get_algo(optarg); + break; + case 'd': + dflg++; + break; + case 'l': + lflg++; + break; + case 'u': + uflg++; + break; + default: + bb_show_usage(); + } + } + myname = (char *) bb_xstrdup(my_getpwuid(NULL, getuid(), -1)); + /* exits on error */ + if (optind < argc) { + name = argv[optind]; + } else { + name = myname; + } + if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) { + bb_show_usage(); + } + pw = getpwnam(name); + if (!pw) { + bb_error_msg_and_die("Unknown user %s\n", name); + } + if (!amroot && pw->pw_uid != getuid()) { + syslog(LOG_WARNING, "can't change pwd for `%s'", name); + bb_error_msg_and_die("Permission denied.\n"); + } +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + sp = getspnam(name); + if (!sp) { + sp = (struct spwd *) pwd_to_spwd(pw); + } + cp = sp->sp_pwdp; + np = sp->sp_namp; +#else + cp = pw->pw_passwd; + np = name; +#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ + + safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); + if (!(dflg || lflg || uflg)) { + if (!amroot) { + if (cp[0] == '!') { + syslog(LOG_WARNING, "password locked for `%s'", np); + bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np); + } + } + printf("Changing password for %s\n", name); + if (new_password(pw, amroot, algo)) { + bb_error_msg_and_die( "The password for %s is unchanged.\n", name); + } + } else if (lflg) { + if (crypt_passwd[0] != '!') { + memmove(&crypt_passwd[1], crypt_passwd, + sizeof crypt_passwd - 1); + crypt_passwd[sizeof crypt_passwd - 1] = '\0'; + crypt_passwd[0] = '!'; + } + } else if (uflg) { + if (crypt_passwd[0] == '!') { + memmove(crypt_passwd, &crypt_passwd[1], + sizeof crypt_passwd - 1); + } + } else if (dflg) { + crypt_passwd[0] = '\0'; + } + set_filesize_limit(30000); + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + umask(077); + if (setuid(0)) { + syslog(LOG_ERR, "can't setuid(0)"); + bb_error_msg_and_die( "Cannot change ID to root.\n"); + } + if (!update_passwd(pw, crypt_passwd)) { + syslog(LOG_INFO, "password for `%s' changed by user `%s'", name, + myname); + printf("Password changed.\n"); + } else { + syslog(LOG_WARNING, "an error occurred updating the password file"); + bb_error_msg_and_die("An error occurred updating the password file.\n"); + } + return (0); +} + + + +static int create_backup(const char *backup, FILE * fp) +{ + struct stat sb; + struct utimbuf ub; + FILE *bkfp; + int c, mask; + + if (fstat(fileno(fp), &sb)) + /* return -1; */ + return 1; + + mask = umask(077); + bkfp = fopen(backup, "w"); + umask(mask); + if (!bkfp) + /* return -1; */ + return 1; + + /* TODO: faster copy, not one-char-at-a-time. --marekm */ + rewind(fp); + while ((c = getc(fp)) != EOF) { + if (putc(c, bkfp) == EOF) + break; + } + if (c != EOF || fflush(bkfp)) { + fclose(bkfp); + /* return -1; */ + return 1; + } + if (fclose(bkfp)) + /* return -1; */ + return 1; + + ub.actime = sb.st_atime; + ub.modtime = sb.st_mtime; + utime(backup, &ub); + return 0; +} + +static int i64c(int i) +{ + if (i <= 0) + return ('.'); + if (i == 1) + return ('/'); + if (i >= 2 && i < 12) + return ('0' - 2 + i); + if (i >= 12 && i < 38) + return ('A' - 12 + i); + if (i >= 38 && i < 63) + return ('a' - 38 + i); + return ('z'); +} + +static char *crypt_make_salt(void) +{ + time_t now; + static unsigned long x; + static char result[3]; + + time(&now); + x += now + getpid() + clock(); + result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077); + result[1] = i64c(((x >> 12) ^ x) & 077); + result[2] = '\0'; + return result; +} + + +static int new_password(const struct passwd *pw, int amroot, int algo) +{ + char *clear; + char *cipher; + char *cp; + char orig[200]; + char pass[200]; + time_t start, now; + + if (!amroot && crypt_passwd[0]) { + if (!(clear = bb_askpass(0, "Old password:"))) { + /* return -1; */ + return 1; + } + cipher = pw_encrypt(clear, crypt_passwd); + if (strcmp(cipher, crypt_passwd) != 0) { + syslog(LOG_WARNING, "incorrect password for `%s'", + pw->pw_name); + time(&start); + now = start; + while (difftime(now, start) < FAIL_DELAY) { + sleep(FAIL_DELAY); + time(&now); + } + fprintf(stderr, "Incorrect password.\n"); + /* return -1; */ + return 1; + } + safe_strncpy(orig, clear, sizeof(orig)); + bzero(clear, strlen(clear)); + bzero(cipher, strlen(cipher)); + } else { + orig[0] = '\0'; + } + if (! (cp=bb_askpass(0, "Enter the new password (minimum of 5, maximum of 8 characters)\n" + "Please use a combination of upper and lower case letters and numbers.\n" + "Enter new password: "))) + { + bzero(orig, sizeof orig); + /* return -1; */ + return 1; + } + safe_strncpy(pass, cp, sizeof(pass)); + bzero(cp, strlen(cp)); + /* if (!obscure(orig, pass, pw)) { */ + if (obscure(orig, pass, pw)) { + if (amroot) { + printf("\nWarning: weak password (continuing).\n"); + } else { + /* return -1; */ + return 1; + } + } + if (!(cp = bb_askpass(0, "Re-enter new password: "))) { + bzero(orig, sizeof orig); + /* return -1; */ + return 1; + } + if (strcmp(cp, pass)) { + fprintf(stderr, "Passwords do not match.\n"); + /* return -1; */ + return 1; + } + bzero(cp, strlen(cp)); + bzero(orig, sizeof(orig)); + + if (algo == 1) { + cp = pw_encrypt(pass, "$1$"); + } else + cp = pw_encrypt(pass, crypt_make_salt()); + bzero(pass, sizeof pass); + safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); + return 0; +} + +static void set_filesize_limit(int blocks) +{ + struct rlimit rlimit_fsize; + + rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks; + setrlimit(RLIMIT_FSIZE, &rlimit_fsize); +} diff --git a/busybox/loginutils/su.c b/busybox/loginutils/su.c new file mode 100644 index 000000000..ec0c16c7d --- /dev/null +++ b/busybox/loginutils/su.c @@ -0,0 +1,157 @@ +/* vi: set sw=4 ts=4: */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + + + +/* The shell to run if none is given in the user's passwd entry. */ +#define DEFAULT_USER "root" + +//#define SYSLOG_SUCCESS +#define SYSLOG_FAILURE + + +#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE ) +/* Log the fact that someone has run su */ + +# if defined( SYSLOG_SUCCESS ) && defined( SYSLOG_FAILURE ) +static void log_su (const char *successful, const char *old_user, const char *tty) +{ + syslog ( LOG_NOTICE, "%s%s on %s", successful, old_user, tty); +} +# define log_su_successful(cu, u, tty) if(!cu) log_su("", u, tty) +# define log_su_failure(cu, u, tty) if(!cu) log_su("FAILED SU ", u, tty) +# else + /* partial logging */ +# if !defined( SYSLOG_SUCESS ) +# define log_su_successful(cu, u, tty) +# define log_su_failure(cu, u, t) if(!cu) \ + syslog(LOG_NOTICE, "FAILED SU %s on %s", u, t) +# else +# define log_su_successful(cu, u, t) if(!cu) \ + syslog(LOG_NOTICE, "%s on %s", u, t) +# define log_su_failure(cu, u, tty) +# endif +# endif +#else + /* logging not used */ +# define log_su_successful(cu, u, tty) +# define log_su_failure(cu, u, tty) +#endif + + +int su_main ( int argc, char **argv ) +{ + unsigned long flags; + int opt_preserve; + int opt_loginshell; + char *opt_shell = 0; + char *opt_command = 0; + char *opt_username = DEFAULT_USER; + char **opt_args = 0; + struct passwd *pw; + uid_t cur_uid = getuid(); + +#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE ) + const char *tty; + const char *old_user; +#endif + + flags = bb_getopt_ulflags(argc, argv, "mplc:s:", + &opt_command, &opt_shell); + opt_preserve = flags & 3; + opt_loginshell = (flags & 4 ? 1 : 0); + + if (optind < argc && argv[optind][0] == '-' && argv[optind][1] == 0) { + opt_loginshell = 1; + ++optind; + } + + /* get user if specified */ + if ( optind < argc ) + opt_username = argv [optind++]; + + if ( optind < argc ) + opt_args = argv + optind; + +#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE ) +#ifdef CONFIG_FEATURE_U_W_TMP + /* The utmp entry (via getlogin) is probably the best way to identify + the user, especially if someone su's from a su-shell. */ + old_user = getlogin ( ); + if ( !old_user ) +#endif + { + /* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid. */ + pw = getpwuid ( cur_uid ); + old_user = ( pw ? pw->pw_name : "" ); + } + tty = ttyname ( 2 ); + if(!tty) + tty = "none"; + + openlog ( bb_applet_name, 0, LOG_AUTH ); +#endif + + pw = getpwnam ( opt_username ); + if ( !pw ) + bb_error_msg_and_die ( "user %s does not exist", opt_username ); + + /* Make sure pw->pw_shell is non-NULL. It may be NULL when NEW_USER + is a username that is retrieved via NIS (YP), but that doesn't have + a default shell listed. */ + if ( !pw-> pw_shell || !pw->pw_shell [0] ) + pw-> pw_shell = (char *) DEFAULT_SHELL; + + if ((( cur_uid == 0 ) || correct_password ( pw ))) { + log_su_successful(pw->pw_uid, old_user, tty ); + } else { + log_su_failure (pw->pw_uid, old_user, tty ); + bb_error_msg_and_die ( "incorrect password" ); + } + +#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE ) + closelog(); +#endif + + if ( !opt_shell && opt_preserve ) + opt_shell = getenv ( "SHELL" ); + + if ( opt_shell && cur_uid && restricted_shell ( pw-> pw_shell )) { + /* The user being su'd to has a nonstandard shell, and so is + probably a uucp account or has restricted access. Don't + compromise the account by allowing access with a standard + shell. */ + fputs ( "using restricted shell\n", stderr ); + opt_shell = 0; + } + + if ( !opt_shell ) + opt_shell = pw->pw_shell; + + change_identity ( pw ); + setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw ); + run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args +#ifdef CONFIG_SELINUX + , 0 +#endif + ); + + return EXIT_FAILURE; +} diff --git a/busybox/loginutils/sulogin.c b/busybox/loginutils/sulogin.c new file mode 100644 index 000000000..f21b09571 --- /dev/null +++ b/busybox/loginutils/sulogin.c @@ -0,0 +1,158 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + + +// sulogin defines +#define SULOGIN_PROMPT "\nGive root password for system maintenance\n" \ + "(or type Control-D for normal startup):" + +static const char *forbid[] = { + "ENV", + "BASH_ENV", + "HOME", + "IFS", + "PATH", + "SHELL", + "LD_LIBRARY_PATH", + "LD_PRELOAD", + "LD_TRACE_LOADED_OBJECTS", + "LD_BIND_NOW", + "LD_AOUT_LIBRARY_PATH", + "LD_AOUT_PRELOAD", + "LD_NOWARN", + "LD_KEEPDIR", + (char *) 0 +}; + + + +static void catchalarm(int junk) +{ + exit(EXIT_FAILURE); +} + + +extern int sulogin_main(int argc, char **argv) +{ + char *cp; + char *device = (char *) 0; + const char *name = "root"; + int timeout = 0; + static char pass[BUFSIZ]; + struct passwd pwent; + struct passwd *pwd; + time_t start, now; + const char **p; +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + struct spwd *spwd = NULL; +#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ + + openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); + if (argc > 1) { + if (strncmp(argv[1], "-t", 2) == 0) { + if (strcmp(argv[1], "-t") == 0) { + if (argc > 2) { + timeout = atoi(argv[2]); + if (argc > 3) { + device = argv[3]; + } + } + } else { + if (argc > 2) { + device = argv[2]; + } + } + } else { + device = argv[1]; + } + if (device) { + close(0); + close(1); + close(2); + if (open(device, O_RDWR) >= 0) { + dup(0); + dup(0); + } else { + syslog(LOG_WARNING, "cannot open %s\n", device); + exit(EXIT_FAILURE); + } + } + } + if (access(bb_path_passwd_file, 0) == -1) { + syslog(LOG_WARNING, "No password file\n"); + bb_error_msg_and_die("No password file\n"); + } + if (!isatty(0) || !isatty(1) || !isatty(2)) { + exit(EXIT_FAILURE); + } + + + /* Clear out anything dangerous from the environment */ + for (p = forbid; *p; p++) + unsetenv(*p); + + + signal(SIGALRM, catchalarm); + if (!(pwd = getpwnam(name))) { + syslog(LOG_WARNING, "No password entry for `root'\n"); + bb_error_msg_and_die("No password entry for `root'\n"); + } + pwent = *pwd; +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + spwd = NULL; + if (pwd && ((strcmp(pwd->pw_passwd, "x") == 0) + || (strcmp(pwd->pw_passwd, "*") == 0))) { + endspent(); + spwd = getspnam(name); + if (spwd) { + pwent.pw_passwd = spwd->sp_pwdp; + } + } +#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ + while (1) { + cp = bb_askpass(timeout, SULOGIN_PROMPT); + if (!cp || !*cp) { + puts("\n"); + fflush(stdout); + syslog(LOG_INFO, "Normal startup\n"); + exit(EXIT_SUCCESS); + } else { + safe_strncpy(pass, cp, sizeof(pass)); + bzero(cp, strlen(cp)); + } + if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 0) { + break; + } + time(&start); + now = start; + while (difftime(now, start) < FAIL_DELAY) { + sleep(FAIL_DELAY); + time(&now); + } + puts("Login incorrect"); + fflush(stdout); + syslog(LOG_WARNING, "Incorrect root password\n"); + } + bzero(pass, strlen(pass)); + signal(SIGALRM, SIG_DFL); + puts("Entering System Maintenance Mode\n"); + fflush(stdout); + syslog(LOG_INFO, "System Maintenance Mode\n"); + run_shell(pwent.pw_shell, 1, 0, 0); + return (0); +} diff --git a/busybox/loginutils/vlock.c b/busybox/loginutils/vlock.c new file mode 100644 index 000000000..def484ae6 --- /dev/null +++ b/busybox/loginutils/vlock.c @@ -0,0 +1,231 @@ +/* vi: set sw=4 ts=4: */ +/* + * vlock implementation for busybox + * + * Copyright (C) 2000 by spoon + * Written by 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 + * + */ + +/* Shoutz to Michael K. Johnson , author of the + * original vlock. I snagged a bunch of his code to write this + * minimalistic vlock. + */ +/* Fixed by Erik Andersen to do passwords the tinylogin way... + * It now works with md5, sha1, etc passwords. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +static struct passwd *pw; +static struct vt_mode ovtm; +static struct termios oterm; +static int vfd; +static int o_lock_all = 0; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS +static struct spwd *spw; + +/* getspuid - get a shadow entry by uid */ +struct spwd *getspuid(uid_t uid) +{ + struct spwd *sp; + struct passwd *mypw; + + if ((mypw = getpwuid(getuid())) == NULL) { + return (NULL); + } + setspent(); + while ((sp = getspent()) != NULL) { + if (strcmp(mypw->pw_name, sp->sp_namp) == 0) + break; + } + endspent(); + return (sp); +} +#endif + +static void release_vt(int signo) +{ + if (!o_lock_all) + ioctl(vfd, VT_RELDISP, 1); + else + ioctl(vfd, VT_RELDISP, 0); +} + +static void acquire_vt(int signo) +{ + ioctl(vfd, VT_RELDISP, VT_ACKACQ); +} + +static void restore_terminal(void) +{ + ioctl(vfd, VT_SETMODE, &ovtm); + tcsetattr(STDIN_FILENO, TCSANOW, &oterm); +} + +extern int vlock_main(int argc, char **argv) +{ + sigset_t sig; + struct sigaction sa; + struct vt_mode vtm; + int times = 0; + struct termios term; + + if (argc > 2) { + bb_show_usage(); + } + + if (argc == 2) { + if (strncmp(argv[1], "-a", 2)) { + bb_show_usage(); + } else { + o_lock_all = 1; + } + } + + if ((pw = getpwuid(getuid())) == NULL) { + bb_error_msg_and_die("no password for uid %d\n", getuid()); + } +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + if ((strcmp(pw->pw_passwd, "x") == 0) + || (strcmp(pw->pw_passwd, "*") == 0)) { + + if ((spw = getspuid(getuid())) == NULL) { + bb_error_msg_and_die("could not read shadow password for uid %d: %s\n", + getuid(), strerror(errno)); + } + if (spw->sp_pwdp) { + pw->pw_passwd = spw->sp_pwdp; + } + } +#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ + if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') { + bb_error_msg_and_die("Account disabled for uid %d\n", getuid()); + } + + /* we no longer need root privs */ + setuid(getuid()); + setgid(getgid()); + + if ((vfd = open("/dev/tty", O_RDWR)) < 0) { + bb_error_msg_and_die("/dev/tty"); + }; + + if (ioctl(vfd, VT_GETMODE, &vtm) < 0) { + bb_error_msg_and_die("/dev/tty"); + }; + + /* mask a bunch of signals */ + sigprocmask(SIG_SETMASK, NULL, &sig); + sigdelset(&sig, SIGUSR1); + sigdelset(&sig, SIGUSR2); + sigaddset(&sig, SIGTSTP); + sigaddset(&sig, SIGTTIN); + sigaddset(&sig, SIGTTOU); + sigaddset(&sig, SIGHUP); + sigaddset(&sig, SIGCHLD); + sigaddset(&sig, SIGQUIT); + sigaddset(&sig, SIGINT); + + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = SA_RESTART; + sa.sa_handler = release_vt; + sigaction(SIGUSR1, &sa, NULL); + sa.sa_handler = acquire_vt; + sigaction(SIGUSR2, &sa, NULL); + + /* need to handle some signals so that we don't get killed by them */ + sa.sa_handler = SIG_IGN; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTSTP, &sa, NULL); + + ovtm = vtm; + vtm.mode = VT_PROCESS; + vtm.relsig = SIGUSR1; + vtm.acqsig = SIGUSR2; + ioctl(vfd, VT_SETMODE, &vtm); + + tcgetattr(STDIN_FILENO, &oterm); + term = oterm; + term.c_iflag &= ~BRKINT; + term.c_iflag |= IGNBRK; + term.c_lflag &= ~ISIG; + term.c_lflag &= ~(ECHO | ECHOCTL); + tcsetattr(STDIN_FILENO, TCSANOW, &term); + + do { + char *pass, *crypt_pass; + char prompt[100]; + + if (o_lock_all) { + printf("All Virtual Consoles locked.\n"); + } else { + printf("This Virtual Console locked.\n"); + } + fflush(stdout); + + snprintf(prompt, 100, "%s's password: ", pw->pw_name); + + if ((pass = bb_askpass(0, prompt)) == NULL) { + restore_terminal(); + bb_perror_msg_and_die("password"); + } + + crypt_pass = pw_encrypt(pass, pw->pw_passwd); + if (strncmp(crypt_pass, pw->pw_passwd, sizeof(crypt_pass)) == 0) { + memset(pass, 0, strlen(pass)); + memset(crypt_pass, 0, strlen(crypt_pass)); + restore_terminal(); + return 0; + } + memset(pass, 0, strlen(pass)); + memset(crypt_pass, 0, strlen(crypt_pass)); + + if (isatty(STDIN_FILENO) == 0) { + restore_terminal(); + bb_perror_msg_and_die("isatty"); + } + + sleep(++times); + printf("Password incorrect.\n"); + if (times >= 3) { + sleep(15); + times = 2; + } + } while (1); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/miscutils/Config.in b/busybox/miscutils/Config.in new file mode 100644 index 000000000..77e13e84e --- /dev/null +++ b/busybox/miscutils/Config.in @@ -0,0 +1,201 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Miscellaneous Utilities" + +config CONFIG_ADJTIMEX + bool "adjtimex" + default n + help + Adjtimex reads and optionally sets adjustment parameters for + the Linux clock adjustment algorithm. + +config CONFIG_CROND + bool "crond" + default n + select CONFIG_FEATURE_SUID + help + Crond is a background daemon that parses individual crontab + files and executes commands on behalf of the users in question. + This is a port of dcron from slackware. It uses files of the + format /var/spool/cron/crontabs/ files, for example: + $ cat /var/spool/cron/crontabs/root + # Run daily cron jobs at 4:40 every day: + 40 4 * * * /etc/cron/daily > /dev/null 2>&1 + Note that Busybox binary must be setuid root for this applet to + work properly. + +config CONFIG_FEATURE_CROND_CALL_SENDMAIL + bool " Using /usr/sbin/sendmail?" + default n + depends on CONFIG_CROND + help + Support calling /usr/sbin/sendmail for send cmd outputs. + +config CONFIG_CRONTAB + bool "crontab" + default n + help + Crontab manipulates the crontab for a particular user. Only + the superuser may specify a different user and/or crontab directory. + +config CONFIG_DC + bool "dc" + default n + help + Dc is a reverse-polish desk calculator which supports unlimited + precision arithmetic. + +config CONFIG_DEVFSD + bool "devfsd" + default n + help + Provides compatibility with old device names on a devfs systems. + You should set it to true if you have devfs enabled. + The following keywords in devsfd.conf are supported: + "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE", + "PERMISSIONS", "EXECUTE", "COPY", "IGNORE", + "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT". + + But only if they are written UPPERCASE!!!!!!!! + +config CONFIG_DEVFSD_MODLOAD + bool "Adds support for MODLOAD keyword in devsfd.conf" + default n + depends on CONFIG_DEVFSD + help + This actually doesn't work with busybox modutils but needs the real modutils. + +config CONFIG_DEVFSD_FG_NP + bool "Enables the -fg and -np options" + default n + depends on CONFIG_DEVFSD + help + -fg Run the daemon in the foreground. + -np Exit after parsing the configuration file. Do not poll for events. + +config CONFIG_DEVFSD_VERBOSE + bool "Increases logging (and size)" + default n + depends on CONFIG_DEVFSD + help + Increases logging to stderr or syslog. + +config CONFIG_LAST + bool "last" + default n + select CONFIG_FEATURE_U_W_TMP + help + 'last' displays a list of the last users that logged into the system. + +config CONFIG_HDPARM + bool "hdparm" + default n + help + Get/Set hard drive parameters. Primarily intended for ATA + drives. Adds about 13k (or around 30k if you enable the + CONFIG_FEATURE_HDPARM_GET_IDENTITY option).... + +config CONFIG_FEATURE_HDPARM_GET_IDENTITY + bool " Support obtaining detailed information directly from drives" + default y + depends on CONFIG_HDPARM + help + Enables the -I and -Istdin options to obtain detailed information + directly from drives about their capabilities and supported ATA + feature set. Enabling this option will add about 16k... + +config CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF + bool " Register an IDE interface (DANGEROUS)" + default n + depends on CONFIG_HDPARM + help + Enables the 'hdparm -R' option to register an IDE interface. + This is dangerous stuff, so you should probably say N. + +config CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF + bool " Un-register an IDE interface (DANGEROUS)" + default n + depends on CONFIG_HDPARM + help + Enables the 'hdparm -U' option to un-register an IDE interface. + This is dangerous stuff, so you should probably say N. + +config CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET + bool " perform device reset (DANGEROUS)" + default n + depends on CONFIG_HDPARM + help + Enables the 'hdparm -w' option to perform a device reset. + This is dangerous stuff, so you should probably say N. + +config CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF + bool " tristate device for hotswap (DANGEROUS)" + default n + depends on CONFIG_HDPARM + help + Enables the 'hdparm -x' option to tristate device for hotswap, + and the '-b' option to get/set bus state. This is dangerous + stuff, so you should probably say N. + +config CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA + bool " get/set using_dma flag (DANGEROUS)" + default n + depends on CONFIG_HDPARM + help + Enables the 'hdparm -d' option to get/set using_dma flag. + This is dangerous stuff, so you should probably say N. + +config CONFIG_MAKEDEVS + bool "makedevs" + default n + help + 'makedevs' is a utility used and created by the Linux Router Project. + It creates a large number of device special files (/dev devices) + rather quickly, and can be considerably faster then running mknod a + zillion times. + +config CONFIG_MT + bool "mt" + default n + help + mt is used to control tape devices. You can use the mt utility + to advance or rewind a tape past a specified number of archive + files on the tape. + +config CONFIG_RX + bool "rx" + default n + help + Receive files using the Xmodem protocol. + +config CONFIG_STRINGS + bool "strings" + default n + help + strings prints the printable character sequences for each file + specified. + +config CONFIG_TIME + bool "time" + default n + help + The time command runs the specified program with the given arguments. + When the command finishes, time writes a message to standard output + giving timing statistics about this program run. + +config CONFIG_WATCHDOG + bool "watchdog" + default n + help + The watchdog utility is used with hardware or software watchdog + device drivers. It opens the specified watchdog device special file + and periodically writes a magic character to the device. If the + watchdog applet ever fails to write the magic character within a + certain amount of time, the watchdog device assumes the system has + hung, and will cause the hardware to reboot. + +endmenu + diff --git a/busybox/miscutils/Makefile b/busybox/miscutils/Makefile new file mode 100644 index 000000000..ac427dc09 --- /dev/null +++ b/busybox/miscutils/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/miscutils +MISCUTILS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/miscutils/Makefile.in b/busybox/miscutils/Makefile.in new file mode 100644 index 000000000..ddddf72b3 --- /dev/null +++ b/busybox/miscutils/Makefile.in @@ -0,0 +1,55 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +MISCUTILS_AR:=miscutils.a +ifndef $(MISCUTILS_DIR) +MISCUTILS_DIR:=$(top_builddir)/miscutils/ +endif +srcdir=$(top_srcdir)/miscutils + +MISCUTILS-y:= +MISCUTILS-$(CONFIG_ADJTIMEX) += adjtimex.o +MISCUTILS-$(CONFIG_CROND) += crond.o +MISCUTILS-$(CONFIG_CRONTAB) += crontab.o +MISCUTILS-$(CONFIG_DC) += dc.o +MISCUTILS-$(CONFIG_DEVFSD) += devfsd.o +MISCUTILS-$(CONFIG_HDPARM) += hdparm.o +MISCUTILS-$(CONFIG_LAST) += last.o +MISCUTILS-$(CONFIG_MAKEDEVS) += makedevs.o +MISCUTILS-$(CONFIG_MT) += mt.o +MISCUTILS-$(CONFIG_RX) += rx.o +MISCUTILS-$(CONFIG_STRINGS) += strings.o +MISCUTILS-$(CONFIG_TIME) += time.o +MISCUTILS-$(CONFIG_WATCHDOG) += watchdog.o + +libraries-y+=$(MISCUTILS_DIR)$(MISCUTILS_AR) + +needlibm-y:= +needlibm-$(CONFIG_DC) := y + +ifeq ($(needlibm-y),y) + LIBRARIES += -lm +endif + +$(MISCUTILS_DIR)$(MISCUTILS_AR): $(patsubst %,$(MISCUTILS_DIR)%, $(MISCUTILS-y)) + $(AR) -ro $@ $(patsubst %,$(MISCUTILS_DIR)%, $(MISCUTILS-y)) + +$(MISCUTILS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/miscutils/adjtimex.c b/busybox/miscutils/adjtimex.c new file mode 100644 index 000000000..110c02654 --- /dev/null +++ b/busybox/miscutils/adjtimex.c @@ -0,0 +1,167 @@ +/* + * adjtimex.c - read, and possibly modify, the Linux kernel `timex' variables. + * + * Originally written: October 1997 + * Last hack: March 2001 + * Copyright 1997, 2000, 2001 Larry Doolittle + * + * 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 +#include +#include "busybox.h" + +static struct {int bit; char *name;} statlist[] = { + { STA_PLL, "PLL" }, + { STA_PPSFREQ, "PPSFREQ" }, + { STA_PPSTIME, "PPSTIME" }, + { STA_FLL, "FFL" }, + { STA_INS, "INS" }, + { STA_DEL, "DEL" }, + { STA_UNSYNC, "UNSYNC" }, + { STA_FREQHOLD, "FREQHOLD" }, + { STA_PPSSIGNAL, "PPSSIGNAL" }, + { STA_PPSJITTER, "PPSJITTER" }, + { STA_PPSWANDER, "PPSWANDER" }, + { STA_PPSERROR, "PPSERROR" }, + { STA_CLOCKERR, "CLOCKERR" }, + { 0, NULL } }; + +static char *ret_code_descript[] = { + "clock synchronized", + "insert leap second", + "delete leap second", + "leap second in progress", + "leap second has occurred", + "clock not synchronized" }; + +#ifdef BB_VER +#define main adjtimex_main +#else +void usage(char *prog) +{ + fprintf(stderr, + "Usage: %s [ -q ] [ -o offset ] [ -f frequency ] [ -p timeconstant ] [ -t tick ]\n", + prog); +} +#define bb_show_usage() usage(argv[0]) +#endif + +int main(int argc, char ** argv) +{ + struct timex txc; + int quiet=0; + int c, i, ret, sep; + char *descript; + txc.modes=0; + for (;;) { + c = getopt( argc, argv, "qo:f:p:t:"); + if (c == EOF) break; + switch (c) { + case 'q': + quiet=1; + break; + case 'o': + txc.offset = atoi(optarg); + txc.modes |= ADJ_OFFSET_SINGLESHOT; + break; + case 'f': + txc.freq = atoi(optarg); + txc.modes |= ADJ_FREQUENCY; + break; + case 'p': + txc.constant = atoi(optarg); + txc.modes |= ADJ_TIMECONST; + break; + case 't': + txc.tick = atoi(optarg); + txc.modes |= ADJ_TICK; + break; + default: + bb_show_usage(); + exit(1); + } + } + if (argc != optind) { /* no valid non-option parameters */ + bb_show_usage(); + exit(1); + } + + ret = adjtimex(&txc); + + if (ret < 0) perror("adjtimex"); + + if (!quiet && ret>=0) { + printf( + " mode: %d\n" + "-o offset: %ld\n" + "-f frequency: %ld\n" + " maxerror: %ld\n" + " esterror: %ld\n" + " status: %d ( ", + txc.modes, txc.offset, txc.freq, txc.maxerror, + txc.esterror, txc.status); + + /* representative output of next code fragment: + "PLL | PPSTIME" */ + sep=0; + for (i=0; statlist[i].name; i++) { + if (txc.status & statlist[i].bit) { + if (sep) fputs(" | ",stdout); + fputs(statlist[i].name,stdout); + sep=1; + } + } + + descript = "error"; + if (ret >= 0 && ret <= 5) descript = ret_code_descript[ret]; + printf(" )\n" + "-p timeconstant: %ld\n" + " precision: %ld\n" + " tolerance: %ld\n" + "-t tick: %ld\n" + " time.tv_sec: %ld\n" + " time.tv_usec: %ld\n" + " return value: %d (%s)\n", + txc.constant, + txc.precision, txc.tolerance, txc.tick, + (long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript); + } + return (ret<0); +} diff --git a/busybox/miscutils/crond.c b/busybox/miscutils/crond.c new file mode 100644 index 000000000..085cf6e9d --- /dev/null +++ b/busybox/miscutils/crond.c @@ -0,0 +1,1055 @@ +/* + * crond -d[#] -c -f -b + * + * run as root, but NOT setuid root + * + * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com) + * May be distributed under the GNU General Public License + * + * Vladimir Oleynik (C) 2002 to be used in busybox + */ + +#define VERSION "2.3.2" + +#undef FEATURE_DEBUG_OPT + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +#define arysize(ary) (sizeof(ary)/sizeof((ary)[0])) + +#ifndef CRONTABS +#define CRONTABS "/var/spool/cron/crontabs" +#endif +#ifndef TMPDIR +#define TMPDIR "/var/spool/cron" +#endif +#ifndef SENDMAIL +#define SENDMAIL "/usr/sbin/sendmail" +#endif +#ifndef SENDMAIL_ARGS +#define SENDMAIL_ARGS "-ti", "oem" +#endif +#ifndef CRONUPDATE +#define CRONUPDATE "cron.update" +#endif +#ifndef MAXLINES +#define MAXLINES 256 /* max lines in non-root crontabs */ +#endif + +typedef struct CronFile { + struct CronFile *cf_Next; + struct CronLine *cf_LineBase; + char *cf_User; /* username */ + int cf_Ready; /* bool: one or more jobs ready */ + int cf_Running; /* bool: one or more jobs running */ + int cf_Deleted; /* marked for deletion, ignore */ +} CronFile; + +typedef struct CronLine { + struct CronLine *cl_Next; + char *cl_Shell; /* shell command */ + pid_t cl_Pid; /* running pid, 0, or armed (-1) */ + int cl_MailFlag; /* running pid is for mail */ + int cl_MailPos; /* 'empty file' size */ + char cl_Mins[60]; /* 0-59 */ + char cl_Hrs[24]; /* 0-23 */ + char cl_Days[32]; /* 1-31 */ + char cl_Mons[12]; /* 0-11 */ + char cl_Dow[7]; /* 0-6, beginning sunday */ +} CronLine; + +#define RUN_RANOUT 1 +#define RUN_RUNNING 2 +#define RUN_FAILED 3 + +#define DaemonUid 0 + +#ifdef FEATURE_DEBUG_OPT +static short DebugOpt; +#endif + +static short LogLevel = 8; +static const char *LogFile; +static const char *CDir = CRONTABS; + +static void startlogger(void); + +static void CheckUpdates(void); +static void SynchronizeDir(void); +static int TestJobs(time_t t1, time_t t2); +static void RunJobs(void); +static int CheckJobs(void); + +static void RunJob(const char *user, CronLine * line); + +#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL +static void EndJob(const char *user, CronLine * line); +#else +#define EndJob(user, line) line->cl_Pid = 0 +#endif + +static void DeleteFile(const char *userName); + +static CronFile *FileBase; + + +static void crondlog(const char *ctl, ...) +{ + va_list va; + const char *fmt; + int level = (int) (ctl[0] & 0xf); + int type = level == 20 ? + LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE); + + + va_start(va, ctl); + fmt = ctl + 1; + if (level >= LogLevel) { + +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) { + vfprintf(stderr, fmt, va); + } else +#endif + if (LogFile == 0) { + vsyslog(type, fmt, va); + } else { + int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 600); + if (logfd >= 0) { + vdprintf(logfd, fmt, va); + close(logfd); +#ifdef FEATURE_DEBUG_OPT + } else { + bb_perror_msg("Can't open log file"); +#endif + } + } + } + va_end(va); + if (ctl[0] & 0200) { + exit(20); + } +} + +int crond_main(int ac, char **av) +{ + unsigned long opt; + char *lopt, *Lopt, *copt; + +#ifdef FEATURE_DEBUG_OPT + char *dopt; + + bb_opt_complementaly = "f-b:b-f:S-L:L-S:d-l"; +#else + bb_opt_complementaly = "f-b:b-f:S-L:L-S"; +#endif + + opterr = 0; /* disable getopt 'errors' message. */ + opt = bb_getopt_ulflags(ac, av, "l:L:fbSc:" +#ifdef FEATURE_DEBUG_OPT + "d:" +#endif + , &lopt, &Lopt, &copt +#ifdef FEATURE_DEBUG_OPT + , &dopt +#endif + ); + if (opt & 1) { + LogLevel = atoi(lopt); + } + if (opt & 2) { + if (*Lopt != 0) { + LogFile = Lopt; + } + } + if (opt & 32) { + if (*copt != 0) { + CDir = copt; + } + } +#ifdef FEATURE_DEBUG_OPT + if (opt & 64) { + DebugOpt = atoi(dopt); + LogLevel = 0; + } +#endif + + /* + * change directory + */ + + if (chdir(CDir) != 0) { + bb_perror_msg_and_die("%s", CDir); + } + signal(SIGHUP, SIG_IGN); /* hmm.. but, if kill -HUP original + * version - his died. ;( + */ + /* + * close stdin and stdout, stderr. + * close unused descriptors - don't need. + * optional detach from controlling terminal + */ + + if (!(opt & 4)) { +#if defined(__uClinux__) + /* reexec for vfork() do continue parent */ + vfork_daemon_rexec(1, 0, ac, av, "-f"); +#else /* uClinux */ + if (daemon(1, 0) < 0) { + bb_perror_msg_and_die("daemon"); + } +#endif /* uClinux */ + } + + (void) startlogger(); /* need if syslog mode selected */ + + /* + * main loop - synchronize to 1 second after the minute, minimum sleep + * of 1 second. + */ + + crondlog("\011%s " VERSION " dillon, started, log level %d\n", + bb_applet_name, LogLevel); + + SynchronizeDir(); + + { + time_t t1 = time(NULL); + time_t t2; + long dt; + short rescan = 60; + short sleep_time = 60; + + for (;;) { + sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time)); + + t2 = time(NULL); + dt = t2 - t1; + + /* + * The file 'cron.update' is checked to determine new cron + * jobs. The directory is rescanned once an hour to deal + * with any screwups. + * + * check for disparity. Disparities over an hour either way + * result in resynchronization. A reverse-indexed disparity + * less then an hour causes us to effectively sleep until we + * match the original time (i.e. no re-execution of jobs that + * have just been run). A forward-indexed disparity less then + * an hour causes intermediate jobs to be run, but only once + * in the worst case. + * + * when running jobs, the inequality used is greater but not + * equal to t1, and less then or equal to t2. + */ + + if (--rescan == 0) { + rescan = 60; + SynchronizeDir(); + } + CheckUpdates(); +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) + crondlog("\005Wakeup dt=%d\n", dt); +#endif + if (dt < -60 * 60 || dt > 60 * 60) { + t1 = t2; + crondlog("\111time disparity of %d minutes detected\n", dt / 60); + } else if (dt > 0) { + TestJobs(t1, t2); + RunJobs(); + sleep(5); + if (CheckJobs() > 0) { + sleep_time = 10; + } else { + sleep_time = 60; + } + t1 = t2; + } + } + } + /* not reached */ +} + +#if defined(FEATURE_DEBUG_OPT) || defined(CONFIG_FEATURE_CROND_CALL_SENDMAIL) +/* + write to temp file.. +*/ +static void fdprintf(int fd, const char *ctl, ...) +{ + va_list va; + + va_start(va, ctl); + vdprintf(fd, ctl, va); + va_end(va); +} +#endif + + +static int ChangeUser(const char *user) +{ + struct passwd *pas; + const char *err_msg; + + /* + * Obtain password entry and change privileges + */ + pas = getpwnam(user); + if (pas == 0) { + crondlog("\011failed to get uid for %s", user); + return (-1); + } + setenv("USER", pas->pw_name, 1); + setenv("HOME", pas->pw_dir, 1); + setenv("SHELL", DEFAULT_SHELL, 1); + + /* + * Change running state to the user in question + */ + err_msg = change_identity_e2str(pas); + if (err_msg) { + crondlog("\011%s for user %s", err_msg, user); + return (-1); + } + if (chdir(pas->pw_dir) < 0) { + crondlog("\011chdir failed: %s: %m", pas->pw_dir); + if (chdir(TMPDIR) < 0) { + crondlog("\011chdir failed: %s: %m", TMPDIR); + return (-1); + } + } + return (pas->pw_uid); +} + +static void startlogger(void) +{ + if (LogFile == 0) { + openlog(bb_applet_name, LOG_CONS | LOG_PID, LOG_CRON); + } +#ifdef FEATURE_DEBUG_OPT + else { /* test logfile */ + int logfd; + + if ((logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 600)) >= 0) { + close(logfd); + } else { + bb_perror_msg("Failed to open log file '%s' reason", LogFile); + } + } +#endif +} + + +static const char *const DowAry[] = { + "sun", + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + NULL +}; + +static const char *const MonAry[] = { + "jan", + "feb", + "mar", + "apr", + "may", + "jun", + "jul", + "aug", + "sep", + "oct", + "nov", + "dec", + + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + NULL +}; + +static char *ParseField(char *user, char *ary, int modvalue, int off, + const char *const *names, char *ptr) +{ + char *base = ptr; + int n1 = -1; + int n2 = -1; + + if (base == NULL) { + return (NULL); + } + + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') { + int skip = 0; + + /* Handle numeric digit or symbol or '*' */ + + if (*ptr == '*') { + n1 = 0; /* everything will be filled */ + n2 = modvalue - 1; + skip = 1; + ++ptr; + } else if (*ptr >= '0' && *ptr <= '9') { + if (n1 < 0) { + n1 = strtol(ptr, &ptr, 10) + off; + } else { + n2 = strtol(ptr, &ptr, 10) + off; + } + skip = 1; + } else if (names) { + int i; + + for (i = 0; names[i]; ++i) { + if (strncmp(ptr, names[i], strlen(names[i])) == 0) { + break; + } + } + if (names[i]) { + ptr += strlen(names[i]); + if (n1 < 0) { + n1 = i; + } else { + n2 = i; + } + skip = 1; + } + } + + /* handle optional range '-' */ + + if (skip == 0) { + crondlog("\111failed user %s parsing %s\n", user, base); + return (NULL); + } + if (*ptr == '-' && n2 < 0) { + ++ptr; + continue; + } + + /* + * collapse single-value ranges, handle skipmark, and fill + * in the character array appropriately. + */ + + if (n2 < 0) { + n2 = n1; + } + if (*ptr == '/') { + skip = strtol(ptr + 1, &ptr, 10); + } + /* + * fill array, using a failsafe is the easiest way to prevent + * an endless loop + */ + + { + int s0 = 1; + int failsafe = 1024; + + --n1; + do { + n1 = (n1 + 1) % modvalue; + + if (--s0 == 0) { + ary[n1 % modvalue] = 1; + s0 = skip; + } + } + while (n1 != n2 && --failsafe); + + if (failsafe == 0) { + crondlog("\111failed user %s parsing %s\n", user, base); + return (NULL); + } + } + if (*ptr != ',') { + break; + } + ++ptr; + n1 = -1; + n2 = -1; + } + + if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') { + crondlog("\111failed user %s parsing %s\n", user, base); + return (NULL); + } + + while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') { + ++ptr; + } +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) { + int i; + + for (i = 0; i < modvalue; ++i) { + crondlog("\005%d", ary[i]); + } + crondlog("\005\n"); + } +#endif + + return (ptr); +} + +static void FixDayDow(CronLine * line) +{ + short i; + short weekUsed = 0; + short daysUsed = 0; + + for (i = 0; i < arysize(line->cl_Dow); ++i) { + if (line->cl_Dow[i] == 0) { + weekUsed = 1; + break; + } + } + for (i = 0; i < arysize(line->cl_Days); ++i) { + if (line->cl_Days[i] == 0) { + daysUsed = 1; + break; + } + } + if (weekUsed && !daysUsed) { + memset(line->cl_Days, 0, sizeof(line->cl_Days)); + } + if (daysUsed && !weekUsed) { + memset(line->cl_Dow, 0, sizeof(line->cl_Dow)); + } +} + + + +static void SynchronizeFile(const char *fileName) +{ + int maxEntries = MAXLINES; + int maxLines; + char buf[1024]; + + if (strcmp(fileName, "root") == 0) { + maxEntries = 65535; + } + maxLines = maxEntries * 10; + + if (fileName) { + FILE *fi; + + DeleteFile(fileName); + + fi = fopen(fileName, "r"); + if (fi != NULL) { + struct stat sbuf; + + if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) { + CronFile *file = calloc(1, sizeof(CronFile)); + CronLine **pline; + + file->cf_User = strdup(fileName); + pline = &file->cf_LineBase; + + while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) { + CronLine line; + char *ptr; + + if (buf[0]) { + buf[strlen(buf) - 1] = 0; + } + if (buf[0] == 0 || buf[0] == '#' || buf[0] == ' ' || buf[0] == '\t') { + continue; + } + if (--maxEntries == 0) { + break; + } + memset(&line, 0, sizeof(line)); + +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) { + crondlog("\111User %s Entry %s\n", fileName, buf); + } +#endif + + /* parse date ranges */ + ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf); + ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr); + ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr); + ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr); + ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr); + + /* check failure */ + if (ptr == NULL) { + continue; + } + + /* + * fix days and dow - if one is not * and the other + * is *, the other is set to 0, and vise-versa + */ + + FixDayDow(&line); + + *pline = calloc(1, sizeof(CronLine)); + **pline = line; + + /* copy command */ + (*pline)->cl_Shell = strdup(ptr); + +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) { + crondlog("\111 Command %s\n", ptr); + } +#endif + + pline = &((*pline)->cl_Next); + } + *pline = NULL; + + file->cf_Next = FileBase; + FileBase = file; + + if (maxLines == 0 || maxEntries == 0) { + crondlog("\111Maximum number of lines reached for user %s\n", fileName); + } + } + fclose(fi); + } + } +} + +static void CheckUpdates(void) +{ + FILE *fi; + char buf[256]; + + fi = fopen(CRONUPDATE, "r"); + if (fi != NULL) { + remove(CRONUPDATE); + while (fgets(buf, sizeof(buf), fi) != NULL) { + SynchronizeFile(strtok(buf, " \t\r\n")); + } + fclose(fi); + } +} + +static void SynchronizeDir(void) +{ + /* Attempt to delete the database. */ + + for (;;) { + CronFile *file; + + for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next); + if (file == NULL) { + break; + } + DeleteFile(file->cf_User); + } + + /* + * Remove cron update file + * + * Re-chdir, in case directory was renamed & deleted, or otherwise + * screwed up. + * + * scan directory and add associated users + */ + + remove(CRONUPDATE); + if (chdir(CDir) < 0) { + crondlog("\311unable to find %s\n", CDir); + } + { + DIR *dir = opendir("."); + struct dirent *den; + + if (dir) { + while ((den = readdir(dir))) { + if (strchr(den->d_name, '.') != NULL) { + continue; + } + if (getpwnam(den->d_name)) { + SynchronizeFile(den->d_name); + } else { + crondlog("\007ignoring %s\n", den->d_name); + } + } + closedir(dir); + } else { + crondlog("\311Unable to open current dir!\n"); + } + } +} + + +/* + * DeleteFile() - delete user database + * + * Note: multiple entries for same user may exist if we were unable to + * completely delete a database due to running processes. + */ + +static void DeleteFile(const char *userName) +{ + CronFile **pfile = &FileBase; + CronFile *file; + + while ((file = *pfile) != NULL) { + if (strcmp(userName, file->cf_User) == 0) { + CronLine **pline = &file->cf_LineBase; + CronLine *line; + + file->cf_Running = 0; + file->cf_Deleted = 1; + + while ((line = *pline) != NULL) { + if (line->cl_Pid > 0) { + file->cf_Running = 1; + pline = &line->cl_Next; + } else { + *pline = line->cl_Next; + free(line->cl_Shell); + free(line); + } + } + if (file->cf_Running == 0) { + *pfile = file->cf_Next; + free(file->cf_User); + free(file); + } else { + pfile = &file->cf_Next; + } + } else { + pfile = &file->cf_Next; + } + } +} + +/* + * TestJobs() + * + * determine which jobs need to be run. Under normal conditions, the + * period is about a minute (one scan). Worst case it will be one + * hour (60 scans). + */ + +static int TestJobs(time_t t1, time_t t2) +{ + short nJobs = 0; + time_t t; + + /* Find jobs > t1 and <= t2 */ + + for (t = t1 - t1 % 60; t <= t2; t += 60) { + if (t > t1) { + struct tm *tp = localtime(&t); + CronFile *file; + CronLine *line; + + for (file = FileBase; file; file = file->cf_Next) { +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) + crondlog("\005FILE %s:\n", file->cf_User); +#endif + if (file->cf_Deleted) + continue; + for (line = file->cf_LineBase; line; line = line->cl_Next) { +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) + crondlog("\005 LINE %s\n", line->cl_Shell); +#endif + if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] && + (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday]) + && line->cl_Mons[tp->tm_mon]) { +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) { + crondlog("\005 JobToDo: %d %s\n", + line->cl_Pid, line->cl_Shell); + } +#endif + if (line->cl_Pid > 0) { + crondlog("\010 process already running: %s %s\n", + file->cf_User, line->cl_Shell); + } else if (line->cl_Pid == 0) { + line->cl_Pid = -1; + file->cf_Ready = 1; + ++nJobs; + } + } + } + } + } + } + return (nJobs); +} + +static void RunJobs(void) +{ + CronFile *file; + CronLine *line; + + for (file = FileBase; file; file = file->cf_Next) { + if (file->cf_Ready) { + file->cf_Ready = 0; + + for (line = file->cf_LineBase; line; line = line->cl_Next) { + if (line->cl_Pid < 0) { + + RunJob(file->cf_User, line); + + crondlog("\010USER %s pid %3d cmd %s\n", + file->cf_User, line->cl_Pid, line->cl_Shell); + if (line->cl_Pid < 0) { + file->cf_Ready = 1; + } + else if (line->cl_Pid > 0) { + file->cf_Running = 1; + } + } + } + } + } +} + +/* + * CheckJobs() - check for job completion + * + * Check for job completion, return number of jobs still running after + * all done. + */ + +static int CheckJobs(void) +{ + CronFile *file; + CronLine *line; + int nStillRunning = 0; + + for (file = FileBase; file; file = file->cf_Next) { + if (file->cf_Running) { + file->cf_Running = 0; + + for (line = file->cf_LineBase; line; line = line->cl_Next) { + if (line->cl_Pid > 0) { + int status; + int r = wait4(line->cl_Pid, &status, WNOHANG, NULL); + + if (r < 0 || r == line->cl_Pid) { + EndJob(file->cf_User, line); + if (line->cl_Pid) { + file->cf_Running = 1; + } + } else if (r == 0) { + file->cf_Running = 1; + } + } + } + } + nStillRunning += file->cf_Running; + } + return (nStillRunning); +} + + +#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL +static void +ForkJob(const char *user, CronLine * line, int mailFd, + const char *prog, const char *cmd, const char *arg, const char *mailf) +{ + /* Fork as the user in question and run program */ + pid_t pid = fork(); + + line->cl_Pid = pid; + if (pid == 0) { + /* CHILD */ + + /* Change running state to the user in question */ + + if (ChangeUser(user) < 0) { + exit(0); + } +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) { + crondlog("\005Child Running %s\n", prog); + } +#endif + + if (mailFd >= 0) { + dup2(mailFd, mailf != NULL); + dup2((mailf ? mailFd : 1), 2); + close(mailFd); + } + execl(prog, prog, cmd, arg, NULL); + crondlog("\024unable to exec, user %s cmd %s %s %s\n", user, prog, cmd, arg); + if (mailf) { + fdprintf(1, "Exec failed: %s -c %s\n", prog, arg); + } + exit(0); + } else if (pid < 0) { + /* FORK FAILED */ + crondlog("\024couldn't fork, user %s\n", user); + line->cl_Pid = 0; + if (mailf) { + remove(mailf); + } + } else if (mailf) { + /* PARENT, FORK SUCCESS + * rename mail-file based on pid of process + */ + char mailFile2[128]; + + snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid); + rename(mailf, mailFile2); + } + /* + * Close the mail file descriptor.. we can't just leave it open in + * a structure, closing it later, because we might run out of descriptors + */ + + if (mailFd >= 0) { + close(mailFd); + } +} + +static void RunJob(const char *user, CronLine * line) +{ + char mailFile[128]; + int mailFd; + + line->cl_Pid = 0; + line->cl_MailFlag = 0; + + /* open mail file - owner root so nobody can screw with it. */ + + snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid()); + mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); + + if (mailFd >= 0) { + line->cl_MailFlag = 1; + fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user, + line->cl_Shell); + line->cl_MailPos = lseek(mailFd, 0, 1); + } else { + crondlog("\024unable to create mail file user %s file %s, output to /dev/null\n", user, mailFile); + } + + ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile); +} + +/* + * EndJob - called when job terminates and when mail terminates + */ + +static void EndJob(const char *user, CronLine * line) +{ + int mailFd; + char mailFile[128]; + struct stat sbuf; + + /* No job */ + + if (line->cl_Pid <= 0) { + line->cl_Pid = 0; + return; + } + + /* + * End of job and no mail file + * End of sendmail job + */ + + snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid); + line->cl_Pid = 0; + + if (line->cl_MailFlag != 1) { + return; + } + line->cl_MailFlag = 0; + + /* + * End of primary job - check for mail file. If size has increased and + * the file is still valid, we sendmail it. + */ + + mailFd = open(mailFile, O_RDONLY); + remove(mailFile); + if (mailFd < 0) { + return; + } + + if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid || sbuf.st_nlink != 0 || + sbuf.st_size == line->cl_MailPos || !S_ISREG(sbuf.st_mode)) { + close(mailFd); + return; + } + ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL); +} +#else +/* crond without sendmail */ + +static void RunJob(const char *user, CronLine * line) +{ + /* Fork as the user in question and run program */ + pid_t pid = fork(); + + if (pid == 0) { + /* CHILD */ + + /* Change running state to the user in question */ + + if (ChangeUser(user) < 0) { + exit(0); + } +#ifdef FEATURE_DEBUG_OPT + if (DebugOpt) { + crondlog("\005Child Running %s\n", DEFAULT_SHELL); + } +#endif + + execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL); + crondlog("\024unable to exec, user %s cmd %s -c %s\n", user, + DEFAULT_SHELL, line->cl_Shell); + exit(0); + } else if (pid < 0) { + /* FORK FAILED */ + crondlog("\024couldn't fork, user %s\n", user); + pid = 0; + } + line->cl_Pid = pid; +} +#endif /* CONFIG_FEATURE_CROND_CALL_SENDMAIL */ diff --git a/busybox/miscutils/crontab.c b/busybox/miscutils/crontab.c new file mode 100644 index 000000000..89e13775f --- /dev/null +++ b/busybox/miscutils/crontab.c @@ -0,0 +1,368 @@ +/* + * CRONTAB + * + * usually setuid root, -c option only works if getuid() == geteuid() + * + * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com) + * May be distributed under the GNU General Public License + * + * Vladimir Oleynik (C) 2002 to be used in busybox + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CRONTABS +#define CRONTABS "/var/spool/cron/crontabs" +#endif +#ifndef TMPDIR +#define TMPDIR "/var/spool/cron" +#endif +#ifndef CRONUPDATE +#define CRONUPDATE "cron.update" +#endif +#ifndef PATH_VI +#define PATH_VI "/bin/vi" /* location of vi */ +#endif + +#include "busybox.h" + +static const char *CDir = CRONTABS; + +static void EditFile(const char *user, const char *file); +static int GetReplaceStream(const char *user, const char *file); +static int ChangeUser(const char *user, short dochdir); + +int +crontab_main(int ac, char **av) +{ + enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE; + const struct passwd *pas; + const char *repFile = NULL; + int repFd = 0; + int i; + char caller[256]; /* user that ran program */ + int UserId; + + UserId = getuid(); + if ((pas = getpwuid(UserId)) == NULL) + bb_perror_msg_and_die("getpwuid"); + + strncpy(caller, pas->pw_name, sizeof(caller)); + + i = 1; + if (ac > 1) { + if (av[1][0] == '-' && av[1][1] == 0) { + option = REPLACE; + ++i; + } else if (av[1][0] != '-') { + option = REPLACE; + ++i; + repFile = av[1]; + } + } + + for (; i < ac; ++i) { + char *ptr = av[i]; + + if (*ptr != '-') + break; + ptr += 2; + + switch(ptr[-1]) { + case 'l': + if (ptr[-1] == 'l') + option = LIST; + /* fall through */ + case 'e': + if (ptr[-1] == 'e') + option = EDIT; + /* fall through */ + case 'd': + if (ptr[-1] == 'd') + option = DELETE; + /* fall through */ + case 'u': + if (i + 1 < ac && av[i+1][0] != '-') { + ++i; + if (getuid() == geteuid()) { + pas = getpwnam(av[i]); + if (pas) { + UserId = pas->pw_uid; + } else { + bb_error_msg_and_die("user %s unknown", av[i]); + } + } else { + bb_error_msg_and_die("only the superuser may specify a user"); + } + } + break; + case 'c': + if (getuid() == geteuid()) { + CDir = (*ptr) ? ptr : av[++i]; + } else { + bb_error_msg_and_die("-c option: superuser only"); + } + break; + default: + i = ac; + break; + } + } + if (i != ac || option == NONE) + bb_show_usage(); + + /* + * Get password entry + */ + + if ((pas = getpwuid(UserId)) == NULL) + bb_perror_msg_and_die("getpwuid"); + + /* + * If there is a replacement file, obtain a secure descriptor to it. + */ + + if (repFile) { + repFd = GetReplaceStream(caller, repFile); + if (repFd < 0) + bb_error_msg_and_die("unable to read replacement file"); + } + + /* + * Change directory to our crontab directory + */ + + if (chdir(CDir) < 0) + bb_perror_msg_and_die("cannot change dir to %s", CDir); + + /* + * Handle options as appropriate + */ + + switch(option) { + case LIST: + { + FILE *fi; + char buf[1024]; + + if ((fi = fopen(pas->pw_name, "r"))) { + while (fgets(buf, sizeof(buf), fi) != NULL) + fputs(buf, stdout); + fclose(fi); + } else { + bb_error_msg("no crontab for %s", pas->pw_name); + } + } + break; + case EDIT: + { + FILE *fi; + int fd; + int n; + char tmp[128]; + char buf[1024]; + + snprintf(tmp, sizeof(tmp), TMPDIR "/crontab.%d", getpid()); + if ((fd = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600)) >= 0) { + chown(tmp, getuid(), getgid()); + if ((fi = fopen(pas->pw_name, "r"))) { + while ((n = fread(buf, 1, sizeof(buf), fi)) > 0) + write(fd, buf, n); + } + EditFile(caller, tmp); + remove(tmp); + lseek(fd, 0L, 0); + repFd = fd; + } else { + bb_error_msg_and_die("unable to create %s", tmp); + } + + } + option = REPLACE; + /* fall through */ + case REPLACE: + { + char buf[1024]; + char path[1024]; + int fd; + int n; + + snprintf(path, sizeof(path), "%s.new", pas->pw_name); + if ((fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600)) >= 0) { + while ((n = read(repFd, buf, sizeof(buf))) > 0) { + write(fd, buf, n); + } + close(fd); + rename(path, pas->pw_name); + } else { + bb_error_msg("unable to create %s/%s", CDir, path); + } + close(repFd); + } + break; + case DELETE: + remove(pas->pw_name); + break; + case NONE: + default: + break; + } + + /* + * Bump notification file. Handle window where crond picks file up + * before we can write our entry out. + */ + + if (option == REPLACE || option == DELETE) { + FILE *fo; + struct stat st; + + while ((fo = fopen(CRONUPDATE, "a"))) { + fprintf(fo, "%s\n", pas->pw_name); + fflush(fo); + if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) { + fclose(fo); + break; + } + fclose(fo); + /* loop */ + } + if (fo == NULL) { + bb_error_msg("unable to append to %s/%s", CDir, CRONUPDATE); + } + } + return 0; +} + +static int +GetReplaceStream(const char *user, const char *file) +{ + int filedes[2]; + int pid; + int fd; + int n; + char buf[1024]; + + if (pipe(filedes) < 0) { + perror("pipe"); + return(-1); + } + if ((pid = fork()) < 0) { + perror("fork"); + return(-1); + } + if (pid > 0) { + /* + * PARENT + */ + + close(filedes[1]); + if (read(filedes[0], buf, 1) != 1) { + close(filedes[0]); + filedes[0] = -1; + } + return(filedes[0]); + } + + /* + * CHILD + */ + + close(filedes[0]); + + if (ChangeUser(user, 0) < 0) + exit(0); + + fd = open(file, O_RDONLY); + if (fd < 0) { + bb_error_msg("unable to open %s", file); + exit(0); + } + buf[0] = 0; + write(filedes[1], buf, 1); + while ((n = read(fd, buf, sizeof(buf))) > 0) { + write(filedes[1], buf, n); + } + exit(0); +} + +static void +EditFile(const char *user, const char *file) +{ + int pid; + + if ((pid = fork()) == 0) { + /* + * CHILD - change user and run editor + */ + char *ptr; + char visual[1024]; + + if (ChangeUser(user, 1) < 0) + exit(0); + if ((ptr = getenv("VISUAL")) == NULL || strlen(ptr) > 256) + ptr = PATH_VI; + + snprintf(visual, sizeof(visual), "%s %s", ptr, file); + execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", visual, NULL); + perror("exec"); + exit(0); + } + if (pid < 0) { + /* + * PARENT - failure + */ + bb_perror_msg_and_die("fork"); + } + wait4(pid, NULL, 0, NULL); +} + +static int +ChangeUser(const char *user, short dochdir) +{ + struct passwd *pas; + + /* + * Obtain password entry and change privileges + */ + + if ((pas = getpwnam(user)) == 0) { + bb_perror_msg_and_die("failed to get uid for %s", user); + return(-1); + } + setenv("USER", pas->pw_name, 1); + setenv("HOME", pas->pw_dir, 1); + setenv("SHELL", DEFAULT_SHELL, 1); + + /* + * Change running state to the user in question + */ + change_identity(pas); + + if (dochdir) { + if (chdir(pas->pw_dir) < 0) { + bb_perror_msg_and_die("chdir failed: %s %s", user, pas->pw_dir); + if (chdir(TMPDIR) < 0) { + bb_perror_msg_and_die("chdir failed: %s %s", user, TMPDIR); + return(-1); + } + } + } + return(pas->pw_uid); +} diff --git a/busybox/miscutils/dc.c b/busybox/miscutils/dc.c new file mode 100644 index 000000000..112f6df3f --- /dev/null +++ b/busybox/miscutils/dc.c @@ -0,0 +1,228 @@ +/* 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 unsigned char base; + +static void push(double a) +{ + if (pointer >= (sizeof(stack) / sizeof(*stack))) + bb_error_msg_and_die("stack overflow"); + stack[pointer++] = a; +} + +static double pop(void) +{ + if (pointer == 0) + bb_error_msg_and_die("stack underflow"); + return stack[--pointer]; +} + +static void add(void) +{ + push(pop() + pop()); +} + +static void sub(void) +{ + double subtrahend = pop(); + + push(pop() - subtrahend); +} + +static void mul(void) +{ + push(pop() * pop()); +} + +static void power(void) +{ + double topower = pop(); + + push(pow(pop(), topower)); +} + +static void divide(void) +{ + double divisor = pop(); + + push(pop() / divisor); +} + +static void mod(void) +{ + unsigned int d = pop(); + + push((unsigned int) pop() % d); +} + +static void and(void) +{ + push((unsigned int) pop() & (unsigned int) pop()); +} + +static void or(void) +{ + push((unsigned int) pop() | (unsigned int) pop()); +} + +static void eor(void) +{ + push((unsigned int) pop() ^ (unsigned int) pop()); +} + +static void not(void) +{ + push(~(unsigned int) pop()); +} + +static void set_output_base(void) +{ + base=(unsigned char)pop(); + if ((base != 10) && (base != 16)) { + fprintf(stderr, "Error: base = %d is not supported.\n", base); + base=10; + } +} + +static void print_base(double print) +{ + if (base == 16) + printf("%x\n", (unsigned int)print); + else + printf("%g\n", print); +} + +static void print_stack_no_pop(void) +{ + unsigned int i=pointer; + while (i) + print_base(stack[--i]); +} + +static void print_no_pop(void) +{ + print_base(stack[pointer-1]); +} + +struct op { + const char *name; + void (*function) (void); +}; + +static const struct op operators[] = { + {"+", add}, + {"add", add}, + {"-", sub}, + {"sub", sub}, + {"*", mul}, + {"mul", mul}, + {"/", divide}, + {"div", divide}, + {"**", power}, + {"exp", power}, + {"pow", power}, + {"%", mod}, + {"mod", mod}, + {"and", and}, + {"or", or}, + {"not", not}, + {"eor", eor}, + {"xor", eor}, + {"p", print_no_pop}, + {"f", print_stack_no_pop}, + {"o", set_output_base}, + {0, 0} +}; + +static void stack_machine(const char *argument) +{ + char *endPointer = 0; + double d; + const struct op *o = operators; + + if (argument == 0) + return; + + d = strtod(argument, &endPointer); + + if (endPointer != argument) { + push(d); + return; + } + + while (o->name != 0) { + if (strcmp(o->name, argument) == 0) { + (*(o->function)) (); + return; + } + o++; + } + bb_error_msg_and_die("%s: syntax error.", argument); +} + +/* return pointer to next token in buffer and set *buffer to one char + * past the end of the above mentioned token + */ +static char *get_token(char **buffer) +{ + char *start = NULL; + char *current = *buffer; + + while (isspace(*current)) { current++; } + if (*current != 0) { + start = current; + while (!isspace(*current) && *current != 0) { current++; } + *buffer = current; + } + return start; +} + +/* In Perl one might say, scalar m|\s*(\S+)\s*|g */ +static int number_of_tokens(char *buffer) +{ + int i = 0; + char *b = buffer; + while (get_token(&b)) { i++; } + return i; +} + +int dc_main(int argc, char **argv) +{ + /* take stuff from stdin if no args are given */ + if (argc <= 1) { + int i, len; + char *line = NULL; + char *cursor = NULL; + char *token = NULL; + while ((line = bb_get_chomped_line_from_file(stdin))) { + cursor = line; + len = number_of_tokens(line); + for (i = 0; i < len; i++) { + token = get_token(&cursor); + *cursor++ = 0; + stack_machine(token); + } + free(line); + } + } else { + if (*argv[1]=='-') + bb_show_usage(); + while (argc >= 2) { + stack_machine(argv[1]); + argv++; + argc--; + } + } + stack_machine(0); + return EXIT_SUCCESS; +} diff --git a/busybox/miscutils/devfsd.c b/busybox/miscutils/devfsd.c new file mode 100644 index 000000000..5e183e61f --- /dev/null +++ b/busybox/miscutils/devfsd.c @@ -0,0 +1,2183 @@ +/* + devfsd implementation for busybox + + Copyright (C) 2003 by Tito Ragusa + + Busybox version is based on some previous work and ideas + Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it> + + devfsd.c + + Main file for devfsd (devfs daemon for Linux). + + Copyright (C) 1998-2002 Richard Gooch + + devfsd.h + + Header file for devfsd (devfs daemon for Linux). + + Copyright (C) 1998-2000 Richard Gooch + + compat_name.c + + Compatibility name file for devfsd (build compatibility names). + + Copyright (C) 1998-2002 Richard Gooch + + expression.c + + This code provides Borne Shell-like expression expansion. + + Copyright (C) 1997-1999 Richard Gooch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. +*/ + +#include "libbb.h" +#include "busybox.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Various defines taken from linux/major.h */ +#define IDE0_MAJOR 3 +#define IDE1_MAJOR 22 +#define IDE2_MAJOR 33 +#define IDE3_MAJOR 34 +#define IDE4_MAJOR 56 +#define IDE5_MAJOR 57 +#define IDE6_MAJOR 88 +#define IDE7_MAJOR 89 +#define IDE8_MAJOR 90 +#define IDE9_MAJOR 91 + + +/* Various defines taken from linux/devfs_fs.h */ +#define DEVFSD_PROTOCOL_REVISION_KERNEL 5 +#define DEVFSD_IOCTL_BASE 'd' +/* These are the various ioctls */ +#define DEVFSDIOC_GET_PROTO_REV _IOR(DEVFSD_IOCTL_BASE, 0, int) +#define DEVFSDIOC_SET_EVENT_MASK _IOW(DEVFSD_IOCTL_BASE, 2, int) +#define DEVFSDIOC_RELEASE_EVENT_QUEUE _IOW(DEVFSD_IOCTL_BASE, 3, int) +#define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int) +#define DEVFSD_NOTIFY_REGISTERED 0 +#define DEVFSD_NOTIFY_UNREGISTERED 1 +#define DEVFSD_NOTIFY_ASYNC_OPEN 2 +#define DEVFSD_NOTIFY_CLOSE 3 +#define DEVFSD_NOTIFY_LOOKUP 4 +#define DEVFSD_NOTIFY_CHANGE 5 +#define DEVFSD_NOTIFY_CREATE 6 +#define DEVFSD_NOTIFY_DELETE 7 +#define DEVFS_PATHLEN 1024 +/* Never change this otherwise the binary interface will change */ + +struct devfsd_notify_struct +{ /* Use native C types to ensure same types in kernel and user space */ + unsigned int type; /* DEVFSD_NOTIFY_* value */ + unsigned int mode; /* Mode of the inode or device entry */ + unsigned int major; /* Major number of device entry */ + unsigned int minor; /* Minor number of device entry */ + unsigned int uid; /* Uid of process, inode or device entry */ + unsigned int gid; /* Gid of process, inode or device entry */ + unsigned int overrun_count; /* Number of lost events */ + unsigned int namelen; /* Number of characters not including '\0' */ + /* The device name MUST come last */ + char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */ +}; + +#define BUFFER_SIZE 16384 +#define DEVFSD_VERSION "1.3.25" +#define CONFIG_FILE "/etc/devfsd.conf" +#define MODPROBE "/sbin/modprobe" +#define MODPROBE_SWITCH_1 "-k" +#define MODPROBE_SWITCH_2 "-C" +#define CONFIG_MODULES_DEVFS "/etc/modules.devfs" +#define MAX_ARGS (6 + 1) +#define MAX_SUBEXPR 10 +#define STRING_LENGTH 255 + +/* for get_uid_gid() */ +#define UID 0 +#define GID 1 + +/* for msg_logger(), do_ioctl(), + fork_and_execute() */ +# define DIE 1 +# define NO_DIE 0 + +/* for dir_operation() */ +#define RESTORE 0 +#define SERVICE 1 +#define READ_CONFIG 2 + +/* Update only after changing code to reflect new protocol */ +#define DEVFSD_PROTOCOL_REVISION_DAEMON 5 + +/* Compile-time check */ +#if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON +#error protocol version mismatch. Update your kernel headers +#endif + +#define AC_PERMISSIONS 0 +#define AC_MODLOAD 1 +#define AC_EXECUTE 2 +#define AC_MFUNCTION 3 /* not supported by busybox */ +#define AC_CFUNCTION 4 /* not supported by busybox */ +#define AC_COPY 5 +#define AC_IGNORE 6 +#define AC_MKOLDCOMPAT 7 +#define AC_MKNEWCOMPAT 8 +#define AC_RMOLDCOMPAT 9 +#define AC_RMNEWCOMPAT 10 +#define AC_RESTORE 11 + + +struct permissions_type +{ + mode_t mode; + uid_t uid; + gid_t gid; +}; + +struct execute_type +{ + char *argv[MAX_ARGS + 1]; /* argv[0] must always be the programme */ +}; + +struct copy_type +{ + const char *source; + const char *destination; +}; + +struct action_type +{ + unsigned int what; + unsigned int when; +}; + +struct config_entry_struct +{ + struct action_type action; + regex_t preg; + union + { + struct permissions_type permissions; + struct execute_type execute; + struct copy_type copy; + } + u; + struct config_entry_struct *next; +}; + +struct get_variable_info +{ + const struct devfsd_notify_struct *info; + const char *devname; + char devpath[STRING_LENGTH]; +}; + +static void dir_operation(int , const char * , int, unsigned long* ); +static void service(struct stat statbuf, char *path); +static int st_expr_expand(char *, unsigned, const char *, const char *(*) (const char *, void *), void *); +static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned); +static int mksymlink (const char *oldpath, const char *newpath); +static void read_config_file (char *path, int optional, unsigned long *event_mask); +static void process_config_line (const char *, unsigned long *); +static int do_servicing (int, unsigned long); +static void service_name (const struct devfsd_notify_struct *); +static void action_permissions (const struct devfsd_notify_struct *, const struct config_entry_struct *); +static void action_execute (const struct devfsd_notify_struct *, const struct config_entry_struct *, + const regmatch_t *, unsigned); +#ifdef CONFIG_DEVFSD_MODLOAD +static void action_modload (const struct devfsd_notify_struct *info, const struct config_entry_struct *entry); +#endif +static void action_copy (const struct devfsd_notify_struct *, const struct config_entry_struct *, + const regmatch_t *, unsigned); +static void action_compat (const struct devfsd_notify_struct *, unsigned); +static void free_config (void); +static void restore(char *spath, struct stat source_stat, int rootlen); +static int copy_inode (const char *, const struct stat *, mode_t, const char *, const struct stat *); +static mode_t get_mode (const char *); +static void signal_handler (int); +static const char *get_variable (const char *, void *); +static int make_dir_tree (const char *); +static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *, + const char *, const regmatch_t *, unsigned ); +static void expand_regexp (char *, size_t, const char *, const char *, const regmatch_t *, unsigned ); +static const char *expand_variable( char *, unsigned, unsigned *, const char *, + const char *(*) (const char *, void *), void * ); +static const char *get_variable_v2(const char *, const char *(*) (const char *, void *), void *); +static char get_old_ide_name (unsigned , unsigned); +static char *write_old_sd_name (char *, unsigned, unsigned, char *); + +/* busybox functions */ +#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG) +static void msg_logger(int die, int pri, const char * fmt, ... ); +#endif +static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag); +static void fork_and_execute(int die, char *arg0, char **arg ); +static int get_uid_gid ( int, const char *); +static void safe_memcpy( char * dest, const char * src, int len); +static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr); +static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr); + +/* Structs and vars */ +static struct config_entry_struct *first_config = NULL; +static struct config_entry_struct *last_config = NULL; +static const char *mount_point = NULL; +static volatile int caught_signal = FALSE; +static volatile int caught_sighup = FALSE; +static struct initial_symlink_struct +{ + char *dest; + char *name; +} initial_symlinks[] = +{ + {"/proc/self/fd", "fd"}, + {"fd/0", "stdin"}, + {"fd/1", "stdout"}, + {"fd/2", "stderr"}, + {NULL, NULL}, +}; + +static struct event_type +{ + unsigned int type; /* The DEVFSD_NOTIFY_* value */ + const char *config_name; /* The name used in the config file */ +} event_types[] = +{ + {DEVFSD_NOTIFY_REGISTERED, "REGISTER"}, + {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"}, + {DEVFSD_NOTIFY_ASYNC_OPEN, "ASYNC_OPEN"}, + {DEVFSD_NOTIFY_CLOSE, "CLOSE"}, + {DEVFSD_NOTIFY_LOOKUP, "LOOKUP"}, + {DEVFSD_NOTIFY_CHANGE, "CHANGE"}, + {DEVFSD_NOTIFY_CREATE, "CREATE"}, + {DEVFSD_NOTIFY_DELETE, "DELETE"}, + {0xffffffff, NULL} +}; + +/* busybox functions and messages */ + +extern void xregcomp(regex_t * preg, const char *regex, int cflags); + +const char * const bb_msg_proto_rev = "protocol revision"; +#ifdef CONFIG_DEVFSD_VERBOSE +const char * const bb_msg_bad_config = "bad %s config file: %s\n"; +const char * const bb_msg_small_buffer = "buffer too small\n"; +const char * const bb_msg_variable_not_found = "variable: %s not found\n"; +#endif + +#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG) +static void msg_logger(int die, int pri, const char * fmt, ... ) +{ + va_list ap; + + va_start(ap, fmt); + if (access ("/dev/log", F_OK) == 0) + { + openlog(bb_applet_name, 0, LOG_DAEMON); + vsyslog( pri , fmt , ap); + closelog(); + } +#ifndef CONFIG_DEBUG + else +#endif + bb_verror_msg(fmt, ap); + va_end(ap); + if(die==DIE) + exit(EXIT_FAILURE); +} +#endif + +static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag) +{ +#ifdef CONFIG_DEVFSD_VERBOSE + if (ioctl (fd, request, event_mask_flag) == -1) + msg_logger(die, LOG_ERR, "ioctl(): %m\n"); +#else + if (ioctl (fd, request, event_mask_flag) == -1) + exit(EXIT_FAILURE); +#endif +} + +static void fork_and_execute(int die, char *arg0, char **arg ) +{ + switch ( fork () ) + { + case 0: + /* Child */ + break; + case -1: + /* Parent: Error : die or return */ +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger(die, LOG_ERR,(char *) bb_msg_memory_exhausted); +#else + if(die == DIE) + exit(EXIT_FAILURE); +#endif + return; + default: + /* Parent : ok : return or exit */ + if(arg0 != NULL) + { + wait (NULL); + return; + } + exit (EXIT_SUCCESS); + } + /* Child : if arg0 != NULL do execvp */ + if(arg0 != NULL ) + { + execvp (arg0, arg); +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger(DIE, LOG_ERR, "execvp(): %s: %m\n", arg0); +#else + exit(EXIT_FAILURE); +#endif + } +} + +static void safe_memcpy( char *dest, const char *src, int len) +{ + memcpy (dest , src , len ); + dest[len] = '\0'; +} + +static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr) +{ + if( d[n - 4]=='d' && d[n - 3]=='i' && d[n - 2]=='s' && d[n - 1]=='c') + return (2 + addendum); + else if( d[n - 2]=='c' && d[n - 1]=='d') + return (3 + addendum); + else if(ptr[0]=='p' && ptr[1]=='a' && ptr[2]=='r' && ptr[3]=='t') + return (4 + addendum); + else if( ptr[n - 2]=='m' && ptr[n - 1]=='t') + return (5 + addendum); + else + return 0; +} + +static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr) +{ + if(d[0]=='s' && d[1]=='c' && d[2]=='s' && d[3]=='i' && d[4]=='/') + { + if( d[n - 7]=='g' && d[n - 6]=='e' && d[n - 5]=='n' && + d[n - 4]=='e' && d[n - 3]=='r' && d[n - 2]=='i' && + d[n - 1]=='c' ) + return 1; + return scan_dev_name_common(d, n, 0, ptr); + } + else if(d[0]=='i' && d[1]=='d' && d[2]=='e' && d[3]=='/' && + d[4]=='h' && d[5]=='o' && d[6]=='s' && d[7]=='t') + { + return scan_dev_name_common(d, n, 4, ptr); + } + else if(d[0]=='s' && d[1]=='b' && d[2]=='p' && d[3]=='/') + { + return 10; + } + else if(d[0]=='v' && d[1]=='c' && d[2]=='c' && d[3]=='/') + { + return 11; + } + else if(d[0]=='p' && d[1]=='t' && d[2]=='y' && d[3]=='/') + { + return 12; + } + return 0; +} + +/* Public functions follow */ + +int devfsd_main (int argc, char **argv) +{ + int print_version = FALSE; +#ifdef CONFIG_DEVFSD_FG_NP + int do_daemon = TRUE; + int no_polling = FALSE; +#endif + int do_scan; + int fd, proto_rev, count; + unsigned long event_mask = 0; + struct sigaction new_action; + struct initial_symlink_struct *curr; + + if (argc < 2) + bb_show_usage(); + + for (count = 2; count < argc; ++count) + { + if(argv[count][0] == '-') + { + if(argv[count][1]=='v' && !argv[count][2]) /* -v */ + print_version = TRUE; +#ifdef CONFIG_DEVFSD_FG_NP + else if(argv[count][1]=='f' && argv[count][2]=='g' && !argv[count][3]) /* -fg */ + do_daemon = FALSE; + else if(argv[count][1]=='n' && argv[count][2]=='p' && !argv[count][3]) /* -np */ + no_polling = TRUE; +#endif + else + bb_show_usage(); + } + } + + /* strip last / from mount point, so we don't need to check for it later */ + while( argv[1][1]!='\0' && argv[1][strlen(argv[1])-1] == '/' ) + argv[1][strlen(argv[1]) -1] = '\0'; + + mount_point = argv[1]; + + if (chdir (mount_point) != 0) +#ifdef CONFIG_DEVFSD_VERBOSE + bb_error_msg_and_die( " %s: %m", mount_point); +#else + exit(EXIT_FAILURE); +#endif + + fd = bb_xopen (".devfsd", O_RDONLY); + + if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0) +#ifdef CONFIG_DEVFSD_VERBOSE + bb_error_msg( "FD_CLOEXEC"); +#else + exit(EXIT_FAILURE); +#endif + + do_ioctl(DIE, fd, DEVFSDIOC_GET_PROTO_REV,(int )&proto_rev); + + /*setup initial entries */ + for (curr = initial_symlinks; curr->dest != NULL; ++curr) + symlink (curr->dest, curr->name); + + /* NB: The check for CONFIG_FILE is done in read_config_file() */ + + if ( print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev) ) + { + bb_printf( "%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n", + bb_applet_name,DEVFSD_VERSION,bb_msg_proto_rev, + DEVFSD_PROTOCOL_REVISION_DAEMON,bb_msg_proto_rev, proto_rev); + if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev) + bb_error_msg_and_die( "%s mismatch!",bb_msg_proto_rev); + exit(EXIT_SUCCESS); /* -v */ + } + /* Tell kernel we are special (i.e. we get to see hidden entries) */ + do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, 0); + + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + /* Set up SIGHUP and SIGUSR1 handlers */ + new_action.sa_handler = signal_handler; + if (sigaction (SIGHUP, &new_action, NULL) != 0 || sigaction (SIGUSR1, &new_action, NULL) != 0 ) +#ifdef CONFIG_DEVFSD_VERBOSE + bb_error_msg_and_die( "sigaction()"); +#else + exit(EXIT_FAILURE); +#endif + + bb_printf("%s v%s started for %s\n",bb_applet_name, DEVFSD_VERSION, mount_point); + + /* Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions */ + umask (0); + read_config_file (CONFIG_FILE, FALSE, &event_mask); + /* Do the scan before forking, so that boot scripts see the finished product */ + dir_operation(SERVICE,mount_point,0,NULL); +#ifdef CONFIG_DEVFSD_FG_NP + if (no_polling) + exit (0); + if (do_daemon) + { +#endif + /* Release so that the child can grab it */ + do_ioctl(DIE, fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0); + fork_and_execute(DIE, NULL, NULL); + setsid (); /* Prevent hangups and become pgrp leader */ +#ifdef CONFIG_DEVFSD_FG_NP + } + else + setpgid (0, 0); /* Become process group leader */ +#endif + + while (TRUE) + { + do_scan = do_servicing (fd, event_mask); + + free_config (); + read_config_file (CONFIG_FILE, FALSE, &event_mask); + if (do_scan) + dir_operation(SERVICE,mount_point,0,NULL); + } +} /* End Function main */ + + +/* Private functions follow */ + +static void read_config_file (char *path, int optional, unsigned long *event_mask) +/* [SUMMARY] Read a configuration database. + The path to read the database from. If this is a directory, all + entries in that directory will be read (except hidden entries). + If TRUE, the routine will silently ignore a missing config file. + The event mask is written here. This is not initialised. + [RETURNS] Nothing. +*/ +{ + struct stat statbuf; + FILE *fp; + char buf[STRING_LENGTH]; + char *line=NULL; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "read_config_file(): %s\n", path); +#endif + if (stat (path, &statbuf) != 0 || statbuf.st_size == 0 ) + goto read_config_file_err; + + if ( S_ISDIR (statbuf.st_mode) ) + { + /* strip last / from dirname so we don't need to check for it later */ + while( path && path[1]!='\0' && path[strlen(path)-1] == '/') + path[strlen(path) -1] = '\0'; + + dir_operation(READ_CONFIG, path, 0, event_mask); + return; + } + + if ( ( fp = fopen (path, "r") ) != NULL ) + { + while (fgets (buf, STRING_LENGTH, fp) != NULL) + { + /* GETS(3) Linux Programmer's Manual + fgets() reads in at most one less than size characters from stream and + stores them into the buffer pointed to by s. Reading stops after an + EOF or a newline. If a newline is read, it is stored into the buffer. + A '\0' is stored after the last character in the buffer. + */ + /*buf[strlen (buf) - 1] = '\0';*/ + /* Skip whitespace */ + for (line = buf; isspace (*line); ++line) + /*VOID*/; + if (line[0] == '\0' || line[0] == '#' ) + continue; + process_config_line (line, event_mask); + } + fclose (fp); + errno=0; + } +read_config_file_err: +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger(((optional == 0 ) && (errno == ENOENT))? DIE : NO_DIE, LOG_ERR, "read config file: %s: %m\n", path); +#else + if(optional == 0 && errno == ENOENT) + exit(EXIT_FAILURE); +#endif + return; +} /* End Function read_config_file */ + +static void process_config_line (const char *line, unsigned long *event_mask) +/* [SUMMARY] Process a line from a configuration file. + The configuration line. + The event mask is written here. This is not initialised. + [RETURNS] Nothing. +*/ +{ + int num_args, count; + struct config_entry_struct *new; + char p[MAX_ARGS][STRING_LENGTH]; + char when[STRING_LENGTH], what[STRING_LENGTH]; + char name[STRING_LENGTH]; + char * msg=""; + char *ptr; + + /* !!!! Only Uppercase Keywords in devsfd.conf */ + const char *options[] = { "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE", + "PERMISSIONS", "MODLOAD", "EXECUTE", "COPY", "IGNORE", + "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT", 0 }; + + short int i; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "process_config_line()\n"); +#endif + + for (count = 0; count < MAX_ARGS; ++count) p[count][0] = '\0'; + num_args = sscanf (line, "%s %s %s %s %s %s %s %s %s %s", + when, name, what, + p[0], p[1], p[2], p[3], p[4], p[5], p[6]); + + i = compare_string_array(options, when ); + + /*"CLEAR_CONFIG"*/ + if( i == 0) + { + free_config (); + *event_mask = 0; + return; + } + + if ( num_args < 2) + goto process_config_line_err; + + /* "INCLUDE" & "OPTIONAL_INCLUDE" */ + if( i == 1 || i == 2 ) + { + st_expr_expand (name, STRING_LENGTH, name, get_variable, NULL ); +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "%sinclude: %s\n",(toupper (when[0]) == 'I') ? "": "optional_", name); +#endif + read_config_file (name, (toupper (when[0]) == 'I') ? FALSE : TRUE, event_mask); + return; + } + /* "RESTORE" */ + if( i == 3) + { + dir_operation(RESTORE,name, strlen (name),NULL); + return; + } + if (num_args < 3) + goto process_config_line_err; + + new = xmalloc (sizeof *new); + memset (new, 0, sizeof *new); + + for (count = 0; event_types[count].config_name != NULL; ++count) + { + if (strcasecmp (when, event_types[count].config_name) != 0) + continue; + new->action.when = event_types[count].type; + break; + } + if (event_types[count].config_name == NULL) + { + msg="WHEN in"; + goto process_config_line_err; + } + + i = compare_string_array(options, what ); + + switch(i) + { + case 4: /* "PERMISSIONS" */ + new->action.what = AC_PERMISSIONS; + /* Get user and group */ + if ( ( ptr = strchr (p[0], '.') ) == NULL ) + { + msg="UID.GID"; + goto process_config_line_err; /*"missing '.' in UID.GID */ + } + + *ptr++ = '\0'; + new->u.permissions.uid = get_uid_gid (UID, p[0]); + new->u.permissions.gid = get_uid_gid (GID, ptr); + /* Get mode */ + new->u.permissions.mode = get_mode (p[1]); + break; +#ifdef CONFIG_DEVFSD_MODLOAD + case 5: /* MODLOAD */ + /*This action will pass "/dev/$devname" (i.e. "/dev/" prefixed to + the device name) to the module loading facility. In addition, + the /etc/modules.devfs configuration file is used.*/ + new->action.what = AC_MODLOAD; + break; +#endif + case 6: /* EXECUTE */ + new->action.what = AC_EXECUTE; + num_args -= 3; + + for (count = 0; count < num_args; ++count) + new->u.execute.argv[count] = bb_xstrdup (p[count]); + + new->u.execute.argv[num_args] = NULL; + break; + case 7: /* COPY */ + new->action.what = AC_COPY; + num_args -= 3; + if (num_args != 2) + goto process_config_line_err; /* missing path and function in line */ + + new->u.copy.source = bb_xstrdup (p[0]); + new->u.copy.destination = bb_xstrdup (p[1]); + break; + case 8: /* IGNORE */ + /* FALLTROUGH */ + case 9: /* MKOLDCOMPAT */ + /* FALLTROUGH */ + case 10: /* MKNEWCOMPAT */ + /* FALLTROUGH */ + case 11:/* RMOLDCOMPAT */ + /* FALLTROUGH */ + case 12: /* RMNEWCOMPAT */ + /* AC_IGNORE 6 + AC_MKOLDCOMPAT 7 + AC_MKNEWCOMPAT 8 + AC_RMOLDCOMPAT 9 + AC_RMNEWCOMPAT 10*/ + new->action.what = i - 2; + break; + default: + msg ="WHAT in"; + goto process_config_line_err; + /*esac*/ + } /* switch (i) */ + + xregcomp( &new->preg, name, REG_EXTENDED); + + *event_mask |= 1 << new->action.when; + new->next = NULL; + if (first_config == NULL) + first_config = new; + else + last_config->next = new; + last_config = new; + return; +process_config_line_err: +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( DIE, LOG_ERR, bb_msg_bad_config, msg , line); +#else + exit(EXIT_FAILURE); +#endif +} /* End Function process_config_line */ + +static int do_servicing (int fd, unsigned long event_mask) +/* [SUMMARY] Service devfs changes until a signal is received. + The open control file. + The event mask. + [RETURNS] TRUE if SIGHUP was caught, else FALSE. +*/ +{ + ssize_t bytes; + struct devfsd_notify_struct info; + unsigned long tmp_event_mask; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "do_servicing()\n"); +#endif + /* Tell devfs what events we care about */ + tmp_event_mask = event_mask; + do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, tmp_event_mask); + while (!caught_signal) + { + errno = 0; + bytes = read (fd, (char *) &info, sizeof info); + if (caught_signal) + break; /* Must test for this first */ + if (errno == EINTR) + continue; /* Yes, the order is important */ + if (bytes < 1) + break; + service_name (&info); + } + if (caught_signal) + { + int c_sighup = caught_sighup; + + caught_signal = FALSE; + caught_sighup = FALSE; + return (c_sighup); + } +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_ERR, "read error on control file: %m\n"); +#endif + /* This is to shut up a compiler warning */ + exit(EXIT_FAILURE); +} /* End Function do_servicing */ + +static void service_name (const struct devfsd_notify_struct *info) +/* [SUMMARY] Service a single devfs change. + The devfs change. + [RETURNS] Nothing. +*/ +{ + unsigned int n; + regmatch_t mbuf[MAX_SUBEXPR]; + struct config_entry_struct *entry; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "service_name()\n"); + if (info->overrun_count > 0) + msg_logger( NO_DIE, LOG_ERR, "lost %u events\n", info->overrun_count); +#endif + + /* Discard lookups on "/dev/log" and "/dev/initctl" */ + if( info->type == DEVFSD_NOTIFY_LOOKUP && + ((info->devname[0]=='l' && info->devname[1]=='o' && + info->devname[2]=='g' && !info->devname[3]) || + ( info->devname[0]=='i' && info->devname[1]=='n' && + info->devname[2]=='i' && info->devname[3]=='t' && + info->devname[4]=='c' && info->devname[5]=='t' && + info->devname[6]=='l' && !info->devname[7]))) + return; + for (entry = first_config; entry != NULL; entry = entry->next) + { + /* First check if action matches the type, then check if name matches */ + if (info->type != entry->action.when || regexec (&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0 ) + continue; + for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n) + /* VOID */; +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "service_name(): action.what %d\n", entry->action.what); +#endif + switch (entry->action.what) + { + case AC_PERMISSIONS: + action_permissions (info, entry); + break; +#ifdef CONFIG_DEVFSD_MODLOAD + case AC_MODLOAD: + action_modload (info, entry); + break; +#endif + case AC_EXECUTE: + action_execute (info, entry, mbuf, n); + break; + case AC_COPY: + action_copy (info, entry, mbuf, n); + break; + case AC_IGNORE: + return; + /*break;*/ + case AC_MKOLDCOMPAT: + case AC_MKNEWCOMPAT: + case AC_RMOLDCOMPAT: + case AC_RMNEWCOMPAT: + action_compat (info, entry->action.what); + break; + default: +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( DIE, LOG_ERR, "Unknown action\n"); +#else + exit(EXIT_FAILURE); +#endif + /*break;*/ + } + } +} /* End Function service_name */ + +static void action_permissions (const struct devfsd_notify_struct *info, + const struct config_entry_struct *entry) +/* [SUMMARY] Update permissions for a device entry. + The devfs change. + The config file entry. + [RETURNS] Nothing. +*/ +{ + struct stat statbuf; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "action_permission()\n"); +#endif + + if ( stat (info->devname, &statbuf) != 0 || + chmod (info->devname,(statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 || + chown (info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0) + { +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_ERR, "chmod() or chown(): %s: %m\n",info->devname); +#endif + return; + } + +} /* End Function action_permissions */ + +#ifdef CONFIG_DEVFSD_MODLOAD +static void action_modload (const struct devfsd_notify_struct *info, + const struct config_entry_struct *entry) +/* [SUMMARY] Load a module. + The devfs change. + The config file entry. + [RETURNS] Nothing. +*/ +{ + char *argv[6]; + char device[STRING_LENGTH]; + + argv[0] = MODPROBE; + argv[1] = MODPROBE_SWITCH_1; /* "-k" */ + argv[2] = MODPROBE_SWITCH_2; /* "-C" */ + argv[3] = CONFIG_MODULES_DEVFS; + argv[4] = device; + argv[5] = NULL; + + snprintf (device, sizeof (device), "/dev/%s", info->devname); + #ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "action_modload():%s %s %s %s %s\n",argv[0],argv[1],argv[2],argv[3],argv[4]); + #endif + fork_and_execute(DIE, argv[0], argv); +} /* End Function action_modload */ +#endif + +static void action_execute (const struct devfsd_notify_struct *info, + const struct config_entry_struct *entry, + const regmatch_t *regexpr, unsigned int numexpr) +/* [SUMMARY] Execute a programme. + The devfs change. + The config file entry. + The number of subexpression (start, end) offsets within the + device name. + The number of elements within <>. + [RETURNS] Nothing. +*/ +{ + unsigned int count; + struct get_variable_info gv_info; + char *argv[MAX_ARGS + 1]; + char largv[MAX_ARGS + 1][STRING_LENGTH]; + +#ifdef CONFIG_DEBUG + int i; + char buff[512]; +#endif + + gv_info.info = info; + gv_info.devname = info->devname; + snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname); + for (count = 0; entry->u.execute.argv[count] != NULL; ++count) + { + expand_expression (largv[count], STRING_LENGTH, + entry->u.execute.argv[count], + get_variable, &gv_info, + gv_info.devname, regexpr, numexpr ); + argv[count] = largv[count]; + } + argv[count] = NULL; + +#ifdef CONFIG_DEBUG + buff[0]='\0'; + for(i=0;argv[i]!=NULL;i++) /* argv[i] < MAX_ARGS + 1 */ + { + strcat(buff," "); + if( (strlen(buff)+ 1 + strlen(argv[i])) >= 512) + break; + strcat(buff,argv[i]); + } + strcat(buff,"\n"); + msg_logger( NO_DIE, LOG_INFO, "action_execute(): %s",buff); +#endif + + fork_and_execute(NO_DIE, argv[0], argv); +} /* End Function action_execute */ + + +static void action_copy (const struct devfsd_notify_struct *info, + const struct config_entry_struct *entry, + const regmatch_t *regexpr, unsigned int numexpr) +/* [SUMMARY] Copy permissions. + The devfs change. + The config file entry. + This list of subexpression (start, end) offsets within the + device name. + The number of elements in <>. + [RETURNS] Nothing. +*/ +{ + mode_t new_mode; + struct get_variable_info gv_info; + struct stat source_stat, dest_stat; + char source[STRING_LENGTH], destination[STRING_LENGTH]; + dest_stat.st_mode = 0; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "action_copy()\n"); +#endif + + if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) ) + return; + gv_info.info = info; + gv_info.devname = info->devname; + + snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname); + expand_expression (source, STRING_LENGTH, entry->u.copy.source, + get_variable, &gv_info, gv_info.devname, + regexpr, numexpr); + + expand_expression (destination, STRING_LENGTH, entry->u.copy.destination, + get_variable, &gv_info, gv_info.devname, + regexpr, numexpr); + + if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0) + return; + lstat (destination, &dest_stat); + new_mode = source_stat.st_mode & ~S_ISVTX; + if (info->type == DEVFSD_NOTIFY_CREATE) + new_mode |= S_ISVTX; + else if ( (info->type == DEVFSD_NOTIFY_CHANGE) && (dest_stat.st_mode & S_ISVTX) ) + new_mode |= S_ISVTX; +#ifdef CONFIG_DEBUG + if ( !copy_inode (destination, &dest_stat, new_mode, source, &source_stat) && (errno != EEXIST)) + msg_logger( NO_DIE, LOG_ERR, "copy_inode(): %s to %s: %m\n", source, destination); +#else + copy_inode (destination, &dest_stat, new_mode, source, &source_stat); +#endif + return; +} /* End Function action_copy */ + +static void action_compat (const struct devfsd_notify_struct *info, unsigned int action) +/* [SUMMARY] Process a compatibility request. + The devfs change. + The action to take. + [RETURNS] Nothing. +*/ +{ + const char *compat_name = NULL; + const char *dest_name = info->devname; + char *ptr=NULL; + char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH]; + int mode, host, bus, target, lun; + unsigned int i; + char rewind_; + /* 1 to 5 "scsi/" , 6 to 9 "ide/host" */ + const char *fmt[] = { NULL , + "sg/c%db%dt%du%d", /* scsi/generic */ + "sd/c%db%dt%du%d", /* scsi/disc */ + "sr/c%db%dt%du%d", /* scsi/cd */ + "sd/c%db%dt%du%dp%d", /* scsi/part */ + "st/c%db%dt%du%dm%d%c", /* scsi/mt */ + "ide/hd/c%db%dt%du%d", /* ide/host/disc */ + "ide/cd/c%db%dt%du%d", /* ide/host/cd */ + "ide/hd/c%db%dt%du%dp%d", /* ide/host/part */ + "ide/mt/c%db%dt%du%d%s", /* ide/host/mt */ + NULL }; + + /* First construct compatibility name */ + switch (action) + { + case AC_MKOLDCOMPAT: + case AC_RMOLDCOMPAT: + compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor); + break; + case AC_MKNEWCOMPAT: + case AC_RMNEWCOMPAT: + ptr = strrchr (info->devname, '/') + 1; + i=scan_dev_name(info->devname, info->namelen, ptr); + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "action_compat(): scan_dev_name() returned %d\n", i); +#endif + + /* nothing found */ + if(i==0 || i > 9) + return; + + sscanf (info->devname +((i<6)?5:4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun); + snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname + ((i>5)?4:0)); + dest_name = dest_buf; + compat_name = compat_buf; + + + /* 1 == scsi/generic 2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */ + if( i == 1 || i == 2 || i == 3 || i == 6 || i ==7 ) + sprintf ( compat_buf, fmt[i], host, bus, target, lun); + + /* 4 == scsi/part 8 == ide/host/part */ + if( i == 4 || i == 8) + sprintf ( compat_buf, fmt[i], host, bus, target, lun, atoi (ptr + 4) ); + + /* 5 == scsi/mt */ + if( i == 5) + { + rewind_ = info->devname[info->namelen - 1]; + if (rewind_ != 'n') + rewind_ = '\0'; + mode=0; + if(ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/) + mode = ptr[2] - 107; /* 1 or 2 */ + if(ptr[2] == 'a') + mode = 3; + sprintf (compat_buf, fmt [i], host, bus, target, lun, mode, rewind_); + } + + /* 9 == ide/host/mt */ + if( i == 9 ) + snprintf (compat_buf, sizeof (compat_buf), fmt[i], host, bus, target, lun, ptr + 2); + /* esac */ + } /* switch(action) */ + + if(compat_name == NULL ) + return; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "action_compat(): %s\n", compat_name); +#endif + + /* Now decide what to do with it */ + switch (action) + { + case AC_MKOLDCOMPAT: + case AC_MKNEWCOMPAT: + mksymlink (dest_name, compat_name); + break; + case AC_RMOLDCOMPAT: + case AC_RMNEWCOMPAT: +#ifdef CONFIG_DEBUG + if (unlink (compat_name) != 0) + msg_logger( NO_DIE, LOG_ERR, "unlink(): %s: %m\n", compat_name); +#else + unlink (compat_name); +#endif + break; + /*esac*/ + } /* switch(action) */ +} /* End Function action_compat */ + +static void restore(char *spath, struct stat source_stat, int rootlen) +{ + char dpath[STRING_LENGTH]; + struct stat dest_stat; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "restore()\n"); +#endif + + dest_stat.st_mode = 0; + snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen); + lstat (dpath, &dest_stat); + + if ( S_ISLNK (source_stat.st_mode) || (source_stat.st_mode & S_ISVTX) ) + copy_inode (dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX) , spath, &source_stat); + + if ( S_ISDIR (source_stat.st_mode) ) + dir_operation(RESTORE, spath, rootlen,NULL); +} + + +static int copy_inode (const char *destpath, const struct stat *dest_stat, + mode_t new_mode, + const char *sourcepath, const struct stat *source_stat) +/* [SUMMARY] Copy an inode. + The destination path. An existing inode may be deleted. + The destination stat(2) information. + The desired new mode for the destination. + The source path. + The source stat(2) information. + [RETURNS] TRUE on success, else FALSE. +*/ +{ + int source_len, dest_len; + char source_link[STRING_LENGTH], dest_link[STRING_LENGTH]; + int fd, val; + struct sockaddr_un un_addr; + char symlink_val[STRING_LENGTH]; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "copy_inode()\n"); +#endif + + if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) ) + { + /* Same type */ + if ( S_ISLNK (source_stat->st_mode) ) + { + if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 || + ( dest_len = readlink (destpath , dest_link , STRING_LENGTH - 1) ) < 0 ) + return (FALSE); + source_link[source_len] = '\0'; + dest_link[dest_len] = '\0'; + if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) ) + { + unlink (destpath); + symlink (source_link, destpath); + } + return (TRUE); + } /* Else not a symlink */ + chmod (destpath, new_mode & ~S_IFMT); + chown (destpath, source_stat->st_uid, source_stat->st_gid); + return (TRUE); + } + /* Different types: unlink and create */ + unlink (destpath); + switch (source_stat->st_mode & S_IFMT) + { + case S_IFSOCK: + if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 ) + break; + un_addr.sun_family = AF_UNIX; + snprintf (un_addr.sun_path, sizeof (un_addr.sun_path), "%s", destpath); + val = bind (fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr); + close (fd); + if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0) + break; + goto do_chown; + case S_IFLNK: + if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 ) + break; + symlink_val[val] = '\0'; + if (symlink (symlink_val, destpath) == 0) + return (TRUE); + break; + case S_IFREG: + if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 ) + break; + close (fd); + if (chmod (destpath, new_mode & ~S_IFMT) != 0) + break; + goto do_chown; + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + if (mknod (destpath, new_mode, source_stat->st_rdev) != 0) + break; + goto do_chown; + case S_IFDIR: + if (mkdir (destpath, new_mode & ~S_IFMT) != 0) + break; +do_chown: + if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0) + return (TRUE); + /*break;*/ + } + return (FALSE); +} /* End Function copy_inode */ + +static void free_config () +/* [SUMMARY] Free the configuration information. + [RETURNS] Nothing. +*/ +{ + struct config_entry_struct *c_entry; + void *next; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "free_config()\n"); +#endif + + for (c_entry = first_config; c_entry != NULL; c_entry = next) + { + unsigned int count; + + next = c_entry->next; + regfree (&c_entry->preg); + if (c_entry->action.what == AC_EXECUTE) + { + for (count = 0; count < MAX_ARGS; ++count) + { + if (c_entry->u.execute.argv[count] == NULL) + break; + free (c_entry->u.execute.argv[count]); + } + } + free (c_entry); + } + first_config = NULL; + last_config = NULL; +} /* End Function free_config */ + +static int get_uid_gid (int flag, const char *string) +/* [SUMMARY] Convert a string to a UID or GID value. + "UID" or "GID". + The string. + [RETURNS] The UID or GID value. +*/ +{ + struct passwd *pw_ent; + struct group *grp_ent; +#ifdef CONFIG_DEVFSD_VERBOSE + char * msg="user"; +#endif + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_uid_gid()\n"); + + + if(flag != UID && flag != GID ) + msg_logger( DIE, LOG_ERR,"get_uid_gid(): flag != UID && flag != GID\n"); +#endif + + if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) ) + return atoi (string); + + if ( flag == UID && ( pw_ent = getpwnam (string) ) != NULL ) + return (pw_ent->pw_uid); + + if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL ) + return (grp_ent->gr_gid); +#ifdef CONFIG_DEVFSD_VERBOSE + else + msg="group"; + + msg_logger( NO_DIE, LOG_ERR,"unknown %s: %s, defaulting to %cID=0\n", msg, string, msg[0] - 32); +#endif + return (0); +}/* End Function get_uid_gid */ + +static mode_t get_mode (const char *string) +/* [SUMMARY] Convert a string to a mode value. + The string. + [RETURNS] The mode value. +*/ +{ + mode_t mode; + int i; +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_mode()\n"); +#endif + + if ( isdigit (string[0]) ) + return strtoul (string, NULL, 8); + if (strlen (string) != 9) +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( DIE, LOG_ERR, "bad mode: %s\n", string); +#else + exit(EXIT_FAILURE); +#endif + mode = 0; + i= S_IRUSR; + while(i>0) + { + if(string[0]=='r'||string[0]=='w'||string[0]=='x') + mode+=i; + i=i/2; + string++; + } + return (mode); +} /* End Function get_mode */ + +static void signal_handler (int sig) +{ +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "signal_handler()\n"); +#endif + + caught_signal = TRUE; + if (sig == SIGHUP) + caught_sighup = TRUE; +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_INFO, "Caught %s\n",(sig == SIGHUP)?"SIGHUP" : "SIGUSR1"); +#endif +} /* End Function signal_handler */ + +static const char *get_variable (const char *variable, void *info) +{ + struct get_variable_info *gv_info = info; + static char hostname[STRING_LENGTH], sbuf[STRING_LENGTH]; + const char *field_names[] = { "hostname", "mntpt", "devpath", "devname", + "uid", "gid", "mode", hostname, mount_point, + gv_info->devpath, gv_info->devname, 0 }; + short int i; +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_variable()\n"); +#endif + + if (gethostname (hostname, STRING_LENGTH - 1) != 0) +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( DIE, LOG_ERR, "gethostname(): %m\n"); +#else + exit(EXIT_FAILURE); +#endif + /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */ + hostname[STRING_LENGTH - 1] = '\0'; + + /* compare_string_array returns i>=0 */ + i=compare_string_array(field_names, variable); + + if ( i > 6 && (i > 1 && gv_info == NULL)) + return (NULL); + if( i >= 0 || i <= 3) + { +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_variable(): i=%d %s\n",i ,field_names[i+7]); +#endif + return(field_names[i+7]); + } + + if(i == 4 ) + sprintf (sbuf, "%u", gv_info->info->uid); + else if(i == 5) + sprintf (sbuf, "%u", gv_info->info->gid); + else if(i == 6) + sprintf (sbuf, "%o", gv_info->info->mode); +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_variable(): %s\n", sbuf); +#endif + return (sbuf); +} /* End Function get_variable */ + +static void service(struct stat statbuf, char *path) +{ + struct devfsd_notify_struct info; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "service()\n"); +#endif + + memset (&info, 0, sizeof info); + info.type = DEVFSD_NOTIFY_REGISTERED; + info.mode = statbuf.st_mode; + info.major = major (statbuf.st_rdev); + info.minor = minor (statbuf.st_rdev); + info.uid = statbuf.st_uid; + info.gid = statbuf.st_gid; + snprintf (info.devname, sizeof (info.devname), "%s", path + strlen (mount_point) + 1); + info.namelen = strlen (info.devname); + service_name (&info); + if ( S_ISDIR (statbuf.st_mode) ) + dir_operation(SERVICE,path,0,NULL); +} + +static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask) +/* [SUMMARY] Scan a directory tree and generate register events on leaf nodes. + To choose which function to perform + The directory pointer. This is closed upon completion. + The name of the directory. + string length parameter. + [RETURNS] Nothing. +*/ +{ + struct stat statbuf; + DIR *dp; + struct dirent *de; + char path[STRING_LENGTH]; + + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "dir_operation()\n"); +#endif + + if((dp = opendir( dir_name))==NULL) + { +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_ERR, "opendir(): %s: %m\n", dir_name); +#endif + return; + } + + while ( (de = readdir (dp) ) != NULL ) + { + + if(de->d_name && *de->d_name == '.' && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))) + continue; + snprintf (path, sizeof (path), "%s/%s", dir_name, de->d_name); +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_ERR, "dir_operation(): %s\n", path); +#endif + + if (lstat (path, &statbuf) != 0) + { +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_ERR, "%s: %m\n", path); +#endif + continue; + } + switch(type) + { + case SERVICE: + service(statbuf,path); + break; + case RESTORE: + restore(path, statbuf, var); + break; + case READ_CONFIG: + read_config_file (path, var, event_mask); + break; + } + } + closedir (dp); +} /* End Function do_scan_and_service */ + +static int mksymlink (const char *oldpath, const char *newpath) +/* [SUMMARY] Create a symlink, creating intervening directories as required. + The string contained in the symlink. + The name of the new symlink. + [RETURNS] 0 on success, else -1. +*/ +{ +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "mksymlink()\n", newpath); +#endif + + + if ( !make_dir_tree (newpath) ) + return (-1); + + if (symlink (oldpath, newpath) != 0) + { + if (errno != EEXIST) + { +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_ERR, "mksymlink(): %s to %s: %m\n", oldpath, newpath); +#endif + return (-1); + } + } + return (0); +} /* End Function mksymlink */ + + +static int make_dir_tree (const char *path) +/* [SUMMARY] Creating intervening directories for a path as required. + The full pathname (including the leaf node). + [RETURNS] TRUE on success, else FALSE. +*/ +{ +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "make_dir_tree()\n"); +#endif + if (bb_make_directory( dirname((char *)path), -1, FILEUTILS_RECUR )==-1) + { +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_ERR, "make_dir_tree(): %s: %m\n", path); +#endif + return (FALSE); + } + return(TRUE); +} /* End Function make_dir_tree */ + +static int expand_expression(char *output, unsigned int outsize, + const char *input, + const char *(*get_variable_func)(const char *variable, void *info), + void *info, + const char *devname, + const regmatch_t *ex, unsigned int numexp) +/* [SUMMARY] Expand environment variables and regular subexpressions in string. + The output expanded expression is written here. + The size of the output buffer. + The input expression. This may equal <>. + A function which will be used to get variable values. If + this returns NULL, the environment is searched instead. If this is NULL, + only the environment is searched. + An arbitrary pointer passed to <>. + Device name; specifically, this is the string that contains all + of the regular subexpressions. + Array of start / end offsets into info->devname for each subexpression + Number of regular subexpressions found in <>. + [RETURNS] TRUE on success, else FALSE. +*/ +{ + char temp[STRING_LENGTH]; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "expand_expression()\n"); +#endif + + if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) ) + return (FALSE); + expand_regexp (output, outsize, temp, devname, ex, numexp); + return (TRUE); +} /* End Function expand_expression */ + +static void expand_regexp (char *output, size_t outsize, const char *input, + const char *devname, + const regmatch_t *ex, unsigned int numex ) +/* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9. + The output expanded expression is written here. + The size of the output buffer. + The input expression. This may NOT equal <>, because + supporting that would require yet another string-copy. However, it's not + hard to write a simple wrapper function to add this functionality for those + few cases that need it. + Device name; specifically, this is the string that contains all + of the regular subexpressions. + An array of start and end offsets into <>, one for each + subexpression + Number of subexpressions in the offset-array <>. + [RETURNS] Nothing. +*/ +{ + const char last_exp = '0' - 1 + numex; + int c = -1; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "expand_regexp()\n"); +#endif + + /* Guarantee NULL termination by writing an explicit '\0' character into + the very last byte */ + if (outsize) + output[--outsize] = '\0'; + /* Copy the input string into the output buffer, replacing '\\' with '\' + and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x + codes are deleted */ + while ( (c != '\0') && (outsize != 0) ) + { + c = *input; + ++input; + if (c == '\\') + { + c = *input; + ++input; + if (c != '\\') + { + if ((c >= '0') && (c <= last_exp)) + { + const regmatch_t *subexp = ex + (c - '0'); + unsigned int sublen = subexp->rm_eo - subexp->rm_so; + + /* Range checking */ + if (sublen > outsize) + sublen = outsize; + strncpy (output, devname + subexp->rm_so, sublen); + output += sublen; + outsize -= sublen; + } + continue; + } + } + *output = c; + ++output; + --outsize; + } /* while */ +} /* End Function expand_regexp */ + + +/* from compat_name.c */ + +struct translate_struct +{ + char *match; /* The string to match to (up to length) */ + char *format; /* Format of output, "%s" takes data past match string, + NULL is effectively "%s" (just more efficient) */ +}; + +static struct translate_struct translate_table[] = +{ + {"sound/", NULL}, + {"printers/", "lp%s"}, + {"v4l/", NULL}, + {"parports/", "parport%s"}, + {"fb/", "fb%s"}, + {"netlink/", NULL}, + {"loop/", "loop%s"}, + {"floppy/", "fd%s"}, + {"rd/", "ram%s"}, + {"md/", "md%s"}, /* Meta-devices */ + {"vc/", "tty%s"}, + {"misc/", NULL}, + {"isdn/", NULL}, + {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/ + {"i2c/", "i2c-%s"}, + {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */ + {"tts/E", "ttyE%s"}, /* Stallion serial driver */ + {"cua/E", "cue%s"}, /* Stallion serial driver callout */ + {"tts/R", "ttyR%s"}, /* Rocketport serial driver */ + {"cua/R", "cur%s"}, /* Rocketport serial driver callout */ + {"ip2/", "ip2%s"}, /* Computone serial driver control */ + {"tts/F", "ttyF%s"}, /* Computone serial driver */ + {"cua/F", "cuf%s"}, /* Computone serial driver callout */ + {"tts/C", "ttyC%s"}, /* Cyclades serial driver */ + {"cua/C", "cub%s"}, /* Cyclades serial driver callout */ + {"tts/", "ttyS%s"}, /* Generic serial: must be after others */ + {"cua/", "cua%s"}, /* Generic serial: must be after others */ + {"input/js", "js%s"}, /* Joystick driver */ + {NULL, NULL} +}; + +const char *get_old_name (const char *devname, unsigned int namelen, + char *buffer, unsigned int major, unsigned int minor) +/* [SUMMARY] Translate a kernel-supplied name into an old name. + The device name provided by the kernel. + The length of the name. + A buffer that may be used. This should be at least 128 bytes long. + The major number for the device. + The minor number for the device. + [RETURNS] A pointer to the old name if known, else NULL. +*/ +{ + const char *compat_name = NULL; + char *ptr; + struct translate_struct *trans; + unsigned int i; + char mode; + int indexx; + const char *pty1; + const char *pty2; + size_t len; + /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */ + const char *fmt[] = { NULL , + "sg%u", /* scsi/generic */ + NULL, /* scsi/disc */ + "sr%u", /* scsi/cd */ + NULL, /* scsi/part */ + "nst%u%c", /* scsi/mt */ + "hd%c" , /* ide/host/disc */ + "hd%c" , /* ide/host/cd */ + "hd%c%s", /* ide/host/part */ + "%sht%d", /* ide/host/mt */ + "sbpcd%u", /* sbp/ */ + "vcs%s", /* vcc/ */ + "%cty%c%c", /* pty/ */ + NULL }; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_old_name()\n"); +#endif + + for (trans = translate_table; trans->match != NULL; ++trans) + { + len = strlen (trans->match); + + if (strncmp (devname, trans->match, len) == 0) + { + if (trans->format == NULL) + return (devname + len); + sprintf (buffer, trans->format, devname + len); + return (buffer); + } + } + + ptr = (strrchr (devname, '/') + 1); + i = scan_dev_name(devname, namelen, ptr); + + if( i > 0 && i < 13) + compat_name = buffer; + else + return NULL; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_old_name(): scan_dev_name() returned %d\n", i); +#endif + + /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */ + if( i == 1 || i == 3 || i == 10 ) + sprintf (buffer, fmt[i], minor); + + /* 2 ==scsi/disc, 4 == scsi/part */ + if( i == 2 || i == 4) + compat_name = write_old_sd_name (buffer, major, minor,((i == 2)?"":(ptr + 4))); + + /* 5 == scsi/mt */ + if( i == 5) + { + mode = ptr[2]; + if (mode == 'n') + mode = '\0'; + sprintf (buffer, fmt[i], minor & 0x1f, mode); + if (devname[namelen - 1] != 'n') + ++compat_name; + } + /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */ + if( i == 6 || i == 7 || i == 8 ) + sprintf (buffer, fmt[i] , get_old_ide_name (major, minor), ptr + 4); /* last arg should be ignored for i == 6 or i== 7 */ + + /* 9 == ide/host/mt */ + if( i == 9 ) + sprintf (buffer, fmt[i], ptr + 2, minor & 0x7f); + + /* 11 == vcc/ */ + if( i == 11 ) + { + sprintf (buffer, fmt[i], devname + 4); + if (buffer[3] == '0') + buffer[3] = '\0'; + } + /* 12 == pty/ */ + if( i == 12 ) + { + pty1 = "pqrstuvwxyzabcde"; + pty2 = "0123456789abcdef"; + indexx = atoi (devname + 5); + sprintf (buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]); + } +#ifdef CONFIG_DEBUG + if(compat_name!=NULL) + msg_logger( NO_DIE, LOG_INFO, "get_old_name(): compat_name %s\n", compat_name); +#endif + return (compat_name); +} /* End Function get_old_name */ + +static char get_old_ide_name (unsigned int major, unsigned int minor) +/* [SUMMARY] Get the old IDE name for a device. + The major number for the device. + The minor number for the device. + [RETURNS] The drive letter. +*/ +{ + char letter='y'; /* 121 */ + char c='a'; /* 97 */ + int i=IDE0_MAJOR; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_old_ide_name()\n"); +#endif + + /* I hope it works like the previous code as it saves a few bytes. Tito ;P */ + do { + if( i==IDE0_MAJOR || i==IDE1_MAJOR || i==IDE2_MAJOR || + i==IDE3_MAJOR || i==IDE4_MAJOR || i==IDE5_MAJOR || + i==IDE6_MAJOR || i==IDE7_MAJOR || i==IDE8_MAJOR || + i==IDE9_MAJOR ) + { + if(i==major) + { + letter=c; + break; + } + c+=2; + } + i++; + } while(i<=IDE9_MAJOR); + + if (minor > 63) + ++letter; + return (letter); +} /* End Function get_old_ide_name */ + +static char *write_old_sd_name (char *buffer, + unsigned int major, unsigned int minor, + char *part) +/* [SUMMARY] Write the old SCSI disc name to a buffer. + The buffer to write to. + The major number for the device. + The minor number for the device. + The partition string. Must be "" for a whole-disc entry. + [RETURNS] A pointer to the buffer on success, else NULL. +*/ +{ + unsigned int disc_index; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "write_old_sd_name()\n"); +#endif + + if (major == 8) + { + sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part); + return (buffer); + } + if ( (major > 64) && (major < 72) ) + { + disc_index = ( (major - 64) << 4 ) + (minor >> 4); + if (disc_index < 26) + sprintf (buffer, "sd%c%s", 'a' + disc_index, part); + else + sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part); + return (buffer); + } + return (NULL); +} /* End Function write_old_sd_name */ + + +/* expression.c */ + +/*EXPERIMENTAL_FUNCTION*/ + +int st_expr_expand (char *output, unsigned int length, const char *input, + const char *(*get_variable_func) (const char *variable, + void *info), + void *info) +/* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules. + The output expanded expression is written here. + The size of the output buffer. + The input expression. This may equal <>. + A function which will be used to get variable values. If + this returns NULL, the environment is searched instead. If this is NULL, + only the environment is searched. + An arbitrary pointer passed to <>. + [RETURNS] TRUE on success, else FALSE. +*/ +{ + char ch; + unsigned int len; + unsigned int out_pos = 0; + const char *env; + const char *ptr; + struct passwd *pwent; + char buffer[BUFFER_SIZE], tmp[STRING_LENGTH]; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "st_expr_expand()\n"); +#endif + + if (length > BUFFER_SIZE) + length = BUFFER_SIZE; + for (; TRUE; ++input) + { + switch (ch = *input) + { + case '$': + /* Variable expansion */ + input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info); + if (input == NULL) + return (FALSE); + break; + case '~': + /* Home directory expansion */ + ch = input[1]; + if ( isspace (ch) || (ch == '/') || (ch == '\0') ) + { + /* User's own home directory: leave separator for next time */ + if ( ( env = getenv ("HOME") ) == NULL ) + { +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, "HOME"); +#endif + return (FALSE); + } + len = strlen (env); + if (len + out_pos >= length) + goto st_expr_expand_out; + memcpy (buffer + out_pos, env, len + 1); + out_pos += len; + continue; + } + /* Someone else's home directory */ + for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr) + /* VOID */ ; + len = ptr - input; + if (len >= sizeof tmp) + goto st_expr_expand_out; + safe_memcpy (tmp, input, len); + input = ptr - 1; + if ( ( pwent = getpwnam (tmp) ) == NULL ) + { +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_INFO, "no pwent for: %s\n", tmp); +#endif + return (FALSE); + } + len = strlen (pwent->pw_dir); + if (len + out_pos >= length) + goto st_expr_expand_out; + memcpy (buffer + out_pos, pwent->pw_dir, len + 1); + out_pos += len; + break; + case '\0': + /* Falltrough */ + default: + if (out_pos >= length) + goto st_expr_expand_out; + buffer[out_pos++] = ch; + if (ch == '\0') + { + memcpy (output, buffer, out_pos); + return (TRUE); + } + break; + /* esac */ + } + } + return (FALSE); +st_expr_expand_out: +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer); +#endif + return (FALSE); +} /* End Function st_expr_expand */ + + +/* Private functions follow */ + +static const char *expand_variable (char *buffer, unsigned int length, + unsigned int *out_pos, const char *input, + const char *(*func) (const char *variable, + void *info), + void *info) +/* [SUMMARY] Expand a variable. + The buffer to write to. + The length of the output buffer. + The current output position. This is updated. + A pointer to the input character pointer. + A function which will be used to get variable values. If this + returns NULL, the environment is searched instead. If this is NULL, only + the environment is searched. + An arbitrary pointer passed to <>. + Diagnostic messages are written here. + [RETURNS] A pointer to the end of this subexpression on success, else NULL. +*/ +{ + char ch; + int len; + unsigned int open_braces; + const char *env, *ptr; + char tmp[STRING_LENGTH]; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "expand_variable()\n"); +#endif + + ch = input[0]; + if (ch == '$') + { + /* Special case for "$$": PID */ + sprintf ( tmp, "%d", (int) getpid () ); + len = strlen (tmp); + if (len + *out_pos >= length) + goto expand_variable_out; + + memcpy (buffer + *out_pos, tmp, len + 1); + out_pos += len; + return (input); + } + /* Ordinary variable expansion, possibly in braces */ + if (ch != '{') + { + /* Simple variable expansion */ + for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr) + /* VOID */ ; + len = ptr - input; + if (len >= sizeof tmp) + goto expand_variable_out; + + safe_memcpy (tmp, input, len); + input = ptr - 1; + if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL ) + { +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, tmp); +#endif + return (NULL); + } + len = strlen (env); + if (len + *out_pos >= length) + goto expand_variable_out; + + memcpy (buffer + *out_pos, env, len + 1); + *out_pos += len; + return (input); + } + /* Variable in braces: check for ':' tricks */ + ch = *++input; + for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr) + /* VOID */; + if (ch == '}') + { + /* Must be simple variable expansion with "${var}" */ + len = ptr - input; + if (len >= sizeof tmp) + goto expand_variable_out; + + safe_memcpy (tmp, input, len); + ptr = expand_variable (buffer, length, out_pos, tmp, func, info ); + if (ptr == NULL) + return (NULL); + return (input + len); + } + if (ch != ':' || ptr[1] != '-' ) + { +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_INFO,"illegal char in var name\n"); +#endif + return (NULL); + } + /* It's that handy "${var:-word}" expression. Check if var is defined */ + len = ptr - input; + if (len >= sizeof tmp) + goto expand_variable_out; + + safe_memcpy (tmp, input, len); + /* Move input pointer to ':' */ + input = ptr; + /* First skip to closing brace, taking note of nested expressions */ + ptr += 2; + ch = ptr[0]; + for (open_braces = 1; open_braces > 0; ch = *++ptr) + { + switch (ch) + { + case '{': + ++open_braces; + break; + case '}': + --open_braces; + break; + case '\0': +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_INFO,"\"}\" not found in: %s\n", input); +#endif + return (NULL); + default: + break; + } + } + --ptr; + /* At this point ptr should point to closing brace of "${var:-word}" */ + if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL ) + { + /* Found environment variable, so skip the input to the closing brace + and return the variable */ + input = ptr; + len = strlen (env); + if (len + *out_pos >= length) + goto expand_variable_out; + + memcpy (buffer + *out_pos, env, len + 1); + *out_pos += len; + return (input); + } + /* Environment variable was not found, so process word. Advance input + pointer to start of word in "${var:-word}" */ + input += 2; + len = ptr - input; + if (len >= sizeof tmp) + goto expand_variable_out; + + safe_memcpy (tmp, input, len); + input = ptr; + if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) ) + return (NULL); + len = strlen (tmp); + if (len + *out_pos >= length) + goto expand_variable_out; + + memcpy (buffer + *out_pos, tmp, len + 1); + *out_pos += len; + return (input); +expand_variable_out: +#ifdef CONFIG_DEVFSD_VERBOSE + msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer); +#endif + return (NULL); +} /* End Function expand_variable */ + + +static const char *get_variable_v2 (const char *variable, + const char *(*func) (const char *variable, void *info), + void *info) +/* [SUMMARY] Get a variable from the environment or . + The variable name. + A function which will be used to get the variable. If this returns + NULL, the environment is searched instead. If this is NULL, only the + environment is searched. + [RETURNS] The value of the variable on success, else NULL. +*/ +{ + const char *value; + +#ifdef CONFIG_DEBUG + msg_logger( NO_DIE, LOG_INFO, "get_variable_v2()\n"); +#endif + + if (func != NULL) + { + value = (*func) (variable, info); + if (value != NULL) + return (value); + } + return getenv (variable); +} /* End Function get_variable */ + +/* END OF CODE */ diff --git a/busybox/miscutils/hdparm.c b/busybox/miscutils/hdparm.c new file mode 100644 index 000000000..0d2c328f0 --- /dev/null +++ b/busybox/miscutils/hdparm.c @@ -0,0 +1,2872 @@ +/* vi: set sw=4 ts=4: */ +/* + * hdparm implementation for busybox + * + * + * Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it> + * + * Hacked by Tito for size optimization. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + * + * + * This program is based on the source code of hdparm: see below... + * hdparm.c - Command line interface to get/set hard disk parameters + * - by Mark Lord (C) 1994-2002 -- freely distributable + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include +#include +#include +#include + + +#if (__BYTE_ORDER == __BIG_ENDIAN) && !defined(__USE_XOPEN) +#define __USE_XOPEN +#endif + +/* device types */ +/* ------------ */ +#define NO_DEV 0xffff +#define ATA_DEV 0x0000 +#define ATAPI_DEV 0x0001 + +/* word definitions */ +/* ---------------- */ +#define GEN_CONFIG 0 /* general configuration */ +#define LCYLS 1 /* number of logical cylinders */ +#define CONFIG 2 /* specific configuration */ +#define LHEADS 3 /* number of logical heads */ +#define TRACK_BYTES 4 /* number of bytes/track (ATA-1) */ +#define SECT_BYTES 5 /* number of bytes/sector (ATA-1) */ +#define LSECTS 6 /* number of logical sectors/track */ +#define START_SERIAL 10 /* ASCII serial number */ +#define LENGTH_SERIAL 10 /* 10 words (20 bytes or characters) */ +#define BUF_TYPE 20 /* buffer type (ATA-1) */ +#define BUFFER__SIZE 21 /* buffer size (ATA-1) */ +#define RW_LONG 22 /* extra bytes in R/W LONG cmd ( < ATA-4)*/ +#define START_FW_REV 23 /* ASCII firmware revision */ +#define LENGTH_FW_REV 4 /* 4 words (8 bytes or characters) */ +#define START_MODEL 27 /* ASCII model number */ +#define LENGTH_MODEL 20 /* 20 words (40 bytes or characters) */ +#define SECTOR_XFER_MAX 47 /* r/w multiple: max sectors xfered */ +#define DWORD_IO 48 /* can do double-word IO (ATA-1 only) */ +#define CAPAB_0 49 /* capabilities */ +#define CAPAB_1 50 +#define PIO_MODE 51 /* max PIO mode supported (obsolete)*/ +#define DMA_MODE 52 /* max Singleword DMA mode supported (obs)*/ +#define WHATS_VALID 53 /* what fields are valid */ +#define LCYLS_CUR 54 /* current logical cylinders */ +#define LHEADS_CUR 55 /* current logical heads */ +#define LSECTS_CUR 56 /* current logical sectors/track */ +#define CAPACITY_LSB 57 /* current capacity in sectors */ +#define CAPACITY_MSB 58 +#define SECTOR_XFER_CUR 59 /* r/w multiple: current sectors xfered */ +#define LBA_SECTS_LSB 60 /* LBA: total number of user */ +#define LBA_SECTS_MSB 61 /* addressable sectors */ +#define SINGLE_DMA 62 /* singleword DMA modes */ +#define MULTI_DMA 63 /* multiword DMA modes */ +#define ADV_PIO_MODES 64 /* advanced PIO modes supported */ + /* multiword DMA xfer cycle time: */ +#define DMA_TIME_MIN 65 /* - minimum */ +#define DMA_TIME_NORM 66 /* - manufacturer's recommended */ + /* minimum PIO xfer cycle time: */ +#define PIO_NO_FLOW 67 /* - without flow control */ +#define PIO_FLOW 68 /* - with IORDY flow control */ +#define PKT_REL 71 /* typical #ns from PKT cmd to bus rel */ +#define SVC_NBSY 72 /* typical #ns from SERVICE cmd to !BSY */ +#define CDR_MAJOR 73 /* CD ROM: major version number */ +#define CDR_MINOR 74 /* CD ROM: minor version number */ +#define QUEUE_DEPTH 75 /* queue depth */ +#define MAJOR 80 /* major version number */ +#define MINOR 81 /* minor version number */ +#define CMDS_SUPP_0 82 /* command/feature set(s) supported */ +#define CMDS_SUPP_1 83 +#define CMDS_SUPP_2 84 +#define CMDS_EN_0 85 /* command/feature set(s) enabled */ +#define CMDS_EN_1 86 +#define CMDS_EN_2 87 +#define ULTRA_DMA 88 /* ultra DMA modes */ + /* time to complete security erase */ +#define ERASE_TIME 89 /* - ordinary */ +#define ENH_ERASE_TIME 90 /* - enhanced */ +#define ADV_PWR 91 /* current advanced power management level + in low byte, 0x40 in high byte. */ +#define PSWD_CODE 92 /* master password revision code */ +#define HWRST_RSLT 93 /* hardware reset result */ +#define ACOUSTIC 94 /* acoustic mgmt values ( >= ATA-6) */ +#define LBA_LSB 100 /* LBA: maximum. Currently only 48 */ +#define LBA_MID 101 /* bits are used, but addr 103 */ +#define LBA_48_MSB 102 /* has been reserved for LBA in */ +#define LBA_64_MSB 103 /* the future. */ +#define RM_STAT 127 /* removable media status notification feature set support */ +#define SECU_STATUS 128 /* security status */ +#define CFA_PWR_MODE 160 /* CFA power mode 1 */ +#define START_MEDIA 176 /* media serial number */ +#define LENGTH_MEDIA 20 /* 20 words (40 bytes or characters)*/ +#define START_MANUF 196 /* media manufacturer I.D. */ +#define LENGTH_MANUF 10 /* 10 words (20 bytes or characters) */ +#define INTEGRITY 255 /* integrity word */ + +/* bit definitions within the words */ +/* -------------------------------- */ + +/* many words are considered valid if bit 15 is 0 and bit 14 is 1 */ +#define VALID 0xc000 +#define VALID_VAL 0x4000 +/* many words are considered invalid if they are either all-0 or all-1 */ +#define NOVAL_0 0x0000 +#define NOVAL_1 0xffff + +/* word 0: gen_config */ +#define NOT_ATA 0x8000 +#define NOT_ATAPI 0x4000 /* (check only if bit 15 == 1) */ +#define MEDIA_REMOVABLE 0x0080 +#define DRIVE_NOT_REMOVABLE 0x0040 /* bit obsoleted in ATA 6 */ +#define INCOMPLETE 0x0004 +#define CFA_SUPPORT_VAL 0x848a /* 848a=CFA feature set support */ +#define DRQ_RESPONSE_TIME 0x0060 +#define DRQ_3MS_VAL 0x0000 +#define DRQ_INTR_VAL 0x0020 +#define DRQ_50US_VAL 0x0040 +#define PKT_SIZE_SUPPORTED 0x0003 +#define PKT_SIZE_12_VAL 0x0000 +#define PKT_SIZE_16_VAL 0x0001 +#define EQPT_TYPE 0x1f00 +#define SHIFT_EQPT 8 + +#define CDROM 0x0005 + +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static const char *pkt_str[] = { + "Direct-access device", /* word 0, bits 12-8 = 00 */ + "Sequential-access device", /* word 0, bits 12-8 = 01 */ + "Printer", /* word 0, bits 12-8 = 02 */ + "Processor", /* word 0, bits 12-8 = 03 */ + "Write-once device", /* word 0, bits 12-8 = 04 */ + "CD-ROM", /* word 0, bits 12-8 = 05 */ + "Scanner", /* word 0, bits 12-8 = 06 */ + "Optical memory", /* word 0, bits 12-8 = 07 */ + "Medium changer", /* word 0, bits 12-8 = 08 */ + "Communications device", /* word 0, bits 12-8 = 09 */ + "ACS-IT8 device", /* word 0, bits 12-8 = 0a */ + "ACS-IT8 device", /* word 0, bits 12-8 = 0b */ + "Array controller", /* word 0, bits 12-8 = 0c */ + "Enclosure services", /* word 0, bits 12-8 = 0d */ + "Reduced block command device", /* word 0, bits 12-8 = 0e */ + "Optical card reader/writer", /* word 0, bits 12-8 = 0f */ + "", /* word 0, bits 12-8 = 10 */ + "", /* word 0, bits 12-8 = 11 */ + "", /* word 0, bits 12-8 = 12 */ + "", /* word 0, bits 12-8 = 13 */ + "", /* word 0, bits 12-8 = 14 */ + "", /* word 0, bits 12-8 = 15 */ + "", /* word 0, bits 12-8 = 16 */ + "", /* word 0, bits 12-8 = 17 */ + "", /* word 0, bits 12-8 = 18 */ + "", /* word 0, bits 12-8 = 19 */ + "", /* word 0, bits 12-8 = 1a */ + "", /* word 0, bits 12-8 = 1b */ + "", /* word 0, bits 12-8 = 1c */ + "", /* word 0, bits 12-8 = 1d */ + "", /* word 0, bits 12-8 = 1e */ + "Unknown", /* word 0, bits 12-8 = 1f */ +}; +static const char *ata1_cfg_str[] = { /* word 0 in ATA-1 mode */ + "reserved", /* bit 0 */ + "hard sectored", /* bit 1 */ + "soft sectored", /* bit 2 */ + "not MFM encoded ", /* bit 3 */ + "head switch time > 15us", /* bit 4 */ + "spindle motor control option", /* bit 5 */ + "fixed drive", /* bit 6 */ + "removable drive", /* bit 7 */ + "disk xfer rate <= 5Mbs", /* bit 8 */ + "disk xfer rate > 5Mbs, <= 10Mbs", /* bit 9 */ + "disk xfer rate > 5Mbs", /* bit 10 */ + "rotational speed tol.", /* bit 11 */ + "data strobe offset option", /* bit 12 */ + "track offset option", /* bit 13 */ + "format speed tolerance gap reqd", /* bit 14 */ + "ATAPI" /* bit 14 */ +}; +#endif + +/* word 1: number of logical cylinders */ +#define LCYLS_MAX 0x3fff /* maximum allowable value */ + +/* word 2: specific configuration + * (a) require SET FEATURES to spin-up + * (b) require spin-up to fully reply to IDENTIFY DEVICE + */ +#define STBY_NID_VAL 0x37c8 /* (a) and (b) */ +#define STBY_ID_VAL 0x738c /* (a) and not (b) */ +#define PWRD_NID_VAL 0x8c73 /* not (a) and (b) */ +#define PWRD_ID_VAL 0xc837 /* not (a) and not (b) */ + +/* words 47 & 59: sector_xfer_max & sector_xfer_cur */ +#define SECTOR_XFER 0x00ff /* sectors xfered on r/w multiple cmds*/ +#define MULTIPLE_SETTING_VALID 0x0100 /* 1=multiple sector setting is valid */ + +/* word 49: capabilities 0 */ +#define STD_STBY 0x2000 /* 1=standard values supported (ATA); + 0=vendor specific values */ +#define IORDY_SUP 0x0800 /* 1=support; 0=may be supported */ +#define IORDY_OFF 0x0400 /* 1=may be disabled */ +#define LBA_SUP 0x0200 /* 1=Logical Block Address support */ +#define DMA_SUP 0x0100 /* 1=Direct Memory Access support */ +#define DMA_IL_SUP 0x8000 /* 1=interleaved DMA support (ATAPI) */ +#define CMD_Q_SUP 0x4000 /* 1=command queuing support (ATAPI) */ +#define OVLP_SUP 0x2000 /* 1=overlap operation support (ATAPI) */ +#define SWRST_REQ 0x1000 /* 1=ATA SW reset required (ATAPI, obsolete */ + +/* word 50: capabilities 1 */ +#define MIN_STANDBY_TIMER 0x0001 /* 1=device specific standby timer value minimum */ + +/* words 51 & 52: PIO & DMA cycle times */ +#define MODE 0xff00 /* the mode is in the MSBs */ + +/* word 53: whats_valid */ +#define OK_W88 0x0004 /* the ultra_dma info is valid */ +#define OK_W64_70 0x0002 /* see above for word descriptions */ +#define OK_W54_58 0x0001 /* current cyl, head, sector, cap. info valid */ + +/*word 63,88: dma_mode, ultra_dma_mode*/ +#define MODE_MAX 7 /* bit definitions force udma <=7 (when + * udma >=8 comes out it'll have to be + * defined in a new dma_mode word!) */ + +/* word 64: PIO transfer modes */ +#define PIO_SUP 0x00ff /* only bits 0 & 1 are used so far, */ +#define PIO_MODE_MAX 8 /* but all 8 bits are defined */ + +/* word 75: queue_depth */ +#define DEPTH_BITS 0x001f /* bits used for queue depth */ + +/* words 80-81: version numbers */ +/* NOVAL_0 or NOVAL_1 means device does not report version */ + +/* word 81: minor version number */ +#define MINOR_MAX 0x1C +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static const char *minor_str[] = { /* word 81 value: */ + "device does not report version", /* 0x0000 */ + "ATA-1 X3T9.2 781D prior to revision 4", /* 0x0001 */ + "ATA-1 published, ANSI X3.221-1994", /* 0x0002 */ + "ATA-1 X3T9.2 781D revision 4", /* 0x0003 */ + "ATA-2 published, ANSI X3.279-1996", /* 0x0004 */ + "ATA-2 X3T10 948D prior to revision 2k", /* 0x0005 */ + "ATA-3 X3T10 2008D revision 1", /* 0x0006 */ + "ATA-2 X3T10 948D revision 2k", /* 0x0007 */ + "ATA-3 X3T10 2008D revision 0", /* 0x0008 */ + "ATA-2 X3T10 948D revision 3", /* 0x0009 */ + "ATA-3 published, ANSI X3.298-199x", /* 0x000a */ + "ATA-3 X3T10 2008D revision 6", /* 0x000b */ + "ATA-3 X3T13 2008D revision 7 and 7a", /* 0x000c */ + "ATA/ATAPI-4 X3T13 1153D revision 6", /* 0x000d */ + "ATA/ATAPI-4 T13 1153D revision 13", /* 0x000e */ + "ATA/ATAPI-4 X3T13 1153D revision 7", /* 0x000f */ + "ATA/ATAPI-4 T13 1153D revision 18", /* 0x0010 */ + "ATA/ATAPI-4 T13 1153D revision 15", /* 0x0011 */ + "ATA/ATAPI-4 published, ANSI NCITS 317-1998", /* 0x0012 */ + "ATA/ATAPI-5 T13 1321D revision 3", + "ATA/ATAPI-4 T13 1153D revision 14", /* 0x0014 */ + "ATA/ATAPI-5 T13 1321D revision 1", /* 0x0015 */ + "ATA/ATAPI-5 published, ANSI NCITS 340-2000", /* 0x0016 */ + "ATA/ATAPI-4 T13 1153D revision 17", /* 0x0017 */ + "ATA/ATAPI-6 T13 1410D revision 0", /* 0x0018 */ + "ATA/ATAPI-6 T13 1410D revision 3a", /* 0x0019 */ + "Reserved", /* 0x001a */ + "ATA/ATAPI-6 T13 1410D revision 2", /* 0x001b */ + "ATA/ATAPI-6 T13 1410D revision 1", /* 0x001c */ + "reserved" /* 0x001d */ + "reserved" /* 0x001e */ + "reserved" /* 0x001f-0xfffe*/ +}; +#endif +static const char actual_ver[] = { + /* word 81 value: */ + 0, /* 0x0000 WARNING: */ + 1, /* 0x0001 WARNING: */ + 1, /* 0x0002 WARNING: */ + 1, /* 0x0003 WARNING: */ + 2, /* 0x0004 WARNING: This array */ + 2, /* 0x0005 WARNING: corresponds */ + 3, /* 0x0006 WARNING: *exactly* */ + 2, /* 0x0007 WARNING: to the ATA/ */ + 3, /* 0x0008 WARNING: ATAPI version */ + 2, /* 0x0009 WARNING: listed in */ + 3, /* 0x000a WARNING: the */ + 3, /* 0x000b WARNING: minor_str */ + 3, /* 0x000c WARNING: array */ + 4, /* 0x000d WARNING: above. */ + 4, /* 0x000e WARNING: */ + 4, /* 0x000f WARNING: if you change */ + 4, /* 0x0010 WARNING: that one, */ + 4, /* 0x0011 WARNING: change this one */ + 4, /* 0x0012 WARNING: too!!! */ + 5, /* 0x0013 WARNING: */ + 4, /* 0x0014 WARNING: */ + 5, /* 0x0015 WARNING: */ + 5, /* 0x0016 WARNING: */ + 4, /* 0x0017 WARNING: */ + 6, /* 0x0018 WARNING: */ + 6, /* 0x0019 WARNING: */ + 0, /* 0x001a WARNING: */ + 6, /* 0x001b WARNING: */ + 6, /* 0x001c WARNING: */ + 0 /* 0x001d-0xfffe */ +}; + +/* words 82-84: cmds/feats supported */ +#define CMDS_W82 0x77ff /* word 82: defined command locations*/ +#define CMDS_W83 0x3fff /* word 83: defined command locations*/ +#define CMDS_W84 0x002f /* word 83: defined command locations*/ +#define SUPPORT_48_BIT 0x0400 +#define NUM_CMD_FEAT_STR 48 + +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static const char *cmd_feat_str[] = { + "", /* word 82 bit 15: obsolete */ + "NOP cmd", /* word 82 bit 14 */ + "READ BUFFER cmd", /* word 82 bit 13 */ + "WRITE BUFFER cmd", /* word 82 bit 12 */ + "", /* word 82 bit 11: obsolete */ + "Host Protected Area feature set", /* word 82 bit 10 */ + "DEVICE RESET cmd", /* word 82 bit 9 */ + "SERVICE interrupt", /* word 82 bit 8 */ + "Release interrupt", /* word 82 bit 7 */ + "Look-ahead", /* word 82 bit 6 */ + "Write cache", /* word 82 bit 5 */ + "PACKET command feature set", /* word 82 bit 4 */ + "Power Management feature set", /* word 82 bit 3 */ + "Removable Media feature set", /* word 82 bit 2 */ + "Security Mode feature set", /* word 82 bit 1 */ + "SMART feature set", /* word 82 bit 0 */ + /* --------------*/ + "", /* word 83 bit 15: !valid bit */ + "", /* word 83 bit 14: valid bit */ + "FLUSH CACHE EXT command", /* word 83 bit 13 */ + "Mandatory FLUSH CACHE command ", /* word 83 bit 12 */ + "Device Configuration Overlay feature set ", + "48-bit Address feature set ", /* word 83 bit 10 */ + "", + "SET MAX security extension", /* word 83 bit 8 */ + "Address Offset Reserved Area Boot", /* word 83 bit 7 */ + "SET FEATURES subcommand required to spinup after power up", + "Power-Up In Standby feature set", /* word 83 bit 5 */ + "Removable Media Status Notification feature set", + "Adv. Power Management feature set",/* word 83 bit 3 */ + "CFA feature set", /* word 83 bit 2 */ + "READ/WRITE DMA QUEUED", /* word 83 bit 1 */ + "DOWNLOAD MICROCODE cmd", /* word 83 bit 0 */ + /* --------------*/ + "", /* word 84 bit 15: !valid bit */ + "", /* word 84 bit 14: valid bit */ + "", /* word 84 bit 13: reserved */ + "", /* word 84 bit 12: reserved */ + "", /* word 84 bit 11: reserved */ + "", /* word 84 bit 10: reserved */ + "", /* word 84 bit 9: reserved */ + "", /* word 84 bit 8: reserved */ + "", /* word 84 bit 7: reserved */ + "", /* word 84 bit 6: reserved */ + "General Purpose Logging feature set", /* word 84 bit 5 */ + "", /* word 84 bit 4: reserved */ + "Media Card Pass Through Command feature set ", + "Media serial number ", /* word 84 bit 2 */ + "SMART self-test ", /* word 84 bit 1 */ + "SMART error logging " /* word 84 bit 0 */ +}; +#endif + + +/* words 85-87: cmds/feats enabled */ +/* use cmd_feat_str[] to display what commands and features have + * been enabled with words 85-87 + */ + +/* words 89, 90, SECU ERASE TIME */ +#define ERASE_BITS 0x00ff + +/* word 92: master password revision */ +/* NOVAL_0 or NOVAL_1 means no support for master password revision */ + +/* word 93: hw reset result */ +#define CBLID 0x2000 /* CBLID status */ +#define RST0 0x0001 /* 1=reset to device #0 */ +#define DEV_DET 0x0006 /* how device num determined */ +#define JUMPER_VAL 0x0002 /* device num determined by jumper */ +#define CSEL_VAL 0x0004 /* device num determined by CSEL_VAL */ + +/* word 127: removable media status notification feature set support */ +#define RM_STAT_BITS 0x0003 +#define RM_STAT_SUP 0x0001 + +/* word 128: security */ +#define SECU_ENABLED 0x0002 +#define SECU_LEVEL 0x0010 +#define NUM_SECU_STR 6 +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static const char *secu_str[] = { + "supported", /* word 128, bit 0 */ + "enabled", /* word 128, bit 1 */ + "locked", /* word 128, bit 2 */ + "frozen", /* word 128, bit 3 */ + "expired: security count", /* word 128, bit 4 */ + "supported: enhanced erase" /* word 128, bit 5 */ +}; +#endif + +/* word 160: CFA power mode */ +#define VALID_W160 0x8000 /* 1=word valid */ +#define PWR_MODE_REQ 0x2000 /* 1=CFA power mode req'd by some cmds*/ +#define PWR_MODE_OFF 0x1000 /* 1=CFA power moded disabled */ +#define MAX_AMPS 0x0fff /* value = max current in ma */ + +/* word 255: integrity */ +#define SIG 0x00ff /* signature location */ +#define SIG_VAL 0x00A5 /* signature value */ + +#define VERSION "v5.4" + +#define TIMING_MB 64 +#define TIMING_BUF_MB 1 +#define TIMING_BUF_BYTES (TIMING_BUF_MB * 1024 * 1024) +#define TIMING_BUF_COUNT (timing_MB / TIMING_BUF_MB) +#define BUFCACHE_FACTOR 2 + +#undef DO_FLUSHCACHE /* under construction: force cache flush on -W0 */ + +/* Busybox messages and functions */ + +const char * const bb_msg_shared_mem ="could not %s sharedmem buf"; +const char * const bb_msg_op_not_supp =" operation not supported on %s disks"; + +static void bb_ioctl(int fd, int request, void *argp, const char *string) +{ + if (ioctl (fd, request, argp) != 0) + bb_perror_msg(" %s", string); +} + +static void if_printf(unsigned long i, char *fmt, ... ) +{ + va_list ap; + va_start(ap, fmt); + if (i) + vprintf(fmt, ap); + va_end(ap); +} + +static void on_off (unsigned int value); + +static void if_printf_on_off(unsigned long get_arg,const char *fmt, unsigned long arg) +{ + if (get_arg) + { + printf(fmt, arg); + on_off(arg); + } +} + +static void bb_ioctl_on_off(int fd, int request, void *argp, const char *string, + const char * fmt) +{ + if (ioctl (fd, request, &argp) != 0) + bb_perror_msg(" %s", string); + else + { + printf(fmt, (unsigned long) argp); + on_off((unsigned long) argp); + } +} + +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static void if_else_printf(unsigned long i, char *fmt1, char *fmt2, ... ) +{ + va_list ap; + va_start(ap, fmt2); + if (i) + vprintf(fmt1, ap); + else + vprintf(fmt2, ap); + va_end(ap); +} + +static void print_ascii(uint16_t *p, uint8_t length); + +static void xprint_ascii(uint16_t *val ,int i, char * string, int n) +{ + if(val[i]) + { + printf("\t%-20s",string); + print_ascii(&val[i], n); + } +} + +static void if_strcat(unsigned long test, char *modes, char *string) +{ + if (test) + strcat(modes,string); +} +#endif + +static void sync_and_sleep(int i) +{ + sync(); + sleep(i); +} + +static uint16_t check_if_min_and_set_val(uint16_t a, uint16_t b) +{ + if( a < b) + a = b; + return a; +} + +static uint16_t check_if_maj_and_set_val(uint16_t a, uint16_t b) +{ + if( a > b) + a = b; + return a; +} + +static unsigned long int set_flag(char *p, char max) +{ + if (*p >= '0' && *p <= max ) + return 1; + return 0; +} + +/* end of busybox specific stuff */ + +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static uint8_t mode_loop(uint16_t mode_sup, uint16_t mode_sel, int cc, uint8_t *have_mode) +{ + uint16_t ii; + uint8_t err_dma = 0; + + for(ii = 0; ii <= MODE_MAX; ii++) + { + if(mode_sel & 0x0001) + { + printf("*%cdma%u ",cc,ii); + if(*have_mode) + err_dma = 1; + *have_mode = 1; + } + else if(mode_sup & 0x0001) + printf("%cdma%u ",cc,ii); + + mode_sup >>=1; + mode_sel >>=1; + } + return err_dma; +} + +static void print_ascii(uint16_t *p, uint8_t length) { + uint8_t ii; + char cl; + + /* find first non-space & print it */ + for(ii = 0; ii< length; ii++) + { + if(((char) 0x00ff&((*p)>>8)) != ' ') + break; + if((cl = (char) 0x00ff&(*p)) != ' ') + { + if_printf((cl != '\0'),"%c",cl); + p++; + ii++; + break; + } + p++; + } + /* print the rest */ + for(; ii< length; ii++) + { + if(!(*p)) + break; /* some older devices have NULLs */ + printf("%c%c",(char)0x00ff&((*p)>>8),(char)(*p)&0x00ff); + p++; + } + printf("\n"); +} + +/* identify() is the only extern function used across two source files. The + others, though, were declared in hdparm.c with global scope; since other + functions in that file have static (file) scope, I assume the difference is + intentional. */ +static void identify (uint16_t *id_supplied, const char *devname) +{ + + char *id_file = NULL; + FILE *fl; + uint16_t val[256], ii, jj, kk; + uint16_t like_std = 1, std = 0, min_std = 0xffff; + uint16_t dev = NO_DEV, eqpt = NO_DEV; + uint8_t have_mode = 0, err_dma = 0; + uint8_t chksum = 0; + uint32_t ll, mm, nn, oo; + __u64 bbbig; /* (:) */ + + if (id_supplied) + { +#if __BYTE_ORDER == __BIG_ENDIAN + swab(id_supplied, val, sizeof(val)); +#else + memcpy(val, id_supplied, sizeof(val)); +#endif + } + else + { + id_file = xcalloc(1, strlen(devname)+22); + sprintf(id_file, "/proc/ide/%s/identify", devname); + /* open the file, read in all the info and close it */ + if (id_file == NULL) + fl = stdin; + else + fl = bb_xfopen(id_file, "r"); + + /* calculate checksum over all bytes */ + for(ii = GEN_CONFIG; ii<=INTEGRITY; ii++) + { + unsigned int scratch; + if(1 != fscanf(fl,"%04x",&scratch)) + break; + val[ii] = (uint16_t)scratch; + chksum += val[ii] + (val[ii] >> 8); + } + fclose(fl); + /*bb_fclose_nonstdin(fl);*/ + if(ii < (INTEGRITY+1)) + bb_error_msg_and_die("Input file wrong format or length"); + } + chksum &= 0xff; + + /* check if we recognise the device type */ + printf("\n"); + if(!(val[GEN_CONFIG] & NOT_ATA)) + { + dev = ATA_DEV; + printf("ATA device, with "); + } + else if(val[GEN_CONFIG]==CFA_SUPPORT_VAL) + { + dev = ATA_DEV; + like_std = 4; + printf("CompactFlash ATA device, with "); + } + else if(!(val[GEN_CONFIG] & NOT_ATAPI)) + { + dev = ATAPI_DEV; + eqpt = (val[GEN_CONFIG] & EQPT_TYPE) >> SHIFT_EQPT; + printf("ATAPI %s, with ", pkt_str[eqpt]); + like_std = 3; + } + else + /*"Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n"*/ + bb_error_msg_and_die("Unknown device type"); + + if_printf(!(val[GEN_CONFIG] & MEDIA_REMOVABLE),"non-"); + printf("removable media\n"); + + /* Info from the specific configuration word says whether or not the + * ID command completed correctly. It is only defined, however in + * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior + * standards. Since the values allowed for this word are extremely + * specific, it should be safe to check it now, even though we don't + * know yet what standard this device is using. + */ + if((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL) || + (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL) ) + { + like_std = 5; + if_printf(((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL)), + "powers-up in standby; SET FEATURES subcmd spins-up.\n"); + if_printf((((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && (val[GEN_CONFIG] & INCOMPLETE)), + "\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n\n"); + } + + /* output the model and serial numbers and the fw revision */ + xprint_ascii(val, START_MODEL, "Model Number:", LENGTH_MODEL); + xprint_ascii(val, START_SERIAL, "Serial Number:", LENGTH_SERIAL); + xprint_ascii(val, START_FW_REV, "Firmware Revision:", LENGTH_FW_REV); + xprint_ascii(val, START_MEDIA, "Media Serial Num:", LENGTH_MEDIA); + xprint_ascii(val, START_MANUF, "Media Manufacturer:", LENGTH_MANUF); + + /* major & minor standards version number (Note: these words were not + * defined until ATA-3 & the CDROM std uses different words.) */ + printf("Standards:"); + if(eqpt != CDROM) + { + if(val[MINOR] && (val[MINOR] <= MINOR_MAX)) + { + like_std=check_if_min_and_set_val(like_std, 3); + std = actual_ver[val[MINOR]]; + if_printf(std,"\n\tUsed: %s ",minor_str[val[MINOR]]); + + } + /* looks like when they up-issue the std, they obsolete one; + * thus, only the newest 4 issues need be supported. (That's + * what "kk" and "min_std" are all about.) */ + if(val[MAJOR] && (val[MAJOR] !=NOVAL_1)) + { + printf("\n\tSupported: "); + jj = val[MAJOR] << 1; + kk = like_std >4 ? like_std-4: 0; + for(ii = 14; (ii >0)&&(ii>kk); ii--) + { + if(jj & 0x8000) + { + printf("%u ", ii); + if(like_std < ii) + { + like_std = ii; + kk = like_std >4 ? like_std-4: 0; + } + min_std=check_if_maj_and_set_val(min_std, ii); + } + jj <<= 1; + } + like_std=check_if_min_and_set_val(like_std, 3); + } + /* Figure out what standard the device is using if it hasn't told + * us. If we know the std, check if the device is using any of + * the words from the next level up. It happens. + */ + like_std=check_if_min_and_set_val(like_std, std); + + if(((std == 5) || (!std && (like_std < 6))) && + ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && + (( val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) || + ((( val[CMDS_SUPP_2] & VALID) == VALID_VAL) && + ( val[CMDS_SUPP_2] & CMDS_W84) ) ) ) + { + like_std = 6; + } + else if(((std == 4) || (!std && (like_std < 5))) && + ((((val[INTEGRITY] & SIG) == SIG_VAL) && !chksum) || + (( val[HWRST_RSLT] & VALID) == VALID_VAL) || + ((( val[CMDS_SUPP_1] & VALID) == VALID_VAL) && + (( val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) ) + { + like_std = 5; + } + else if(((std == 3) || (!std && (like_std < 4))) && + ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && + ((( val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) || + (( val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) || + (( val[CAPAB_1] & VALID) == VALID_VAL) || + (( val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) || + (( val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) ) ) + { + like_std = 4; + } + else if(((std == 2) || (!std && (like_std < 3))) && + ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) ) + { + like_std = 3; + } + else if(((std == 1) || (!std && (like_std < 2))) && + ((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) || + (val[WHATS_VALID] & OK_W64_70)) ) + { + like_std = 2; + } + if(!std) + printf("\n\tLikely used: %u\n",like_std); + else if(like_std > std) + printf("& some of %u\n",like_std); + else + printf("\n"); + } + else + { + /* TBD: do CDROM stuff more thoroughly. For now... */ + kk = 0; + if(val[CDR_MINOR] == 9) + { + kk = 1; + printf("\n\tUsed: ATAPI for CD-ROMs, SFF-8020i, r2.5"); + } + if(val[CDR_MAJOR] && (val[CDR_MAJOR] !=NOVAL_1)) + { + kk = 1; + printf("\n\tSupported: CD-ROM ATAPI"); + jj = val[CDR_MAJOR] >> 1; + for(ii = 1; ii <15; ii++) + { + if_printf((jj & 0x0001),"-%u ", ii); + jj >>= 1; + } + } + if_else_printf((!kk),"\n\tLikely used CD-ROM ATAPI-1\n","\n"); + /* the cdrom stuff is more like ATA-2 than anything else, so: */ + like_std = 2; + } + + if(min_std == 0xffff) + min_std = like_std > 4 ? like_std - 3 : 1; + + printf("Configuration:\n"); + /* more info from the general configuration word */ + if((eqpt != CDROM) && (like_std == 1)) + { + jj = val[GEN_CONFIG] >> 1; + for(ii = 1; ii < 15; ii++) + { + if_printf((jj & 0x0001),"\t%s\n",ata1_cfg_str[ii]); + jj >>=1; + } + } + if(dev == ATAPI_DEV) + { + printf("\tDRQ response: "); /* Data Request (DRQ) */ + switch(val[GEN_CONFIG] & DRQ_RESPONSE_TIME) + { + case DRQ_3MS_VAL : + printf("3ms.\n"); + break; + case DRQ_INTR_VAL : + printf("<=10ms with INTRQ\n"); + break; + case DRQ_50US_VAL : + printf("50us.\n"); + break; + default : + printf("unknown.\n"); + break; + } + printf("\tPacket size: "); + switch(val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) + { + case PKT_SIZE_12_VAL : + printf("12 bytes\n"); + break; + case PKT_SIZE_16_VAL : + printf("16 bytes\n"); + break; + default : + printf("Unknown\n"); + break; + } + } + else + { + /* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */ + ll = (uint32_t)val[LBA_SECTS_MSB] << 16 | val[LBA_SECTS_LSB]; + mm = 0; bbbig = 0; + if ( (ll > 0x00FBFC10) && (!val[LCYLS])) + printf("\tCHS addressing not supported\n"); + else + { + jj = val[WHATS_VALID] & OK_W54_58; + printf("\tLogical\t\tmax\tcurrent\n\tcylinders\t%u\t%u\n\theads\t\t%u\t%u\n\tsectors/track\t%u\t%u\n\t--\n", + val[LCYLS],jj?val[LCYLS_CUR]:0, val[LHEADS],jj?val[LHEADS_CUR]:0, val[LSECTS],jj?val[LSECTS_CUR]:0); + + if_printf(((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES])), + "\tbytes/track: %u\tbytes/sector: %u\n",val[TRACK_BYTES], val[SECT_BYTES]); + + if(jj) + { + mm = (uint32_t)val[CAPACITY_MSB] << 16 | val[CAPACITY_LSB]; + if(like_std < 3) + { + /* check Endian of capacity bytes */ + nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR]; + oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB]; + if(abs(mm - nn) > abs(oo - nn)) + mm = oo; + } + printf("\tCHS current addressable sectors:%11u\n",mm); + } + } + /* LBA addressing */ + printf("\tLBA user addressable sectors:%11u\n",ll); + if( ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && + (val[CMDS_SUPP_1] & SUPPORT_48_BIT) ) + { + bbbig = (__u64)val[LBA_64_MSB] << 48 | + (__u64)val[LBA_48_MSB] << 32 | + (__u64)val[LBA_MID] << 16 | + val[LBA_LSB] ; + printf("\tLBA48 user addressable sectors:%11llu\n",bbbig); + } + + if (!bbbig) + bbbig = (__u64)(ll>mm ? ll : mm); /* # 512 byte blocks */ + printf("\tdevice size with M = 1024*1024: %11llu MBytes\n",bbbig>>11); + bbbig = (bbbig<<9)/1000000; + printf("\tdevice size with M = 1000*1000: %11llu MBytes ",bbbig); + + if_else_printf((bbbig > 1000),"(%llu GB)\n","\n",bbbig/1000); + + } + + /* hw support of commands (capabilities) */ + printf("Capabilities:\n\t"); + + if(dev == ATAPI_DEV) + { + if(eqpt != CDROM) + if_printf((val[CAPAB_0] & CMD_Q_SUP),"Cmd queuing, "); + + if_printf((val[CAPAB_0] & OVLP_SUP),"Cmd overlap, "); + } + if_printf((val[CAPAB_0] & LBA_SUP),"LBA, "); + + if(like_std != 1) + { + printf("IORDY"); + if_printf((!(val[CAPAB_0] & IORDY_SUP)),"(may be)"); + if_else_printf((val[CAPAB_0] & IORDY_OFF),"(can","(cannot"); + printf(" be disabled)\n"); + } + else + printf("no IORDY\n"); + + if((like_std == 1) && val[BUF_TYPE]) + { + kk = val[BUF_TYPE]; + printf("\tBuffer type: %04x: ",kk); + if_else_printf((kk < 2),"single port, single-sector","dual port, multi-sector"); + if_printf((kk > 2)," with read caching ability"); + printf("\n"); + } + jj = 0; + if((min_std == 1) && (val[BUFFER__SIZE] && (val[BUFFER__SIZE] != NOVAL_1))) + { + printf("\tBuffer size: %.1fkB",(float)val[BUFFER__SIZE]/2); + jj = 1; + } + if((min_std < 4) && (val[RW_LONG])) + { + printf("\tbytes avail on r/w long: %u",val[RW_LONG]); + jj = 1; + } + if((eqpt != CDROM) && (like_std > 3)) + { + printf("\tQueue depth: %u",(val[QUEUE_DEPTH] & DEPTH_BITS)+1); + jj = 1; + } + if_printf(jj,"\n"); + + if(dev == ATA_DEV) + { + if(like_std == 1) + printf("\tCan%s perform double-word IO\n",(!val[DWORD_IO]) ?"not":""); + else + { + printf("\tStandby timer values: spec'd by "); + if_else_printf((val[CAPAB_0] & STD_STBY),"Standard","Vendor"); + if((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL)) + printf(", %s device specific minimum\n",(val[CAPAB_1] & MIN_STANDBY_TIMER)?"with":"no"); + else + printf("\n"); + } + printf("\tR/W multiple sector transfer: "); + if((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER)) + printf("not supported\n"); + else + { + printf("Max = %u\tCurrent = ",val[SECTOR_XFER_MAX] & SECTOR_XFER); + if_else_printf((val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID), + "%u\n","?\n",val[SECTOR_XFER_CUR] & SECTOR_XFER); + } + if((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008)) + { + /* We print out elsewhere whether the APM feature is enabled or + not. If it's not enabled, let's not repeat the info; just print + nothing here. */ + printf("\tAdvancedPM level: "); + if ( (val[ADV_PWR] & 0xFF00) == 0x4000 ) + { + uint8_t apm_level = val[ADV_PWR] & 0x00FF; + printf("%u (0x%x)\n", apm_level, apm_level); + } + else + printf("unknown setting (0x%04x)\n", val[ADV_PWR]); + } + if(like_std > 5) + { + if_printf(val[ACOUSTIC],"\tRecommended acoustic management value: %u, current value: %u\n", + (val[ACOUSTIC] >> 8) & 0x00ff, val[ACOUSTIC] & 0x00ff); + } + } + else + { + /* ATAPI */ + if(eqpt != CDROM) + if_printf((val[CAPAB_0] & SWRST_REQ),"\tATA sw reset required\n"); + + if(val[PKT_REL] || val[SVC_NBSY]) + { + printf("\tOverlap support:"); + if_printf(val[PKT_REL]," %uus to release bus.",val[PKT_REL]); + if_printf(val[SVC_NBSY]," %uus to clear BSY after SERVICE cmd.",val[SVC_NBSY]); + printf("\n"); + } + } + + /* DMA stuff. Check that only one DMA mode is selected. */ + printf("\tDMA: "); + if(!(val[CAPAB_0] & DMA_SUP)) + printf("not supported\n"); + else + { + if_printf((val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA]), + " sdma%u\n",(val[DMA_MODE] & MODE) >> 8); + if(val[SINGLE_DMA]) + { + jj = val[SINGLE_DMA]; + kk = val[SINGLE_DMA] >> 8; + err_dma += mode_loop(jj,kk,'s',&have_mode); + } + if(val[MULTI_DMA]) + { + jj = val[MULTI_DMA]; + kk = val[MULTI_DMA] >> 8; + err_dma += mode_loop(jj,kk,'m',&have_mode); + } + if((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) + { + jj = val[ULTRA_DMA]; + kk = val[ULTRA_DMA] >> 8; + err_dma += mode_loop(jj,kk,'u',&have_mode); + } + if_printf((err_dma || !have_mode),"(?)"); + printf("\n"); + + if_printf(((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP)), + "\t Interleaved DMA support\n"); + + if((val[WHATS_VALID] & OK_W64_70) && + (val[DMA_TIME_MIN] || val[DMA_TIME_NORM])) + { + printf("\t Cycle time:"); + if_printf(val[DMA_TIME_MIN]," min=%uns",val[DMA_TIME_MIN]); + if_printf(val[DMA_TIME_NORM]," recommended=%uns",val[DMA_TIME_NORM]); + printf("\n"); + } + } + + /* Programmed IO stuff */ + printf("\tPIO: "); + /* If a drive supports mode n (e.g. 3), it also supports all modes less + * than n (e.g. 3, 2, 1 and 0). Print all the modes. */ + if((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP)) + { + jj = ((val[ADV_PIO_MODES] & PIO_SUP) << 3) | 0x0007; + for(ii = 0; ii <= PIO_MODE_MAX ; ii++) + { + if_printf((jj & 0x0001),"pio%d ",ii); + jj >>=1; + } + printf("\n"); + } + else if(((min_std < 5) || (eqpt == CDROM)) && (val[PIO_MODE] & MODE) ) + { + for(ii = 0; ii <= val[PIO_MODE]>>8; ii++) + printf("pio%d ",ii); + printf("\n"); + } + else + printf("unknown\n"); + + if(val[WHATS_VALID] & OK_W64_70) + { + if(val[PIO_NO_FLOW] || val[PIO_FLOW]) + { + printf("\t Cycle time:"); + if_printf(val[PIO_NO_FLOW]," no flow control=%uns", val[PIO_NO_FLOW]); + if_printf(val[PIO_FLOW]," IORDY flow control=%uns", val[PIO_FLOW]); + printf("\n"); + } + } + + if((val[CMDS_SUPP_1] & VALID) == VALID_VAL) + { + printf("Commands/features:\n\tEnabled\tSupported:\n"); + jj = val[CMDS_SUPP_0]; + kk = val[CMDS_EN_0]; + for(ii = 0; ii < NUM_CMD_FEAT_STR; ii++) + { + if((jj & 0x8000) && (*cmd_feat_str[ii] != '\0')) + { + if_else_printf((kk & 0x8000),"\t *","\t"); + printf("\t%s\n",cmd_feat_str[ii]); + } + jj <<=1; kk<<=1; + if(ii%16 == 15) + { + jj = val[CMDS_SUPP_0+1+(ii/16)]; + kk = val[CMDS_EN_0+1+(ii/16)]; + } + if(ii == 31) + { + if((val[CMDS_SUPP_2] & VALID) != VALID_VAL) + ii +=16; + } + } + } + if_printf(((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP), + "\tRemovable Media Status Notification feature set supported\n"); + + + /* security */ + if((eqpt != CDROM) && (like_std > 3) && + (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME])) + { + printf("Security: \n"); + if_printf((val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1)), + "\tMaster password revision code = %u\n",val[PSWD_CODE]); + jj = val[SECU_STATUS]; + if(jj) + { + for(ii = 0; ii < NUM_SECU_STR; ii++) + { + if_else_printf((!(jj & 0x0001)),"\tnot\t%s\n", "\t\t%s\n", secu_str[ii]); + jj >>=1; + } + if(val[SECU_STATUS] & SECU_ENABLED) + { + printf("\tSecurity level "); + if_else_printf((val[SECU_STATUS] & SECU_LEVEL),"maximum\n","high\n"); + } + } + jj = val[ERASE_TIME] & ERASE_BITS; + kk = val[ENH_ERASE_TIME] & ERASE_BITS; + if(jj || kk) + { + printf("\t"); + if_printf(jj,"%umin for SECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1); + if_printf(kk,"%umin for ENHANCED SECURITY ERASE UNIT.", kk==ERASE_BITS ? 508 : kk<<1); + printf("\n"); + } + } + + /* reset result */ + if((val[HWRST_RSLT] & VALID) == VALID_VAL) + { + printf("HW reset results:\n"); + if_else_printf((val[HWRST_RSLT] & CBLID),"\tCBLID- above Vih\n","\tCBLID- below Vih\n"); + + if(val[HWRST_RSLT] & RST0) + { + printf("\tDevice num = 0"); + jj = val[HWRST_RSLT]; + } + else + { + printf("\tDevice num = 1"); + jj = val[HWRST_RSLT] >> 8; + } + + if((jj & DEV_DET) == JUMPER_VAL) + printf(" determined by the jumper"); + else if((jj & DEV_DET) == CSEL_VAL) + printf(" determined by CSEL"); + printf("\n"); + } + + /* more stuff from std 5 */ + if((like_std > 4) && (eqpt != CDROM)) + { + if(val[CFA_PWR_MODE] & VALID_W160) + { + printf("CFA power mode 1:\n\t"); + if_else_printf((val[CFA_PWR_MODE] & PWR_MODE_OFF),"disabled","enabled"); + + if_printf((val[CFA_PWR_MODE] & PWR_MODE_REQ)," and required by some commands"); + printf("\n"); + + if_printf((val[CFA_PWR_MODE] & MAX_AMPS),"\tMaximum current = %uma\n",val[CFA_PWR_MODE] & MAX_AMPS); + } + if((val[INTEGRITY] & SIG) == SIG_VAL) + { + printf("Checksum: "); + if_printf(chksum,"in"); + printf("correct\n"); + } + } + + exit(0); +} +#endif + +static int verbose = 0, get_identity = 0, get_geom = 0, noisy = 1, quiet = 0; +static int flagcount = 0, do_flush = 0, is_scsi_hd = 0, is_xt_hd = 0; +static int do_ctimings, do_timings = 0; + +static unsigned long set_readahead= 0, get_readahead= 0, Xreadahead= 0; +static unsigned long set_readonly = 0, get_readonly = 0, readonly = 0; +static unsigned long set_unmask = 0, get_unmask = 0, unmask = 0; +static unsigned long set_mult = 0, get_mult = 0, mult = 0; +#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA +static unsigned long set_dma = 0, get_dma = 0, dma = 0; +#endif +static unsigned long set_dma_q = 0, get_dma_q = 0, dma_q = 0; +static unsigned long set_nowerr = 0, get_nowerr = 0, nowerr = 0; +static unsigned long set_keep = 0, get_keep = 0, keep = 0; +static unsigned long set_io32bit = 0, get_io32bit = 0, io32bit = 0; +static unsigned long set_piomode = 0, noisy_piomode= 0; +static int piomode = 0; +#ifdef HDIO_DRIVE_CMD +static unsigned long set_dkeep = 0, get_dkeep = 0, dkeep = 0; +static unsigned long set_standby = 0, get_standby = 0, standby_requested= 0; +static unsigned long set_xfermode = 0, get_xfermode = 0; +static int xfermode_requested= 0; +static unsigned long set_lookahead= 0, get_lookahead= 0, lookahead= 0; +static unsigned long set_prefetch = 0, get_prefetch = 0, prefetch = 0; +static unsigned long set_defects = 0, get_defects = 0, defects = 0; +static unsigned long set_wcache = 0, get_wcache = 0, wcache = 0; +static unsigned long set_doorlock = 0, get_doorlock = 0, doorlock = 0; +static unsigned long set_seagate = 0, get_seagate = 0; +static unsigned long set_standbynow = 0, get_standbynow = 0; +static unsigned long set_sleepnow = 0, get_sleepnow = 0; +static unsigned long get_powermode = 0; +static unsigned long set_apmmode = 0, get_apmmode= 0, apmmode = 0; +#endif +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static int get_IDentity = 0; +#endif +#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF +static int unregister_hwif = 0; +static int hwif = 0; +#endif +#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF +static int scan_hwif = 0; +static int hwif_data = 0; +static int hwif_ctrl = 0; +static int hwif_irq = 0; +#endif +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF +static int set_busstate = 0, get_busstate = 0, busstate = 0; +#endif +static int reread_partn = 0; + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET +static int perform_reset = 0; +#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */ +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF +static int perform_tristate = 0, tristate = 0; +#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */ + +// Historically, if there was no HDIO_OBSOLETE_IDENTITY, then +// then the HDIO_GET_IDENTITY only returned 142 bytes. +// Otherwise, HDIO_OBSOLETE_IDENTITY returns 142 bytes, +// and HDIO_GET_IDENTITY returns 512 bytes. But the latest +// 2.5.xx kernels no longer define HDIO_OBSOLETE_IDENTITY +// (which they should, but they should just return -EINVAL). +// +// So.. we must now assume that HDIO_GET_IDENTITY returns 512 bytes. +// On a really old system, it will not, and we will be confused. +// Too bad, really. + +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static const char *cfg_str[] = +{ "", " HardSect", " SoftSect", " NotMFM", + " HdSw>15uSec", " SpinMotCtl", " Fixed", " Removeable", + " DTR<=5Mbs", " DTR>5Mbs", " DTR>10Mbs", " RotSpdTol>.5%", + " dStbOff", " TrkOff", " FmtGapReq", " nonMagnetic" +}; + +static const char *BuffType[] = {"unknown", "1Sect", "DualPort", "DualPortCache"}; + +static void dump_identity (const struct hd_driveid *id) +{ + int i; + char pmodes[64] = {0,}, dmodes[128]={0,}, umodes[128]={0,}; + const unsigned short int *id_regs= (const void*) id; + unsigned long capacity; + + printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n Config={", + id->model, id->fw_rev, id->serial_no); + for (i=0; i<=15; i++) + if_printf((id->config & (1<cyls, id->heads, id->sectors, id->track_bytes, + id->sector_bytes, id->ecc_bytes); + + if (id->buf_type > 3) + printf("%s%u", " BuffType=", id->buf_type); + else + printf("%s%s", " BuffType=", BuffType[id->buf_type]); + + printf(", BuffSize=%ukB, MaxMultSect=%u", id->buf_size/2, id->max_multsect); + if (id->max_multsect) + { + printf(", MultSect="); + if (!(id->multsect_valid&1)) + printf("?%u?", id->multsect); + else if (id->multsect) + printf("%u", id->multsect); + else + printf("off"); + } + printf("\n"); + if (id->tPIO <= 5) + { + strcat(pmodes, "pio0 "); + if_strcat((id->tPIO >= 1), pmodes, "pio1 "); + if_strcat((id->tPIO >= 2), pmodes, "pio2 "); + + } + if_printf((!(id->field_valid&1))," (maybe):"); +#if __BYTE_ORDER == __BIG_ENDIAN + capacity = (id->cur_capacity0 << 16) | id->cur_capacity1; +#else + capacity = (id->cur_capacity1 << 16) | id->cur_capacity0; +#endif + printf(" CurCHS=%u/%u/%u, CurSects=%lu, LBA=%s",id->cur_cyls, id->cur_heads, + id->cur_sectors, capacity , + ((id->capability&2)==0)?"no":"yes"); + + if_printf((id->capability&2),", LBAsects=%u", id->lba_capacity); + + if (id->capability&1) + { + if (id->dma_1word | id->dma_mword) + { + if_strcat((id->dma_1word & 0x100), dmodes, "*"); + if_strcat((id->dma_1word & 1), dmodes, "sdma0 "); + if_strcat((id->dma_1word & 0x200), dmodes, "*"); + if_strcat((id->dma_1word & 2), dmodes, "sdma1 "); + if_strcat((id->dma_1word & 0x400), dmodes, "*"); + if_strcat((id->dma_1word & 4), dmodes, "sdma2 "); + if_strcat((id->dma_1word & 0xf800), dmodes, "*"); + if_strcat((id->dma_1word & 0xf8), dmodes, "sdma? "); + if_strcat((id->dma_mword & 0x100), dmodes, "*"); + if_strcat((id->dma_mword & 1), dmodes, "mdma0 "); + if_strcat((id->dma_mword & 0x200), dmodes, "*"); + if_strcat((id->dma_mword & 2), dmodes, "mdma1 "); + if_strcat((id->dma_mword & 0x400), dmodes, "*"); + if_strcat((id->dma_mword & 4), dmodes, "mdma2 "); + if_strcat((id->dma_mword & 0xf800), dmodes, "*"); + if_strcat((id->dma_mword & 0xf8), dmodes, "mdma? "); + } + } + printf("\n IORDY="); + if (id->capability&8) + printf((id->capability&4) ? "on/off" : "yes"); + else + printf("no"); + + if ((id->capability&8) || (id->field_valid&2)) + { + if (id->field_valid&2) + { + printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy); + if_strcat((id->eide_pio_modes & 1), pmodes, "pio3 "); + if_strcat((id->eide_pio_modes & 2), pmodes, "pio4 "); + if_strcat((id->eide_pio_modes &~3), pmodes, "pio? "); + } + if (id->field_valid&4) + { + if_strcat((id->dma_ultra & 0x100),umodes,"*"); + if_strcat((id->dma_ultra & 0x001),umodes,"udma0 "); + if_strcat((id->dma_ultra & 0x200),umodes,"*"); + if_strcat((id->dma_ultra & 0x002),umodes,"udma1 "); + if_strcat((id->dma_ultra & 0x400),umodes,"*"); + if_strcat((id->dma_ultra & 0x004),umodes,"udma2 "); +#ifdef __NEW_HD_DRIVE_ID + if (id->hw_config & 0x2000) + { +#else /* !__NEW_HD_DRIVE_ID */ + if (id->word93 & 0x2000) + { +#endif /* __NEW_HD_DRIVE_ID */ + if_strcat((id->dma_ultra & 0x0800),umodes,"*"); + if_strcat((id->dma_ultra & 0x0008),umodes,"udma3 "); + if_strcat((id->dma_ultra & 0x1000),umodes,"*"); + if_strcat((id->dma_ultra & 0x0010),umodes,"udma4 "); + if_strcat((id->dma_ultra & 0x2000),umodes,"*"); + if_strcat((id->dma_ultra & 0x0020),umodes,"udma5 "); + if_strcat((id->dma_ultra & 0x4000),umodes,"*"); + if_strcat((id->dma_ultra & 0x0040),umodes,"udma6 "); + if_strcat((id->dma_ultra & 0x8000),umodes,"*"); + if_strcat((id->dma_ultra & 0x0080),umodes,"udma7 "); + } + } + } + if_printf(((id->capability&1) && (id->field_valid&2)), + ", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time); + printf("\n PIO modes: %s", pmodes); + if_printf((*dmodes),"\n DMA modes: %s", dmodes); + if_printf((*umodes),"\n UDMA modes: %s", umodes); + + printf("\n AdvancedPM=%s",((id_regs[83]&8)==0)?"no":"yes"); + if (id_regs[83] & 8) + { + if (!(id_regs[86]&8)) + printf(": disabled (255)"); + else if ((id_regs[91]&0xFF00)!=0x4000) + printf(": unknown setting"); + else + printf(": mode=0x%02X (%u)",id_regs[91]&0xFF,id_regs[91]&0xFF); + } + if_printf( (id_regs[82]&0x20)," WriteCache=%s",(id_regs[85]&0x20) ? "enabled" : "disabled"); +#ifdef __NEW_HD_DRIVE_ID + if ((id->minor_rev_num && id->minor_rev_num <= 31) || (id->major_rev_num && id->minor_rev_num <= 31)) + { + printf("\n Drive conforms to: "); + if_else_printf((id->minor_rev_num <= 31),"%s: ","unknown: ", minor_str[id->minor_rev_num]); + if (id->major_rev_num < 31) + { + for (i=0; i <= 15; i++) + if_printf((id->major_rev_num & (1< 0; --i) + { + if (seek_to_zero (fd)) + goto quit; + if (read_big_block (fd, buf)) + goto quit; + } + getitimer(ITIMER_REAL, &e2); + correction = (e1.it_value.tv_sec - e2.it_value.tv_sec) + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0); + + /* Now remove the lseek() from the correction factor */ + getitimer(ITIMER_REAL, &e1); + for (i = (BUFCACHE_FACTOR * TIMING_BUF_COUNT) ; i > 0; --i) + { + if (seek_to_zero (fd)) + goto quit; + } + getitimer(ITIMER_REAL, &e2); + correction -= (e1.it_value.tv_sec - e2.it_value.tv_sec) + + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0); + + if ((BUFCACHE_FACTOR * timing_MB) >= correction) /* more than 1MB/s */ + printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n", + (BUFCACHE_FACTOR * timing_MB), correction, + (BUFCACHE_FACTOR * timing_MB) / correction); + else + printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n", + (BUFCACHE_FACTOR * timing_MB), correction, + (BUFCACHE_FACTOR * timing_MB) / correction * 1024); + correction /= BUFCACHE_FACTOR; + + flush_buffer_cache(fd); + sleep(1); + } + else /* Time device */ + { + printf(" Timing buffered disk reads: "); + fflush(stdout); + + /* + * getitimer() is used rather than gettimeofday() because + * it is much more consistent (on my machine, at least). + */ + setitimer(ITIMER_REAL, &(struct itimerval){{1000,0},{1000,0}}, NULL); + + /* Now do the timings for real */ + getitimer(ITIMER_REAL, &e1); + for (i = TIMING_BUF_COUNT; i > 0; --i) + { + if (read_big_block (fd, buf)) + goto quit; + } + getitimer(ITIMER_REAL, &e2); + + elapsed = (e1.it_value.tv_sec - e2.it_value.tv_sec) + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0); + + if (timing_MB >= elapsed) /* more than 1MB/s */ + printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n",timing_MB, elapsed, timing_MB / elapsed); + else + printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n",timing_MB, elapsed, timing_MB / elapsed * 1024); + + /*"Hmm.. suspicious results: probably not enough free memory for a proper test.");*/ + if (elapsed <= (correction * 2)) + bb_error_msg(bb_msg_memory_exhausted); + +#if 0 /* the "estimate" is just plain wrong for many systems.. */ + else if (correction != 0.0) { + printf(" Estimating raw driver speed: "); + elapsed -= correction; + if (timing_MB >= elapsed) /* more than 1MB/s */ + printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n", + timing_MB, elapsed, timing_MB / elapsed); + else + printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n", + timing_MB, elapsed, timing_MB / elapsed * 1024); + } +#endif + } +quit: + if (-1 == shmdt(buf)) + bb_error_msg (bb_msg_shared_mem,"detach"); /*"could not detach sharedmem buf"*/ +} + + +static void no_scsi (void) +{ + /*" operation not supported on SCSI disks"*/ + if (is_scsi_hd) + bb_error_msg_and_die(bb_msg_op_not_supp,"SCSI"); +} + +static void no_xt (void) +{ + if (is_xt_hd) + bb_error_msg_and_die(bb_msg_op_not_supp,"XT"); +} + +static void on_off (unsigned int value) +{ + printf(value ? " (on)\n" : " (off)\n"); +} + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF +static void bus_state_value (unsigned int value) +{ + const char *string; + + switch (value) + { + case BUSSTATE_ON: + string = " (on)\n"; + break; + case BUSSTATE_OFF: + string = " (off)\n"; + break; + case BUSSTATE_TRISTATE: + string = " (tristate)\n"; + break; + default: + string = " (unknown: %d)\n"; + break; + } + printf(string, value); +} +#endif + +#ifdef HDIO_DRIVE_CMD +static void interpret_standby (unsigned int standby) +{ + printf(" ("); + switch(standby) + { + case 0: + printf("off"); + break; + case 252: + printf("21 minutes"); + break; + case 253: + printf("vendor-specific"); + break; + case 254: + printf("?reserved"); + break; + case 255: + printf("21 minutes + 15 seconds"); + break; + default: + if (standby <= 240) + { + unsigned int secs = standby * 5; + unsigned int mins = secs / 60; + secs %= 60; + if_printf(mins,"%u minutes", mins); + if_printf((mins && secs)," + "); + if_printf(secs,"%u seconds", secs); + } + else if (standby <= 251) + { + unsigned int mins = (standby - 240) * 30; + unsigned int hrs = mins / 60; + mins %= 60; + if_printf(hrs,"%u hours", hrs); + if_printf((hrs && mins)," + "); + if_printf(mins,"%u minutes", mins); + } + else + printf("illegal value"); + break; + } + printf(")\n"); +} + +struct xfermode_entry { + int val; + const char *name; +}; + +static const struct xfermode_entry xfermode_table[] = { + { 8, "pio0" }, + { 9, "pio1" }, + { 10, "pio2" }, + { 11, "pio3" }, + { 12, "pio4" }, + { 13, "pio5" }, + { 14, "pio6" }, + { 15, "pio7" }, + { 16, "sdma0" }, + { 17, "sdma1" }, + { 18, "sdma2" }, + { 19, "sdma3" }, + { 20, "sdma4" }, + { 21, "sdma5" }, + { 22, "sdma6" }, + { 23, "sdma7" }, + { 32, "mdma0" }, + { 33, "mdma1" }, + { 34, "mdma2" }, + { 35, "mdma3" }, + { 36, "mdma4" }, + { 37, "mdma5" }, + { 38, "mdma6" }, + { 39, "mdma7" }, + { 64, "udma0" }, + { 65, "udma1" }, + { 66, "udma2" }, + { 67, "udma3" }, + { 68, "udma4" }, + { 69, "udma5" }, + { 70, "udma6" }, + { 71, "udma7" }, + { 0, NULL } +}; + +static int translate_xfermode(char * name) +{ + const struct xfermode_entry *tmp; + char *endptr; + int val = -1; + + + for (tmp = xfermode_table; tmp->name != NULL; ++tmp) + { + if (!strcmp(name, tmp->name)) + return tmp->val; + + } + + val = strtol(name, &endptr, 10); + if (*endptr == '\0') + return val; + + return -1; +} + +static void interpret_xfermode (unsigned int xfermode) +{ + printf(" ("); + switch(xfermode) { + case 0: + printf("default PIO mode"); + break; + case 1: + printf("default PIO mode, disable IORDY"); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + printf("PIO flow control mode%u", xfermode-8); + break; + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + printf("singleword DMA mode%u", xfermode-16); + break; + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + printf("multiword DMA mode%u", xfermode-32); + break; + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + printf("UltraDMA mode%u", xfermode-64); + break; + default: + printf("unknown, probably not valid"); + break; + } + printf(")\n"); +} +#endif /* HDIO_DRIVE_CMD */ + +#ifndef VXVM_MAJOR +#define VXVM_MAJOR 199 +#endif + +#ifndef CCISS_MAJOR +#define CCISS_MAJOR 104 +#endif + +static void process_dev (char *devname) +{ + int fd; + static long parm, multcount; + struct stat stat_buf; +#ifndef HDIO_DRIVE_CMD + int force_operation = 0; +#endif + if (stat(devname,&stat_buf)) + bb_perror_msg_and_die(devname); + + switch(major(stat_buf.st_rdev)) + { +#ifdef SCSI_DISK0_MAJOR + case (SCSI_DISK0_MAJOR): + case (SCSI_DISK1_MAJOR): + case (SCSI_DISK2_MAJOR): + case (SCSI_DISK3_MAJOR): + case (SCSI_DISK4_MAJOR): + case (SCSI_DISK5_MAJOR): + case (SCSI_DISK6_MAJOR): + case (SCSI_DISK7_MAJOR): +#else + case (SCSI_DISK_MAJOR): +#endif +#ifdef MD_MAJOR + case (MD_MAJOR): +#endif + case (VXVM_MAJOR): +#ifdef LVM_BLK_MAJOR + case (LVM_BLK_MAJOR): +#endif + case (CCISS_MAJOR): + is_scsi_hd = 1; + break; +#ifdef XT_DISK_MAJOR + case (XT_DISK_MAJOR): + is_xt_hd = 1; + break; +#endif + case IDE0_MAJOR: + case IDE1_MAJOR: +#ifdef IDE2_MAJOR + case IDE2_MAJOR: +#endif +#ifdef IDE3_MAJOR + case IDE3_MAJOR: +#endif +#ifdef IDE4_MAJOR + case IDE4_MAJOR: +#endif +#ifdef IDE5_MAJOR + case IDE5_MAJOR: +#endif +#ifdef IDE6_MAJOR + case IDE6_MAJOR: +#endif +#ifdef IDE7_MAJOR + case IDE7_MAJOR: +#endif +#ifdef IDE8_MAJOR + case IDE8_MAJOR: +#endif +#ifdef IDE9_MAJOR + case IDE9_MAJOR: +#endif + break; /* do nothing */ + default: + bb_error_msg_and_die("%s not supported",devname); + } + + fd = bb_xopen (devname, O_RDONLY|O_NONBLOCK); + if_printf( (!quiet),"\n%s:\n", devname); + + if (set_readahead) + { + if_printf(get_readahead," setting fs readahead to %ld\n", Xreadahead); + bb_ioctl(fd, BLKRASET,(int *)Xreadahead,"BLKRASET"); + } +#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF + if (unregister_hwif) + { + no_scsi(); + printf(" attempting to unregister hwif#%u\n", hwif); + bb_ioctl(fd, HDIO_UNREGISTER_HWIF,(int *)hwif,"HDIO_UNREGISTER_HWIF"); + } +#endif +#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF + if (scan_hwif) + { + int args[3]; + no_scsi(); + printf(" attempting to scan hwif (0x%x, 0x%x, %u)\n", hwif_data, hwif_ctrl, hwif_irq); + args[0] = hwif_data; + args[1] = hwif_ctrl; + args[2] = hwif_irq; + bb_ioctl(fd, HDIO_SCAN_HWIF, args, "HDIO_SCAN_HWIF"); + } +#endif + if (set_piomode) + { + no_scsi(); + no_xt(); + + if (noisy_piomode) + { + printf(" attempting to "); + if (piomode == 255) + printf("auto-tune PIO mode\n"); + else if (piomode < 100) + printf("set PIO mode to %d\n", piomode); + else if (piomode < 200) + printf("set MDMA mode to %d\n", (piomode-100)); + else + printf("set UDMA mode to %d\n", (piomode-200)); + } + bb_ioctl(fd, HDIO_SET_PIO_MODE, (int *)piomode, "HDIO_SET_PIO_MODE"); + } + if (set_io32bit) + { + no_scsi(); + no_xt(); + if_printf(get_io32bit," setting 32-bit IO_support flag to %ld\n", io32bit); + bb_ioctl(fd, HDIO_SET_32BIT, (int *)io32bit, "HDIO_SET_32BIT"); + } + if (set_mult) + { + no_scsi(); + no_xt(); + if_printf(get_mult, " setting multcount to %ld\n", mult); + if(ioctl(fd, HDIO_SET_MULTCOUNT, mult)) + bb_perror_msg("HDIO_SET_MULTCOUNT"); +#ifndef HDIO_DRIVE_CMD + else + force_operation = 1; +#endif + } + if (set_readonly) + { + if_printf_on_off(get_readonly," setting readonly to %ld", readonly); + bb_ioctl(fd, BLKROSET, &readonly, "BLKROSET"); + } + if (set_unmask) + { + no_scsi(); + no_xt(); + if_printf_on_off(get_unmask," setting unmaskirq to %ld", unmask); + bb_ioctl(fd, HDIO_SET_UNMASKINTR, (int *)unmask, "HDIO_SET_UNMASKINTR"); + } +#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA + if (set_dma) + { + no_scsi(); + if_printf_on_off(get_dma," setting using_dma to %ld", dma); + bb_ioctl(fd, HDIO_SET_DMA, (int *)dma, "HDIO_SET_DMA"); + } +#endif /* CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA */ + if (set_dma_q) + { + no_scsi(); + if_printf_on_off(get_dma_q," setting DMA queue_depth to %ld", dma_q); + bb_ioctl(fd, HDIO_SET_QDMA, (int *)dma_q, "HDIO_SET_QDMA"); + } + if (set_nowerr) + { + no_scsi(); + no_xt(); + if_printf_on_off(get_nowerr," setting nowerr to %ld", nowerr); + bb_ioctl(fd, HDIO_SET_NOWERR, (int *)nowerr,"HDIO_SET_NOWERR"); + } + if (set_keep) + { + no_scsi(); + no_xt(); + if_printf_on_off(get_keep," setting keep_settings to %ld", keep); + bb_ioctl(fd, HDIO_SET_KEEPSETTINGS, (int *)keep,"HDIO_SET_KEEPSETTINGS"); + } +#ifdef HDIO_DRIVE_CMD + if (set_doorlock) + { + unsigned char args[4] = {0,0,0,0}; + no_scsi(); + no_xt(); + + args[0] = doorlock ? WIN_DOORLOCK : WIN_DOORUNLOCK; + if_printf_on_off(get_doorlock," setting drive doorlock to %ld", doorlock); + bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(doorlock)"); + } + if (set_dkeep) + { + /* lock/unlock the drive's "feature" settings */ + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + no_scsi(); + no_xt(); + + if_printf_on_off(get_dkeep," setting drive keep features to %ld", dkeep); + args[2] = dkeep ? 0x66 : 0xcc; + bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(keepsettings)"); + } + if (set_defects) + { + unsigned char args[4] = {WIN_SETFEATURES,0,0x04,0}; + no_scsi(); + args[2] = defects ? 0x04 : 0x84; + if_printf(get_defects," setting drive defect-mgmt to %ld\n", defects); + bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(defectmgmt)"); + } + if (set_prefetch) + { + unsigned char args[4] = {WIN_SETFEATURES,0,0xab,0}; + no_scsi(); + no_xt(); + + args[1] = prefetch; + if_printf(get_prefetch," setting drive prefetch to %ld\n", prefetch); + bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setprefetch)"); + } + if (set_xfermode) + { + unsigned char args[4] = {WIN_SETFEATURES,0,3,0}; + no_scsi(); + no_xt(); + + args[1] = xfermode_requested; + if (get_xfermode) + { + printf(" setting xfermode to %d", xfermode_requested); + interpret_xfermode(xfermode_requested); + } + bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(setxfermode)"); + } + if (set_lookahead) + { + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + no_scsi(); + no_xt(); + + args[2] = lookahead ? 0xaa : 0x55; + if_printf_on_off(get_lookahead," setting drive read-lookahead to %ld", lookahead); + bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setreadahead)"); + } + if (set_apmmode) + { + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + no_scsi(); + apmmode=check_if_min_and_set_val(apmmode,1); + apmmode=check_if_maj_and_set_val(apmmode,255); + if_printf(get_apmmode," setting APM level to"); + if (apmmode==255) + { + /* disable Advanced Power Management */ + args[2] = 0x85; /* feature register */ + if_printf(get_apmmode," disabled\n"); + } + else + { + /* set Advanced Power Management mode */ + args[2] = 0x05; /* feature register */ + args[1] = apmmode; /* sector count register */ + if_printf(get_apmmode," 0x%02lX (%ld)\n",apmmode,apmmode); + } + bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD"); + } + if (set_wcache) + { +#ifdef DO_FLUSHCACHE +#ifndef WIN_FLUSHCACHE +#define WIN_FLUSHCACHE 0xe7 +#endif + unsigned char flushcache[4] = {WIN_FLUSHCACHE,0,0,0}; +#endif /* DO_FLUSHCACHE */ + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + no_scsi(); + no_xt(); + args[2] = wcache ? 0x02 : 0x82; + if_printf_on_off(get_wcache," setting drive write-caching to %ld", wcache); +#ifdef DO_FLUSHCACHE + if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache)) + bb_perror_msg ("HDIO_DRIVE_CMD(flushcache)"); +#endif /* DO_FLUSHCACHE */ + bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setcache)"); +#ifdef DO_FLUSHCACHE + if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache)) + bb_perror_msg ("HDIO_DRIVE_CMD(flushcache)"); +#endif /* DO_FLUSHCACHE */ + } + if (set_standbynow) + { +#ifndef WIN_STANDBYNOW1 +#define WIN_STANDBYNOW1 0xE0 +#endif +#ifndef WIN_STANDBYNOW2 +#define WIN_STANDBYNOW2 0x94 +#endif + unsigned char args1[4] = {WIN_STANDBYNOW1,0,0,0}; + unsigned char args2[4] = {WIN_STANDBYNOW2,0,0,0}; + no_scsi(); + if_printf(get_standbynow," issuing standby command\n"); + if (ioctl(fd, HDIO_DRIVE_CMD, &args1) + && ioctl(fd, HDIO_DRIVE_CMD, &args2)) + bb_perror_msg("HDIO_DRIVE_CMD(standby)"); + } + if (set_sleepnow) + { +#ifndef WIN_SLEEPNOW1 +#define WIN_SLEEPNOW1 0xE6 +#endif +#ifndef WIN_SLEEPNOW2 +#define WIN_SLEEPNOW2 0x99 +#endif + unsigned char args1[4] = {WIN_SLEEPNOW1,0,0,0}; + unsigned char args2[4] = {WIN_SLEEPNOW2,0,0,0}; + no_scsi(); + if_printf(get_sleepnow," issuing sleep command\n"); + if (ioctl(fd, HDIO_DRIVE_CMD, &args1) + && ioctl(fd, HDIO_DRIVE_CMD, &args2)) + bb_perror_msg("HDIO_DRIVE_CMD(sleep)"); + } + if (set_seagate) + { + unsigned char args[4] = {0xfb,0,0,0}; + no_scsi(); + no_xt(); + if_printf(get_seagate," disabling Seagate auto powersaving mode\n"); + bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(seagatepwrsave)"); + } + if (set_standby) + { + unsigned char args[4] = {WIN_SETIDLE1,standby_requested,0,0}; + no_scsi(); + no_xt(); + if (get_standby) + { + printf(" setting standby to %lu", standby_requested); + interpret_standby(standby_requested); + } + bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setidle1)"); + } +#else /* HDIO_DRIVE_CMD */ + if (force_operation) + { + char buf[512]; + flush_buffer_cache(fd); + if (-1 == read(fd, buf, sizeof(buf))) + bb_error_msg("access failed"); + } +#endif /* HDIO_DRIVE_CMD */ + + if (!flagcount) + verbose = 1; + + if ((verbose && !is_scsi_hd && !is_xt_hd) || get_mult || get_identity) + { + no_scsi(); + multcount = -1; + if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount)) + { + if ((verbose && !is_xt_hd) || get_mult) + bb_perror_msg("HDIO_GET_MULTCOUNT"); + } + else if (verbose | get_mult) + { + printf(" multcount = %2ld", multcount); + on_off(multcount); + } + } + if ((verbose && !is_scsi_hd && !is_xt_hd) || get_io32bit) + { + no_scsi(); + no_xt(); + if(ioctl(fd, HDIO_GET_32BIT, &parm)) + bb_perror_msg("HDIO_GET_32BIT"); + else + { + printf(" IO_support =%3ld (", parm); + switch (parm) + { + case 0: + printf("default "); + case 2: + printf("16-bit)\n"); + break; + case 1: + printf("32-bit)\n"); + break; + case 3: + printf("32-bit w/sync)\n"); + break; + case 8: + printf("Request-Queue-Bypass)\n"); + break; + default: + printf("\?\?\?)\n"); + /*esac*/ + } + } + } + if ((verbose && !is_scsi_hd && !is_xt_hd) || get_unmask) + { + no_scsi(); + no_xt(); + bb_ioctl_on_off(fd, HDIO_GET_UNMASKINTR,(unsigned long *)parm, + "HDIO_GET_UNMASKINTR"," unmaskirq = %2ld"); + } + + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA + if ((verbose && !is_scsi_hd) || get_dma) { + no_scsi(); + if(ioctl(fd, HDIO_GET_DMA, &parm)) + bb_perror_msg("HDIO_GET_DMA"); + else + { + printf(" using_dma = %2ld", parm); + if (parm == 8) + printf(" (DMA-Assisted-PIO)\n"); + else + on_off(parm); + } + } +#endif + if (get_dma_q) + { + no_scsi(); + bb_ioctl_on_off (fd, HDIO_GET_QDMA,(unsigned long *)parm, + "HDIO_GET_QDMA"," queue_depth = %2ld"); + } + if ((verbose && !is_scsi_hd && !is_xt_hd) || get_keep) + { + no_scsi(); + no_xt(); + bb_ioctl_on_off (fd, HDIO_GET_KEEPSETTINGS,(unsigned long *)parm, + "HDIO_GET_KEEPSETTINGS"," keepsettings = %2ld"); + } + + if (get_nowerr) + { + no_scsi(); + no_xt(); + bb_ioctl_on_off (fd, HDIO_GET_NOWERR,(unsigned long *)&parm, + " HDIO_GET_NOWERR"," nowerr = %2ld"); + } + if (verbose || get_readonly) + { + bb_ioctl_on_off(fd, BLKROGET,(unsigned long *)parm, + " BLKROGET"," readonly = %2ld"); + } + if ((verbose && !is_scsi_hd) || get_readahead) + { + bb_ioctl_on_off (fd, BLKRAGET, (unsigned long *) parm, + " BLKRAGET"," readahead = %2ld"); + } + if (verbose || get_geom) + { + static const char msg[] = " geometry = %u/%u/%u, sectors = %ld, start = %ld\n"; + static struct hd_geometry g; +#ifdef HDIO_GETGEO_BIG + static struct hd_big_geometry bg; +#endif + + if (ioctl(fd, BLKGETSIZE, &parm)) + bb_perror_msg("BLKGETSIZE"); +#ifdef HDIO_GETGEO_BIG + else if (!ioctl(fd, HDIO_GETGEO_BIG, &bg)) + printf(msg, bg.cylinders, bg.heads, bg.sectors, parm, bg.start); +#endif + else if (ioctl(fd, HDIO_GETGEO, &g)) + bb_perror_msg("HDIO_GETGEO"); + else + printf(msg, g.cylinders, g.heads, g.sectors, parm, g.start); + } +#ifdef HDIO_DRIVE_CMD + if (get_powermode) + { +#ifndef WIN_CHECKPOWERMODE1 +#define WIN_CHECKPOWERMODE1 0xE5 +#endif +#ifndef WIN_CHECKPOWERMODE2 +#define WIN_CHECKPOWERMODE2 0x98 +#endif + unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0}; + const char *state; + no_scsi(); + if (ioctl(fd, HDIO_DRIVE_CMD, &args) + && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */ + && ioctl(fd, HDIO_DRIVE_CMD, &args)) + { + if (errno != EIO || args[0] != 0 || args[1] != 0) + state = "unknown"; + else + state = "sleeping"; + } + else + state = (args[2] == 255) ? "active/idle" : "standby"; + + printf(" drive state is: %s\n", state); + } +#endif +#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET + if (perform_reset) + { + no_scsi(); + no_xt(); + bb_ioctl(fd, HDIO_DRIVE_RESET, NULL, "HDIO_DRIVE_RESET"); + } +#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */ +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF + if (perform_tristate) + { + unsigned char args[4] = {0,tristate,0,0}; + no_scsi(); + no_xt(); + bb_ioctl(fd, HDIO_TRISTATE_HWIF, &args, "HDIO_TRISTATE_HWIF"); + } +#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */ +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY + if (get_identity) + { + static struct hd_driveid id; + + no_scsi(); + no_xt(); + + if (!ioctl(fd, HDIO_GET_IDENTITY, &id)) + { + if (multcount != -1) + { + id.multsect = multcount; + id.multsect_valid |= 1; + } + else + id.multsect_valid &= ~1; + dump_identity(&id); + } + else if (errno == -ENOMSG) + printf(" no identification info available\n"); + else + bb_perror_msg("HDIO_GET_IDENTITY"); + } + + if (get_IDentity) + { + unsigned char args[4+512] = {WIN_IDENTIFY,0,0,1,}; + unsigned i; + + no_scsi(); + no_xt(); + + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + { + args[0] = WIN_PIDENTIFY; + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + { + bb_perror_msg("HDIO_DRIVE_CMD(identify)"); + goto identify_abort; + } + } + for(i=0; i<(sizeof args)/2; i+=2) + __le16_to_cpus((uint16_t *)(&args[i])); + + identify((void *)&args[4], NULL); +identify_abort: + /* VOID */; + } +#endif +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF + if (set_busstate) + { + no_scsi(); + if (get_busstate) + { + printf(" setting bus state to %d", busstate); + bus_state_value(busstate); + } + bb_ioctl(fd, HDIO_SET_BUSSTATE, (int *)busstate, "HDIO_SET_BUSSTATE"); + } +#endif +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF + if (get_busstate) + { + no_scsi(); + if (ioctl(fd, HDIO_GET_BUSSTATE, &parm)) + bb_perror_msg("HDIO_GET_BUSSTATE"); + else + { + printf(" busstate = %2ld", parm); + bus_state_value(parm); + } + } +#endif + if (reread_partn) + bb_ioctl(fd, BLKRRPART, NULL, "BLKRRPART"); + + + if (do_ctimings) + do_time(0,fd); /*time cache */ + if (do_timings) + do_time(1,fd); /*time device */ + if (do_flush) + flush_buffer_cache (fd); + close (fd); +} + +static char * GET_NUMBER(char *p, unsigned long *flag, unsigned long *num) +{ + *num = 0; + while (isdigit(*p)) { + *flag = 1; + *num = (*num * 10) + (*p++ - '0'); + } + return p; +} + +static char * GET_STRING(char *p, unsigned long *flag, int *num) +{ + char *tmpstr; + char name[32]; + tmpstr = name; + tmpstr[0] = '\0'; + while (isalnum(*p) && (tmpstr - name) < 31) { + tmpstr[0] = *p++; + tmpstr[1] = '\0'; + ++tmpstr; + } + *num = translate_xfermode(name); + if (*num == -1) + *flag = 0; + else + *flag = 1; + return p; +} + +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY +static int fromhex (unsigned char c) +{ + if (c >= 'a' && c <= 'f') + return 10 + (c - 'a'); + if (c >= '0' && c <= '9') + return (c - '0'); + bb_error_msg_and_die("bad char: '%c' 0x%02x", c, c); +} + +static int identify_from_stdin (void) +{ + unsigned short sbuf[800]; + unsigned char buf[1600], *b = (unsigned char *)buf; + int i, count = read(0, buf, 1280); + + if (count != 1280) + bb_error_msg_and_die("read(1280 bytes) failed (rc=%d)", count); + for (i = 0; count >= 4; ++i) + { + sbuf[i] = (fromhex(b[0]) << 12) | (fromhex(b[1]) << 8) | (fromhex(b[2]) << 4) | fromhex(b[3]); + __le16_to_cpus((uint16_t *)(&sbuf[i])); + b += 5; + count -= 5; + } + identify(sbuf, NULL); + return 0; +} +#endif + +static void missing_arg(int arg, char c, char* add) +{ + if (!arg) + bb_error_msg("-%c: missing value %s", c, (add!=NULL)? add :""); +} + +/* our main() routine: */ +int hdparm_main(int argc, char **argv) +{ + char c, *p; + + ++argv; + if (!--argc) + bb_show_usage(); + + while (argc--) + { +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY + if (!strcmp("-Istdin", *argv)) + { + exit(identify_from_stdin()); + } +#endif + p = *argv++; + if (*p == '-') + { + if (!*++p) + bb_show_usage(); + while ((c = *p++)) + { + ++flagcount; + switch (c) + { + case 'V': + /*bb_error_msg_and_die("%s", VERSION);*/ + /* We have to return 0 here and not 1 */ + printf("%s %s\n",bb_applet_name, VERSION); + exit(EXIT_SUCCESS); + case 'v': + verbose = 1; + break; +#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY + case 'I': + get_IDentity = 1; + break; + case 'i': + get_identity = 1; + break; +#endif + case 'g': + get_geom = 1; + break; + case 'f': + do_flush = 1; + break; + case 'q': + quiet = 1; + noisy = 0; + break; + case 'u': + get_unmask = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_unmask = set_flag(p,'1'))==1) + unmask = *p++ - '0'; + break; +#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA + case 'd': + get_dma = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_dma = set_flag(p,'9'))==1) + dma = *p++ - '0'; + break; +#endif /* CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA */ + case 'n': + get_nowerr = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_nowerr = set_flag(p,'1'))==1) + nowerr = *p++ - '0'; + break; + case 'p': + noisy_piomode = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_STRING(p,&set_piomode,&piomode); + break; + case 'r': + get_readonly = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_readonly = set_flag(p,'1'))==1) + readonly = *p++ - '0'; + break; + case 'm': + get_mult = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_NUMBER(p,&set_mult,&mult); + break; + case 'c': + get_io32bit = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_NUMBER(p,&set_io32bit,&io32bit); + break; +#ifdef HDIO_DRIVE_CMD + case 'S': + get_standby = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_NUMBER(p,&set_standby,&standby_requested); + missing_arg(set_standby, c, NULL); + break; + + case 'D': + get_defects = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_NUMBER(p,&set_defects,&defects); + missing_arg(set_defects, c, NULL); + break; + case 'P': + get_prefetch = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_NUMBER(p,&set_prefetch,&prefetch); + missing_arg(set_prefetch, c, NULL); + break; + + case 'X': + get_xfermode = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_STRING(p,&set_xfermode,&xfermode_requested); + missing_arg(set_xfermode, c, NULL); + break; + + case 'K': + get_dkeep = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_dkeep = set_flag(p,'1'))==1) + dkeep = *p++ - '0'; + else + goto missing_arg_error; + break; + + case 'A': + get_lookahead = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_lookahead = set_flag(p,'1'))==1) + lookahead = *p++ - '0'; + else + goto missing_arg_error; + break; + + case 'L': + get_doorlock = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_doorlock = set_flag(p,'1'))==1) + doorlock = *p++ - '0'; + else + goto missing_arg_error; + break; + + case 'W': + get_wcache = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_wcache = set_flag(p,'1'))==1) + wcache = *p++ - '0'; + else +missing_arg_error: + missing_arg(1, c, "(0/1)"); + break; + + case 'C': + get_powermode = noisy; + noisy = 1; + break; + + case 'y': + get_standbynow = noisy; + noisy = 1; + set_standbynow = 1; + break; + + case 'Y': + get_sleepnow = noisy; + noisy = 1; + set_sleepnow = 1; + break; + + case 'z': + reread_partn = 1; + break; + + case 'Z': + get_seagate = noisy; + noisy = 1; + set_seagate = 1; + break; +#endif /* HDIO_DRIVE_CMD */ + case 'k': + get_keep = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_keep = set_flag(p,'1'))==1) + keep = *p++ - '0'; + break; +#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF + case 'U': + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if(! p) + goto expected_hwif_error; /* "expected hwif_nr" */ + + sscanf(p++, "%i", &hwif); + + unregister_hwif = 1; + break; +#endif /* CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF */ +#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF + case 'R': + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if(! p) + goto expected_hwif_error; /* "expected hwif_data" */ + + sscanf(p++, "%i", &hwif_data); + + if (argc && isdigit(**argv)) + p = *argv++, --argc; + else + goto expected_hwif_error; /* "expected hwif_ctrl" */ + + sscanf(p, "%i", &hwif_ctrl); + + if (argc && isdigit(**argv)) + p = *argv++, --argc; + else +expected_hwif_error: + bb_error_msg_and_die("expected hwif value"); /* "expected hwif_irq" */ + + sscanf(p, "%i", &hwif_irq); + + *p = '\0'; + + scan_hwif = 1; + break; +#endif /* CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF */ + case 'Q': +#ifdef HDIO_GET_QDMA + get_dma_q = noisy; + noisy = 1; +#ifdef HDIO_SET_QDMA + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_NUMBER(p,&set_dma_q,&dma_q); +#ifdef HDIO_GET_QDMA + dma_q = -dma_q; +#endif +#endif +#endif + break; + +#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET + case 'w': + perform_reset = 1; + break; +#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */ +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF + case 'x': + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((perform_tristate = set_flag(p,'1'))==1) + tristate = *p++ - '0'; + else + missing_arg(1, c, "(0/1)"); + break; + +#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */ + case 'a': + get_readahead = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_NUMBER(p,&set_readahead,&Xreadahead); + break; + case 'B': + get_apmmode = noisy; + noisy = 1; + if (!*p && argc && isalnum(**argv)) + p = *argv++, --argc; + p=GET_NUMBER(p,&set_apmmode,&apmmode); + missing_arg(set_apmmode, c, "(1-255)"); + break; + case 't': + do_timings = 1; + do_flush = 1; + break; + case 'T': + do_ctimings = 1; + do_flush = 1; + break; +#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF + case 'b': + get_busstate = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if((set_busstate = set_flag(p,'2'))==1) + busstate = *p++ - '0'; + break; +#endif + case 'h': + default: + bb_show_usage(); + } + } + if (!argc) + bb_show_usage(); + } else { + process_dev (p); + } + } + return 0 ; +} diff --git a/busybox/miscutils/last.c b/busybox/miscutils/last.c new file mode 100644 index 000000000..86613bf27 --- /dev/null +++ b/busybox/miscutils/last.c @@ -0,0 +1,107 @@ +/* vi: set sw=4 ts=4: */ +/* + * last implementation for busybox + * + * Copyright (C) 2003-2004 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 "busybox.h" + +#ifndef SHUTDOWN_TIME +# define SHUTDOWN_TIME 254 +#endif + +/* Grr... utmp char[] members do not have to be nul-terminated. + * Do what we can while still keeping this reasonably small. + * Note: We are assuming the ut_id[] size is fixed at 4. */ + +#if (UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256) +#error struct utmp member char[] size(s) have changed! +#endif + +extern int last_main(int argc, char **argv) +{ + struct utmp ut; + int n, file = STDIN_FILENO; + + if (argc > 1) { + bb_show_usage(); + } + file = bb_xopen(_PATH_WTMP, O_RDONLY); + + printf("%-10s %-14s %-18s %-12.12s %s\n", "USER", "TTY", "HOST", "LOGIN", "TIME"); + while ((n = safe_read(file, (void*)&ut, sizeof(struct utmp))) != 0) { + + if (n != sizeof(struct utmp)) { + bb_perror_msg_and_die("short read"); + } + + if (strncmp(ut.ut_line, "~", 1) == 0) { + if (strncmp(ut.ut_user, "shutdown", 8) == 0) + ut.ut_type = SHUTDOWN_TIME; + else if (strncmp(ut.ut_user, "reboot", 6) == 0) + ut.ut_type = BOOT_TIME; + else if (strncmp(ut.ut_user, "runlevel", 7) == 0) + ut.ut_type = RUN_LVL; + } else { + if (!ut.ut_name[0] || strcmp(ut.ut_name, "LOGIN") == 0 || + ut.ut_name[0] == 0) + { + /* Don't bother. This means we can't find how long + * someone was logged in for. Oh well. */ + continue; + } + if (ut.ut_type != DEAD_PROCESS && + ut.ut_name[0] && ut.ut_line[0]) + { + ut.ut_type = USER_PROCESS; + } + if (strcmp(ut.ut_name, "date") == 0) { + if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME; + if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME; + } + } + + if (ut.ut_type!=USER_PROCESS) { + switch (ut.ut_type) { + case OLD_TIME: + case NEW_TIME: + case RUN_LVL: + case SHUTDOWN_TIME: + continue; + case BOOT_TIME: + strcpy(ut.ut_line, "system boot"); + break; + } + } + + printf("%-10s %-14s %-18s %-12.12s\n", ut.ut_user, ut.ut_line, ut.ut_host, + ctime(&(ut.ut_tv.tv_sec)) + 4); + } + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/miscutils/makedevs.c b/busybox/miscutils/makedevs.c new file mode 100644 index 000000000..54a2e000a --- /dev/null +++ b/busybox/miscutils/makedevs.c @@ -0,0 +1,93 @@ +/* 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 /* major() and minor() */ +#include "busybox.h" + +int makedevs_main(int argc, char **argv) +{ + mode_t mode; + char *basedev, *type, *nodname, buf[255]; + int Smajor, Sminor, S, E; + + if (argc < 7 || *argv[1]=='-') + bb_show_usage(); + + basedev = argv[1]; + type = argv[2]; + Smajor = atoi(argv[3]); + Sminor = atoi(argv[4]); + S = atoi(argv[5]); + E = atoi(argv[6]); + nodname = argc == 8 ? basedev : buf; + + mode = 0660; + + switch (type[0]) { + case 'c': + mode |= S_IFCHR; + break; + case 'b': + mode |= S_IFBLK; + break; + case 'f': + mode |= S_IFIFO; + break; + default: + bb_show_usage(); + } + + while (S <= E) { + int sz; + + sz = snprintf(buf, sizeof(buf), "%s%d", basedev, S); + if(sz<0 || sz>=sizeof(buf)) /* libc different */ + bb_error_msg_and_die("%s too large", basedev); + + /* if mode != S_IFCHR and != S_IFBLK third param in mknod() ignored */ + + if (mknod(nodname, mode, makedev(Smajor, Sminor))) + bb_error_msg("Failed to create: %s", nodname); + + if (nodname == basedev) /* ex. /dev/hda - to /dev/hda1 ... */ + nodname = buf; + S++; + Sminor++; + } + + return 0; +} + +/* +And this is what this program replaces. The shell is too slow! + +makedev () { +local basedev=$1; local S=$2; local E=$3 +local major=$4; local Sminor=$5; local type=$6 +local sbase=$7 + + if [ ! "$sbase" = "" ]; then + mknod "$basedev" $type $major $Sminor + S=`expr $S + 1` + Sminor=`expr $Sminor + 1` + fi + + while [ $S -le $E ]; do + mknod "$basedev$S" $type $major $Sminor + S=`expr $S + 1` + Sminor=`expr $Sminor + 1` + done +} +*/ diff --git a/busybox/miscutils/mt.c b/busybox/miscutils/mt.c new file mode 100644 index 000000000..b0cdaccb9 --- /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) { + bb_show_usage(); + } + + if (strcmp(argv[1], "-f") == 0) { + if (argc < 4) { + bb_show_usage(); + } + file = argv[2]; + argv += 2; + argc -= 2; + } + + while (code->name != 0) { + if (strcmp(code->name, argv[1]) == 0) + break; + code++; + } + + if (code->name == 0) { + bb_error_msg("unrecognized opcode %s.", argv[1]); + return EXIT_FAILURE; + } + + op.mt_op = code->value; + if (argc >= 3) + op.mt_count = atoi(argv[2]); + else + op.mt_count = 1; /* One, not zero, right? */ + + switch (code->value) { + case MTWEOF: + case MTERASE: + case MTWSM: + case MTSETDRVBUFFER: + mode = O_WRONLY; + break; + + default: + mode = O_RDONLY; + break; + } + + if ((fd = open(file, mode, 0)) < 0) + bb_perror_msg_and_die("%s", file); + + switch (code->value) { + case MTTELL: + if (ioctl(fd, MTIOCPOS, &position) < 0) + bb_perror_msg_and_die("%s", file); + printf ("At block %d.\n", (int) position.mt_blkno); + break; + + default: + if (ioctl(fd, MTIOCTOP, &op) != 0) + bb_perror_msg_and_die("%s", file); + break; + } + + return EXIT_SUCCESS; +} diff --git a/busybox/miscutils/rx.c b/busybox/miscutils/rx.c new file mode 100644 index 000000000..8edc8877a --- /dev/null +++ b/busybox/miscutils/rx.c @@ -0,0 +1,344 @@ +/*------------------------------------------------------------------------- + * Filename: xmodem.c + * Version: $Id: rx.c,v 1.2 2004/03/15 08:28:46 andersen Exp $ + * Copyright: Copyright (C) 2001, Hewlett-Packard Company + * Author: Christopher Hoover + * Description: xmodem functionality for uploading of kernels + * and the like + * Created at: Thu Dec 20 01:58:08 PST 2001 + *-----------------------------------------------------------------------*/ +/* + * xmodem.c: xmodem functionality for uploading of kernels and + * the like + * + * Copyright (C) 2001 Hewlett-Packard Laboratories + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This was originally written for blob and then adapted for busybox. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +#define SOH 0x01 +#define STX 0x02 +#define EOT 0x04 +#define ACK 0x06 +#define NAK 0x15 +#define CAN 0x18 +#define BS 0x08 + +/* + +Cf: + + http://www.textfiles.com/apple/xmodem + http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt + http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt + http://www.phys.washington.edu/~belonis/xmodem/modmprot.col + +*/ + +#define TIMEOUT 1 +#define TIMEOUT_LONG 10 +#define MAXERRORS 10 + +static inline void write_byte(int fd, char cc) { + write(fd, &cc, 1); +} + +static inline void write_flush(int fd) { + tcdrain(fd); +} + +static inline void read_flush(int fd) { + tcflush(fd, TCIFLUSH); +} + +static int read_byte(int fd, unsigned int timeout) { + char buf[1]; + int n; + + alarm(timeout); + + n = read(fd, &buf, 1); + + alarm(0); + + if (n == 1) + return buf[0] & 0xff; + else + return -1; +} + +static int receive(char *error_buf, size_t error_buf_size, + int ttyfd, int filefd) +{ + char blockBuf[1024]; + unsigned int errors = 0; + unsigned int wantBlockNo = 1; + unsigned int length = 0; + int docrc = 1; + char nak = 'C'; + unsigned int timeout = TIMEOUT_LONG; + +#define note_error(fmt,args...) \ + snprintf(error_buf, error_buf_size, fmt,##args) + + read_flush(ttyfd); + + /* Ask for CRC; if we get errors, we will go with checksum */ + write_byte(ttyfd, nak); + write_flush(ttyfd); + + for (;;) { + int blockBegin; + int blockNo, blockNoOnesCompl; + int blockLength; + int cksum = 0; + int crcHi = 0; + int crcLo = 0; + + blockBegin = read_byte(ttyfd, timeout); + if (blockBegin < 0) + goto timeout; + + timeout = TIMEOUT; + nak = NAK; + + switch (blockBegin) { + case SOH: + case STX: + break; + + case EOT: + write_byte(ttyfd, ACK); + write_flush(ttyfd); + goto done; + + default: + goto error; + } + + /* block no */ + blockNo = read_byte(ttyfd, TIMEOUT); + if (blockNo < 0) + goto timeout; + + /* block no one's compliment */ + blockNoOnesCompl = read_byte(ttyfd, TIMEOUT); + if (blockNoOnesCompl < 0) + goto timeout; + + if (blockNo != (255 - blockNoOnesCompl)) { + note_error("bad block ones compl"); + goto error; + } + + blockLength = (blockBegin == SOH) ? 128 : 1024; + + { + int i; + + for (i = 0; i < blockLength; i++) { + int cc = read_byte(ttyfd, TIMEOUT); + if (cc < 0) + goto timeout; + blockBuf[i] = cc; + } + } + + if (docrc) { + crcHi = read_byte(ttyfd, TIMEOUT); + if (crcHi < 0) + goto timeout; + + crcLo = read_byte(ttyfd, TIMEOUT); + if (crcLo < 0) + goto timeout; + } else { + cksum = read_byte(ttyfd, TIMEOUT); + if (cksum < 0) + goto timeout; + } + + if (blockNo == ((wantBlockNo - 1) & 0xff)) { + /* a repeat of the last block is ok, just ignore it. */ + /* this also ignores the initial block 0 which is */ + /* meta data. */ + goto next; + } else if (blockNo != (wantBlockNo & 0xff)) { + note_error("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo); + goto error; + } + + if (docrc) { + int crc = 0; + int i, j; + int expectedCrcHi; + int expectedCrcLo; + + for (i = 0; i < blockLength; i++) { + crc = crc ^ (int) blockBuf[i] << 8; + for (j = 0; j < 8; j++) + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + + expectedCrcHi = (crc >> 8) & 0xff; + expectedCrcLo = crc & 0xff; + + if ((crcHi != expectedCrcHi) || + (crcLo != expectedCrcLo)) { + note_error("crc error, expected 0x%02x 0x%02x, got 0x%02x 0x%02x", expectedCrcHi, expectedCrcLo, crcHi, crcLo); + goto error; + } + } else { + unsigned char expectedCksum = 0; + int i; + + for (i = 0; i < blockLength; i++) + expectedCksum += blockBuf[i]; + + if (cksum != expectedCksum) { + note_error("checksum error, expected 0x%02x, got 0x%02x", expectedCksum, cksum); + goto error; + } + } + + wantBlockNo++; + length += blockLength; + + if (bb_full_write(filefd, blockBuf, blockLength) < 0) { + note_error("write to file failed: %m"); + goto fatal; + } + + next: + errors = 0; + write_byte(ttyfd, ACK); + write_flush(ttyfd); + continue; + + error: + timeout: + errors++; + if (errors == MAXERRORS) { + /* Abort */ + int i; + + // if using crc, try again w/o crc + if (nak == 'C') { + nak = NAK; + errors = 0; + docrc = 0; + goto timeout; + } + + note_error("too many errors; giving up"); + + fatal: + for (i = 0; i < 5; i ++) + write_byte(ttyfd, CAN); + for (i = 0; i < 5; i ++) + write_byte(ttyfd, BS); + write_flush(ttyfd); + return -1; + } + + read_flush(ttyfd); + write_byte(ttyfd, nak); + write_flush(ttyfd); + } + + done: + return length; + +#undef note_error +} + +static void sigalrm_handler(int signum) +{ +} + +int rx_main(int argc, char **argv) +{ + char *fn; + int ttyfd, filefd; + struct termios tty, orig_tty; + struct sigaction act; + int n; + char error_buf[256]; + + if (argc != 2) + bb_show_usage(); + + fn = argv[1]; + ttyfd = open("/dev/tty", O_RDWR); + if (ttyfd < 0) + bb_error_msg_and_die("%s: open on /dev/tty failed: %m\n", argv[0]); + + filefd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0666); + if (filefd < 0) + bb_error_msg_and_die("%s: open on %s failed: %m\n", argv[0], fn); + + if (tcgetattr(ttyfd, &tty) < 0) + bb_error_msg_and_die("%s: tcgetattr failed: %m\n", argv[0]); + + orig_tty = tty; + + cfmakeraw(&tty); + tcsetattr(ttyfd, TCSAFLUSH, &tty); + + memset(&act, 0, sizeof(act)); + act.sa_handler = sigalrm_handler; + sigaction(SIGALRM, &act, 0); + + n = receive(error_buf, sizeof(error_buf), ttyfd, filefd); + + close(filefd); + + tcsetattr(ttyfd, TCSAFLUSH, &orig_tty); + + if (n < 0) + bb_error_msg_and_die("\n%s: receive failed:\n %s\n", + argv[0], error_buf); + + bb_fflush_stdout_and_exit(EXIT_SUCCESS); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/miscutils/strings.c b/busybox/miscutils/strings.c new file mode 100644 index 000000000..92e9f0d11 --- /dev/null +++ b/busybox/miscutils/strings.c @@ -0,0 +1,156 @@ +/* vi: set sw=4 ts=4: */ +/* + * strings implementation for busybox + * + * Copyright (c) 1980, 1987 + * The Regents of the University of California. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Original copyright notice is retained at the end of this file. + * + * Modified for BusyBox by Erik Andersen + * Badly hacked by Tito Ragusa + */ + +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define ISSTR(ch) (isprint(ch) || ch == '\t') + +int strings_main(int argc, char **argv) +{ + int n=4, c, i, opt=0, status=EXIT_SUCCESS; + long t=0, count; + FILE *file = stdin; + char *string=NULL; + const char *fmt="%s: "; + + while ((i = getopt(argc, argv, "afon:")) > 0) + switch(i) + { + case 'a': + break; + case 'f': + opt+=1; + break; + case 'o': + opt+=2; + break; + case 'n': + n = bb_xgetlarg(optarg, 10, 1, INT_MAX); + break; + default: + bb_show_usage(); + } + + argc -= optind; + argv += optind; + + i=0; + + string=xmalloc(n+1); + string[n]='\0'; + n-=1; + + if(argc==0) + { + fmt="{%s}: "; + *argv=(char *)bb_msg_standard_input; + goto pipe; + } + + for( ;*argv!=NULL && argc>0;argv++) + { + if((file=bb_wfopen(*argv,"r"))) + { +pipe: + + count=0; + do{ + c=fgetc(file); + if(ISSTR(c)) + { + if(i==0) + t=count; + if(i<=n) + string[i]=c; + if(i==n) + { + if(opt == 1 || opt == 3 ) + printf(fmt,*argv); + if(opt >= 2 ) + printf("%7lo ", t); + printf("%s", string); + } + if(i>n) + putchar(c); + i++; + } + else + { + if(i>n) + putchar('\n'); + i=0; + } + count++; + }while(c!=EOF); + + bb_fclose_nonstdin(file); + } + else + status=EXIT_FAILURE; + } + /*free(string);*/ + exit(status); +} + +/* + * Copyright (c) 1980, 1987 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/busybox/miscutils/time.c b/busybox/miscutils/time.c new file mode 100644 index 000000000..ca896a1c5 --- /dev/null +++ b/busybox/miscutils/time.c @@ -0,0 +1,502 @@ +/* `time' utility to display resource usage of processes. + Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* Originally written by David Keppel . + Heavily modified by David MacKenzie . + Heavily modified for busybox by Erik Andersen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For pid_t. */ +#include +#include /* For getpagesize, maybe. */ + +#define TV_MSEC tv_usec / 1000 +#include +#include "busybox.h" + +/* Information on the resources used by a child process. */ +typedef struct +{ + int waitstatus; + struct rusage ru; + struct timeval start, elapsed; /* Wallclock time of process. */ +} resource_t; + +/* msec = milliseconds = 1/1,000 (1*10e-3) second. + usec = microseconds = 1/1,000,000 (1*10e-6) second. */ + +#ifndef TICKS_PER_SEC +#define TICKS_PER_SEC 100 +#endif + +/* The number of milliseconds in one `tick' used by the `rusage' structure. */ +#define MSEC_PER_TICK (1000 / TICKS_PER_SEC) + +/* Return the number of clock ticks that occur in M milliseconds. */ +#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK) + +#define UL unsigned long + +static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T"; + +/* The output format for the -p option .*/ +static const char *const posix_format = "real %e\nuser %U\nsys %S"; + + +/* Format string for printing all statistics verbosely. + Keep this output to 24 lines so users on terminals can see it all.*/ +static const char *const long_format = + "\tCommand being timed: \"%C\"\n" + "\tUser time (seconds): %U\n" + "\tSystem time (seconds): %S\n" + "\tPercent of CPU this job got: %P\n" + "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n" + "\tAverage shared text size (kbytes): %X\n" + "\tAverage unshared data size (kbytes): %D\n" + "\tAverage stack size (kbytes): %p\n" + "\tAverage total size (kbytes): %K\n" + "\tMaximum resident set size (kbytes): %M\n" + "\tAverage resident set size (kbytes): %t\n" + "\tMajor (requiring I/O) page faults: %F\n" + "\tMinor (reclaiming a frame) page faults: %R\n" + "\tVoluntary context switches: %w\n" + "\tInvoluntary context switches: %c\n" + "\tSwaps: %W\n" + "\tFile system inputs: %I\n" + "\tFile system outputs: %O\n" + "\tSocket messages sent: %s\n" + "\tSocket messages received: %r\n" + "\tSignals delivered: %k\n" + "\tPage size (bytes): %Z\n" + "\tExit status: %x"; + + + /* Wait for and fill in data on child process PID. + Return 0 on error, 1 if ok. */ + +/* pid_t is short on BSDI, so don't try to promote it. */ +static int resuse_end (pid_t pid, resource_t *resp) +{ + int status; + + pid_t caught; + + /* Ignore signals, but don't ignore the children. When wait3 + returns the child process, set the time the command finished. */ + while ((caught = wait3 (&status, 0, &resp->ru)) != pid) + { + if (caught == -1) + return 0; + } + + gettimeofday (&resp->elapsed, (struct timezone *) 0); + resp->elapsed.tv_sec -= resp->start.tv_sec; + if (resp->elapsed.tv_usec < resp->start.tv_usec) + { + /* Manually carry a one from the seconds field. */ + resp->elapsed.tv_usec += 1000000; + --resp->elapsed.tv_sec; + } + resp->elapsed.tv_usec -= resp->start.tv_usec; + + resp->waitstatus = status; + + return 1; +} + +/* Print ARGV to FP, with each entry in ARGV separated by FILLER. */ +static void fprintargv (FILE *fp, char *const *argv, const char *filler) +{ + char *const *av; + + av = argv; + fputs (*av, fp); + while (*++av) + { + fputs (filler, fp); + fputs (*av, fp); + } + if (ferror (fp)) + bb_error_msg_and_die("write error"); +} + +/* Return the number of kilobytes corresponding to a number of pages PAGES. + (Actually, we use it to convert pages*ticks into kilobytes*ticks.) + + Try to do arithmetic so that the risk of overflow errors is minimized. + This is funky since the pagesize could be less than 1K. + Note: Some machines express getrusage statistics in terms of K, + others in terms of pages. */ + +static unsigned long ptok (unsigned long pages) +{ + static unsigned long ps = 0; + unsigned long tmp; + static long size = LONG_MAX; + + /* Initialization. */ + if (ps == 0) + ps = (long) getpagesize (); + + /* Conversion. */ + if (pages > (LONG_MAX / ps)) + { /* Could overflow. */ + tmp = pages / 1024; /* Smaller first, */ + size = tmp * ps; /* then larger. */ + } + else + { /* Could underflow. */ + tmp = pages * ps; /* Larger first, */ + size = tmp / 1024; /* then smaller. */ + } + return size; +} + +/* summarize: Report on the system use of a command. + + Copy the FMT argument to FP except that `%' sequences + have special meaning, and `\n' and `\t' are translated into + newline and tab, respectively, and `\\' is translated into `\'. + + The character following a `%' can be: + (* means the tcsh time builtin also recognizes it) + % == a literal `%' + C == command name and arguments +* D == average unshared data size in K (ru_idrss+ru_isrss) +* E == elapsed real (wall clock) time in [hour:]min:sec +* F == major page faults (required physical I/O) (ru_majflt) +* I == file system inputs (ru_inblock) +* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss) +* M == maximum resident set size in K (ru_maxrss) +* O == file system outputs (ru_oublock) +* P == percent of CPU this job got (total cpu time / elapsed time) +* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt) +* S == system (kernel) time (seconds) (ru_stime) +* T == system time in [hour:]min:sec +* U == user time (seconds) (ru_utime) +* u == user time in [hour:]min:sec +* W == times swapped out (ru_nswap) +* X == average amount of shared text in K (ru_ixrss) + Z == page size +* c == involuntary context switches (ru_nivcsw) + e == elapsed real time in seconds +* k == signals delivered (ru_nsignals) + p == average unshared stack size in K (ru_isrss) +* r == socket messages received (ru_msgrcv) +* s == socket messages sent (ru_msgsnd) + t == average resident set size in K (ru_idrss) +* w == voluntary context switches (ru_nvcsw) + x == exit status of command + + Various memory usages are found by converting from page-seconds + to kbytes by multiplying by the page size, dividing by 1024, + and dividing by elapsed real time. + + FP is the stream to print to. + FMT is the format string, interpreted as described above. + COMMAND is the command and args that are being summarized. + RESP is resource information on the command. */ + +static void summarize (FILE *fp, const char *fmt, char **command, resource_t *resp) +{ + unsigned long r; /* Elapsed real milliseconds. */ + unsigned long v; /* Elapsed virtual (CPU) milliseconds. */ + + if (WIFSTOPPED (resp->waitstatus)) + fprintf (fp, "Command stopped by signal %d\n", WSTOPSIG (resp->waitstatus)); + else if (WIFSIGNALED (resp->waitstatus)) + fprintf (fp, "Command terminated by signal %d\n", WTERMSIG (resp->waitstatus)); + else if (WIFEXITED (resp->waitstatus) && WEXITSTATUS (resp->waitstatus)) + fprintf (fp, "Command exited with non-zero status %d\n", WEXITSTATUS (resp->waitstatus)); + + /* Convert all times to milliseconds. Occasionally, one of these values + comes out as zero. Dividing by zero causes problems, so we first + check the time value. If it is zero, then we take `evasive action' + instead of calculating a value. */ + + r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000; + + v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC + + resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC; + + while (*fmt) + { + switch (*fmt) + { + case '%': + switch (*++fmt) + { + case '%': /* Literal '%'. */ + putc ('%', fp); + break; + case 'C': /* The command that got timed. */ + fprintargv (fp, command, " "); + break; + case 'D': /* Average unshared data size. */ + fprintf (fp, "%lu", + MSEC_TO_TICKS (v) == 0 ? 0 : + ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) + + ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v)); + break; + case 'E': /* Elapsed real (wall clock) time. */ + if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */ + fprintf (fp, "%ldh %ldm %02lds", + resp->elapsed.tv_sec / 3600, + (resp->elapsed.tv_sec % 3600) / 60, + resp->elapsed.tv_sec % 60); + else + fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */ + resp->elapsed.tv_sec / 60, + resp->elapsed.tv_sec % 60, + resp->elapsed.tv_usec / 10000); + break; + case 'F': /* Major page faults. */ + fprintf (fp, "%ld", resp->ru.ru_majflt); + break; + case 'I': /* Inputs. */ + fprintf (fp, "%ld", resp->ru.ru_inblock); + break; + case 'K': /* Average mem usage == data+stack+text. */ + fprintf (fp, "%lu", + MSEC_TO_TICKS (v) == 0 ? 0 : + ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) + + ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v) + + ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v)); + break; + case 'M': /* Maximum resident set size. */ + fprintf (fp, "%lu", ptok ((UL) resp->ru.ru_maxrss)); + break; + case 'O': /* Outputs. */ + fprintf (fp, "%ld", resp->ru.ru_oublock); + break; + case 'P': /* Percent of CPU this job got. */ + /* % cpu is (total cpu time)/(elapsed time). */ + if (r > 0) + fprintf (fp, "%lu%%", (v * 100 / r)); + else + fprintf (fp, "?%%"); + break; + case 'R': /* Minor page faults (reclaims). */ + fprintf (fp, "%ld", resp->ru.ru_minflt); + break; + case 'S': /* System time. */ + fprintf (fp, "%ld.%02ld", + resp->ru.ru_stime.tv_sec, + resp->ru.ru_stime.TV_MSEC / 10); + break; + case 'T': /* System time. */ + if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */ + fprintf (fp, "%ldh %ldm %02lds", + resp->ru.ru_stime.tv_sec / 3600, + (resp->ru.ru_stime.tv_sec % 3600) / 60, + resp->ru.ru_stime.tv_sec % 60); + else + fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */ + resp->ru.ru_stime.tv_sec / 60, + resp->ru.ru_stime.tv_sec % 60, + resp->ru.ru_stime.tv_usec / 10000); + break; + case 'U': /* User time. */ + fprintf (fp, "%ld.%02ld", + resp->ru.ru_utime.tv_sec, + resp->ru.ru_utime.TV_MSEC / 10); + break; + case 'u': /* User time. */ + if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */ + fprintf (fp, "%ldh %ldm %02lds", + resp->ru.ru_utime.tv_sec / 3600, + (resp->ru.ru_utime.tv_sec % 3600) / 60, + resp->ru.ru_utime.tv_sec % 60); + else + fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */ + resp->ru.ru_utime.tv_sec / 60, + resp->ru.ru_utime.tv_sec % 60, + resp->ru.ru_utime.tv_usec / 10000); + break; + case 'W': /* Times swapped out. */ + fprintf (fp, "%ld", resp->ru.ru_nswap); + break; + case 'X': /* Average shared text size. */ + fprintf (fp, "%lu", + MSEC_TO_TICKS (v) == 0 ? 0 : + ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v)); + break; + case 'Z': /* Page size. */ + fprintf (fp, "%d", getpagesize ()); + break; + case 'c': /* Involuntary context switches. */ + fprintf (fp, "%ld", resp->ru.ru_nivcsw); + break; + case 'e': /* Elapsed real time in seconds. */ + fprintf (fp, "%ld.%02ld", + resp->elapsed.tv_sec, + resp->elapsed.tv_usec / 10000); + break; + case 'k': /* Signals delivered. */ + fprintf (fp, "%ld", resp->ru.ru_nsignals); + break; + case 'p': /* Average stack segment. */ + fprintf (fp, "%lu", + MSEC_TO_TICKS (v) == 0 ? 0 : + ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v)); + break; + case 'r': /* Incoming socket messages received. */ + fprintf (fp, "%ld", resp->ru.ru_msgrcv); + break; + case 's': /* Outgoing socket messages sent. */ + fprintf (fp, "%ld", resp->ru.ru_msgsnd); + break; + case 't': /* Average resident set size. */ + fprintf (fp, "%lu", + MSEC_TO_TICKS (v) == 0 ? 0 : + ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v)); + break; + case 'w': /* Voluntary context switches. */ + fprintf (fp, "%ld", resp->ru.ru_nvcsw); + break; + case 'x': /* Exit status. */ + fprintf (fp, "%d", WEXITSTATUS (resp->waitstatus)); + break; + case '\0': + putc ('?', fp); + return; + default: + putc ('?', fp); + putc (*fmt, fp); + } + ++fmt; + break; + + case '\\': /* Format escape. */ + switch (*++fmt) + { + case 't': + putc ('\t', fp); + break; + case 'n': + putc ('\n', fp); + break; + case '\\': + putc ('\\', fp); + break; + default: + putc ('?', fp); + putc ('\\', fp); + putc (*fmt, fp); + } + ++fmt; + break; + + default: + putc (*fmt++, fp); + } + + if (ferror (fp)) + bb_error_msg_and_die("write error"); + } + putc ('\n', fp); + + if (ferror (fp)) + bb_error_msg_and_die("write error"); +} + +/* Run command CMD and return statistics on it. + Put the statistics in *RESP. */ +static void run_command (char *const *cmd, resource_t *resp) +{ + pid_t pid; /* Pid of child. */ + __sighandler_t interrupt_signal, quit_signal; + + gettimeofday (&resp->start, (struct timezone *) 0); + pid = fork (); /* Run CMD as child process. */ + if (pid < 0) + bb_error_msg_and_die("cannot fork"); + else if (pid == 0) + { /* If child. */ + /* Don't cast execvp arguments; that causes errors on some systems, + versus merely warnings if the cast is left off. */ + execvp (cmd[0], cmd); + bb_error_msg("cannot run %s", cmd[0]); + _exit (errno == ENOENT ? 127 : 126); + } + + /* Have signals kill the child but not self (if possible). */ + interrupt_signal = signal (SIGINT, SIG_IGN); + quit_signal = signal (SIGQUIT, SIG_IGN); + + if (resuse_end (pid, resp) == 0) + bb_error_msg("error waiting for child process"); + + /* Re-enable signals. */ + signal (SIGINT, interrupt_signal); + signal (SIGQUIT, quit_signal); +} + +extern int time_main (int argc, char **argv) +{ + int gotone; + resource_t res; + const char *output_format = default_format; + + argc--; + argv++; + /* Parse any options -- don't use getopt() here so we don't + * consume the args of our client application... */ + while (argc > 0 && **argv == '-') { + gotone = 0; + while (gotone==0 && *++(*argv)) { + switch (**argv) { + case 'v': + output_format = long_format; + break; + case 'p': + output_format = posix_format; + break; + default: + bb_show_usage(); + } + argc--; + argv++; + gotone = 1; + } + } + + if (argv == NULL || *argv == NULL) + bb_show_usage(); + + run_command (argv, &res); + summarize (stderr, output_format, argv, &res); + fflush (stderr); + + if (WIFSTOPPED (res.waitstatus)) + exit (WSTOPSIG (res.waitstatus)); + else if (WIFSIGNALED (res.waitstatus)) + exit (WTERMSIG (res.waitstatus)); + else if (WIFEXITED (res.waitstatus)) + exit (WEXITSTATUS (res.waitstatus)); + return 0; +} diff --git a/busybox/miscutils/watchdog.c b/busybox/miscutils/watchdog.c new file mode 100644 index 000000000..276fadebd --- /dev/null +++ b/busybox/miscutils/watchdog.c @@ -0,0 +1,81 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini watchdog implementation for busybox + * + * Copyright (C) 2003 Paul Mundt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +/* Userspace timer duration, in seconds */ +static unsigned int timer_duration = 30; + +/* Watchdog file descriptor */ +static int fd; + +static void watchdog_shutdown(int unused) +{ + write(fd, "V", 1); /* Magic */ + close(fd); + exit(0); +} + +extern int watchdog_main(int argc, char **argv) +{ + int opt; + + while ((opt = getopt(argc, argv, "t:")) > 0) { + switch (opt) { + case 't': + timer_duration = bb_xgetlarg(optarg, 10, 0, INT_MAX); + break; + default: + bb_show_usage(); + } + } + + /* We're only interested in the watchdog device .. */ + if (optind < argc - 1 || argc == 1) + bb_show_usage(); + + if (daemon(0, 1) < 0) + bb_perror_msg_and_die("Failed forking watchdog daemon"); + + signal(SIGHUP, watchdog_shutdown); + signal(SIGINT, watchdog_shutdown); + + fd = bb_xopen(argv[argc - 1], O_WRONLY); + + while (1) { + /* + * Make sure we clear the counter before sleeping, as the counter value + * is undefined at this point -- PFM + */ + write(fd, "\0", 1); + sleep(timer_duration); + } + + watchdog_shutdown(0); + + return EXIT_SUCCESS; +} diff --git a/busybox/modutils/Config.in b/busybox/modutils/Config.in new file mode 100644 index 000000000..ada69d752 --- /dev/null +++ b/busybox/modutils/Config.in @@ -0,0 +1,113 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Linux Module Utilities" + +config CONFIG_INSMOD + bool "insmod" + default n + help + insmod is used to load specified modules in the running kernel. + +config CONFIG_FEATURE_2_4_MODULES + bool " Support version 2.2.x to 2.4.x Linux kernels" + default y + depends on CONFIG_INSMOD + help + Support module loading for 2.2.x and 2.4.x Linux kernels. + +config CONFIG_FEATURE_2_6_MODULES + bool " Support version 2.6.x Linux kernels" + default n + depends on CONFIG_INSMOD + help + Support module loading for newer 2.6.x Linux kernels. + +config CONFIG_FEATURE_INSMOD_VERSION_CHECKING + bool " Module version checking" + default n + depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES + help + Support checking of versions for modules. This is used to + ensure that the kernel and module are made for each other. + +config CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS + bool " Add module symbols to kernel symbol table" + default n + depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES + help + By adding module symbols to the kernel symbol table, Oops messages + occuring within kernel modules can be properly debugged. By enabling + this feature, module symbols will always be added to the kernel symbol + table for properly debugging support. If you are not interested in + Oops messages from kernel modules, say N. + +config CONFIG_FEATURE_INSMOD_LOADINKMEM + bool " In kernel memory optimization (uClinux only)" + default n + depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES + help + This is a special uClinux only memory optimization that lets insmod + load the specified kernel module directly into kernel space, reducing + memory usage by preventing the need for two copies of the module + being loaded into memory. + +config CONFIG_FEATURE_INSMOD_LOAD_MAP + bool " Enable load map (-m) option" + default n + depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES + help + Enabling this, one would be able to get a load map + output on stdout. This makes kernel module debugging + easier. + If you don't plan to debug kernel modules, you + don't need this option. + +config CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL + bool " Symbols in load map" + default y + depends on CONFIG_FEATURE_INSMOD_LOAD_MAP + help + Without this option, -m will only output section + load map. With this option, -m will also output + symbols load map. + +config CONFIG_LSMOD + bool "lsmod" + default n + help + lsmod is used to display a list of loaded modules. + +config CONFIG_FEATURE_QUERY_MODULE_INTERFACE + bool + default y + depends on CONFIG_FEATURE_2_4_MODULES && !CONFIG_FEATURE_2_6_MODULES + +config CONFIG_MODPROBE + bool "modprobe" + default n + help + Handle the loading of modules, and their dependancies on a high + level. + +config CONFIG_RMMOD + bool "rmmod" + default n + help + rmmod is used to unload specified modules from the kernel. + +config CONFIG_FEATURE_CHECK_TAINTED_MODULE + bool "Support tainted module checking with new kernels" + default y + depends on CONFIG_INSMOD || CONFIG_LSMOD + help + Support checking for tainted modules. These are usually binary + only modules that will make the linux-kernel list ignore your + support request. + This option is required to support GPLONLY modules. + + +endmenu + diff --git a/busybox/modutils/Makefile b/busybox/modutils/Makefile new file mode 100644 index 000000000..d2b50b4d8 --- /dev/null +++ b/busybox/modutils/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/modutils +MODUTILS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/modutils/Makefile.in b/busybox/modutils/Makefile.in new file mode 100644 index 000000000..9bd11d4d8 --- /dev/null +++ b/busybox/modutils/Makefile.in @@ -0,0 +1,39 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +MODUTILS_AR:=modutils.a +ifndef $(MODUTILS_DIR) +MODUTILS_DIR:=$(top_builddir)/modutils/ +endif +srcdir=$(top_srcdir)/modutils + +MODUTILS-y:= +MODUTILS-$(CONFIG_INSMOD) += insmod.o +MODUTILS-$(CONFIG_LSMOD) += lsmod.o +MODUTILS-$(CONFIG_MODPROBE) += modprobe.o +MODUTILS-$(CONFIG_RMMOD) += rmmod.o + +libraries-y+=$(MODUTILS_DIR)$(MODUTILS_AR) + +$(MODUTILS_DIR)$(MODUTILS_AR): $(patsubst %,$(MODUTILS_DIR)%, $(MODUTILS-y)) + $(AR) -ro $@ $(patsubst %,$(MODUTILS_DIR)%, $(MODUTILS-y)) + +$(MODUTILS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/modutils/insmod.c b/busybox/modutils/insmod.c new file mode 100644 index 000000000..d88dd1be6 --- /dev/null +++ b/busybox/modutils/insmod.c @@ -0,0 +1,4039 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini insmod implementation for busybox + * + * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64, + * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64. + * + * Copyright (C) 1999-2004 by Erik Andersen + * and Ron Alder + * + * Rodney Radford 17-Aug-2004. + * Added x86_64 support. + * + * Miles Bader added NEC V850E support. + * + * 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. + * + * Yoshinori Sato 19-May-2004. + * added Renesas H8/300 support. + * + * Paul Mundt 08-Aug-2003. + * Integrated support for sh64 (SH-5), from preliminary modutils + * patches from Benedict Gaster . + * Currently limited to support for 32bit ABI. + * + * Magnus Damm 22-May-2002. + * The plt and got code are now using the same structs. + * Added generic linked list code to fully support PowerPC. + * Replaced the mess in arch_apply_relocation() with architecture blocks. + * The arch_create_got() function got cleaned up with architecture blocks. + * These blocks should be easy maintain and sync with obj_xxx.c in modutils. + * + * Magnus Damm added PowerPC support 20-Feb-2001. + * PowerPC specific code stolen from modutils-2.3.16, + * written by Paul Mackerras, Copyright 1996, 1997 Linux International. + * I've only tested the code on mpc8xx platforms in big-endian mode. + * Did some cleanup and added CONFIG_USE_xxx_ENTRIES... + * + * Quinn Jensen 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 +#include "busybox.h" + +#if !defined(CONFIG_FEATURE_2_4_MODULES) && \ + !defined(CONFIG_FEATURE_2_6_MODULES) +#define CONFIG_FEATURE_2_4_MODULES +#endif + +#if !defined(CONFIG_FEATURE_2_4_MODULES) +#define insmod_ng_main insmod_main +#endif + +#if defined(CONFIG_FEATURE_2_6_MODULES) +extern int insmod_ng_main( int argc, char **argv); +#endif + + +#if defined(CONFIG_FEATURE_2_4_MODULES) + + +#ifdef CONFIG_FEATURE_INSMOD_LOADINKMEM +#define LOADBITS 0 +#else +#define LOADBITS 1 +#endif + + +/* ARM support */ +#if defined(__arm__) +#define MATCH_MACHINE(x) (x == EM_ARM) +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_PLT_ENTRIES +#define CONFIG_PLT_ENTRY_SIZE 8 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 8 +#define CONFIG_USE_SINGLE +#endif + +/* CRIS */ +#if defined(__cris__) +#define MATCH_MACHINE(x) (x == EM_CRIS) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#ifndef EM_CRIS +#define EM_CRIS 76 +#define R_CRIS_NONE 0 +#define R_CRIS_32 3 +#endif +#endif + +/* H8/300 */ +#if defined(__H8300H__) || defined(__H8300S__) +#define MATCH_MACHINE(x) (x == EM_H8_300) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_SINGLE +#define SYMBOL_PREFIX "_" +#endif + +/* x86 */ +#if defined(__i386__) +#ifndef EM_486 +#define MATCH_MACHINE(x) (x == EM_386) +#else +#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486) +#endif +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 4 +#define CONFIG_USE_SINGLE +#endif + +/* IA64, aka Itanium */ +#if defined(__ia64__) +#define MATCH_MACHINE(x) (x == EM_IA_64) +#define SHT_RELM SHT_RELA +#define Elf64_RelM Elf64_Rela +#define ELFCLASSM ELFCLASS64 +#endif + +/* m68k */ +#if defined(__mc68000__) +#define MATCH_MACHINE(x) (x == EM_68K) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 4 +#define CONFIG_USE_SINGLE +#endif + +/* MIPS */ +#if defined(__mips__) +#define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE) +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#define ELFCLASSM ELFCLASS32 +/* Account for ELF spec changes. */ +#ifndef EM_MIPS_RS3_LE +#ifdef EM_MIPS_RS4_BE +#define EM_MIPS_RS3_LE EM_MIPS_RS4_BE +#else +#define EM_MIPS_RS3_LE 10 +#endif +#endif /* !EM_MIPS_RS3_LE */ +#define ARCHDATAM "__dbe_table" +#endif + +/* PowerPC */ +#if defined(__powerpc__) +#define MATCH_MACHINE(x) (x == EM_PPC) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_PLT_ENTRIES +#define CONFIG_PLT_ENTRY_SIZE 16 +#define CONFIG_USE_PLT_LIST +#define CONFIG_LIST_ARCHTYPE ElfW(Addr) +#define CONFIG_USE_LIST +#define ARCHDATAM "__ftr_fixup" +#endif + +/* S390 */ +#if defined(__s390__) +#define MATCH_MACHINE(x) (x == EM_S390) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_PLT_ENTRIES +#define CONFIG_PLT_ENTRY_SIZE 8 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 8 +#define CONFIG_USE_SINGLE +#endif + +/* SuperH */ +#if defined(__sh__) +#define MATCH_MACHINE(x) (x == EM_SH) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 4 +#define CONFIG_USE_SINGLE +/* the SH changes have only been tested in =little endian= mode */ +/* I'm not sure about big endian, so let's warn: */ +#if defined(__sh__) && defined(__BIG_ENDIAN__) +#error insmod.c may require changes for use on big endian SH +#endif +/* it may or may not work on the SH1/SH2... Error on those also */ +#if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && (defined(__sh__)) +#error insmod.c may require changes for SH1 or SH2 use +#endif +#endif + +/* Sparc */ +#if defined(__sparc__) +#define MATCH_MACHINE(x) (x == EM_SPARC) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#endif + +/* v850e */ +#if defined (__v850e__) +#define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_PLT_ENTRIES +#define CONFIG_PLT_ENTRY_SIZE 8 +#define CONFIG_USE_SINGLE +#ifndef EM_CYGNUS_V850 /* grumble */ +#define EM_CYGNUS_V850 0x9080 +#endif +#define SYMBOL_PREFIX "_" +#endif + +/* X86_64 */ +#if defined(__x86_64__) +#define MATCH_MACHINE(x) (x == EM_X86_64) +#define SHT_RELM SHT_REL +#define Elf64_RelM Elf64_Rel +#define ELFCLASSM ELFCLASS64 +#endif + +#ifndef SHT_RELM +#error Sorry, but insmod.c does not yet support this architecture... +#endif + + +//---------------------------------------------------------------------------- +//--------modutils module.h, lines 45-242 +//---------------------------------------------------------------------------- + +/* Definitions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +#ifndef MODUTILS_MODULE_H +static const int MODUTILS_MODULE_H = 1; + +#ident "$Id: insmod.c,v 1.125 2004/09/02 23:03:25 andersen Exp $" + +/*======================================================================*/ +/* For sizeof() which are related to the module platform and not to the + environment isnmod is running in, use sizeof_xx instead of sizeof(xx). */ + +#define tgt_sizeof_char sizeof(char) +#define tgt_sizeof_short sizeof(short) +#define tgt_sizeof_int sizeof(int) +#define tgt_sizeof_long sizeof(long) +#define tgt_sizeof_char_p sizeof(char *) +#define tgt_sizeof_void_p sizeof(void *) +#define tgt_long long + +#if defined(__sparc__) && !defined(__sparc_v9__) && defined(ARCH_sparc64) +#undef tgt_sizeof_long +#undef tgt_sizeof_char_p +#undef tgt_sizeof_void_p +#undef tgt_long +static const int tgt_sizeof_long = 8; +static const int tgt_sizeof_char_p = 8; +static const int tgt_sizeof_void_p = 8; +#define tgt_long long long +#endif + +/*======================================================================*/ +/* The structures used in Linux 2.1. */ + +/* Note: new_module_symbol does not use tgt_long intentionally */ +struct new_module_symbol +{ + unsigned long value; + unsigned long name; +}; + +struct new_module_persist; + +struct new_module_ref +{ + unsigned tgt_long dep; /* kernel addresses */ + unsigned tgt_long ref; + unsigned tgt_long next_ref; +}; + +struct new_module +{ + unsigned tgt_long size_of_struct; /* == sizeof(module) */ + unsigned tgt_long next; + unsigned tgt_long name; + unsigned tgt_long size; + + tgt_long usecount; + unsigned tgt_long flags; /* AUTOCLEAN et al */ + + unsigned nsyms; + unsigned ndeps; + + unsigned tgt_long syms; + unsigned tgt_long deps; + unsigned tgt_long refs; + unsigned tgt_long init; + unsigned tgt_long cleanup; + unsigned tgt_long ex_table_start; + unsigned tgt_long ex_table_end; +#ifdef __alpha__ + unsigned tgt_long gp; +#endif + /* Everything after here is extension. */ + unsigned tgt_long persist_start; + unsigned tgt_long persist_end; + unsigned tgt_long can_unload; + unsigned tgt_long runsize; + const char *kallsyms_start; /* All symbols for kernel debugging */ + const char *kallsyms_end; + const char *archdata_start; /* arch specific data for module */ + const char *archdata_end; + const char *kernel_data; /* Reserved for kernel internal use */ +}; + +#ifdef ARCHDATAM +#define ARCHDATA_SEC_NAME ARCHDATAM +#else +#define ARCHDATA_SEC_NAME "__archdata" +#endif +#define KALLSYMS_SEC_NAME "__kallsyms" + + +struct new_module_info +{ + unsigned long addr; + unsigned long size; + unsigned long flags; + long usecount; +}; + +/* Bits of module.flags. */ +static const int NEW_MOD_RUNNING = 1; +static const int NEW_MOD_DELETED = 2; +static const int NEW_MOD_AUTOCLEAN = 4; +static const int NEW_MOD_VISITED = 8; +static const int NEW_MOD_USED_ONCE = 16; + +int init_module(const char *name, const struct new_module *); +int query_module(const char *name, int which, void *buf, + size_t bufsize, size_t *ret); + +/* Values for query_module's which. */ + +static const int QM_MODULES = 1; +static const int QM_DEPS = 2; +static const int QM_REFS = 3; +static const int QM_SYMBOLS = 4; +static const int QM_INFO = 5; + +/*======================================================================*/ +/* The system calls unchanged between 2.0 and 2.1. */ + +unsigned long create_module(const char *, size_t); +int delete_module(const char *); + + +#endif /* module.h */ + +//---------------------------------------------------------------------------- +//--------end of modutils module.h +//---------------------------------------------------------------------------- + + + +//---------------------------------------------------------------------------- +//--------modutils obj.h, lines 253-462 +//---------------------------------------------------------------------------- + +/* Elf object file loading and relocation routines. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +#ifndef MODUTILS_OBJ_H +static const int MODUTILS_OBJ_H = 1; + +#ident "$Id: insmod.c,v 1.125 2004/09/02 23:03:25 andersen Exp $" + +/* The relocatable object is manipulated using elfin types. */ + +#include +#include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ELFDATAM ELFDATA2LSB +#elif __BYTE_ORDER == __BIG_ENDIAN +#define ELFDATAM ELFDATA2MSB +#endif + +#ifndef ElfW +# if ELFCLASSM == ELFCLASS32 +# define ElfW(x) Elf32_ ## x +# define ELFW(x) ELF32_ ## x +# else +# define ElfW(x) Elf64_ ## x +# define ELFW(x) ELF64_ ## x +# endif +#endif + +/* For some reason this is missing from some ancient C libraries.... */ +#ifndef ELF32_ST_INFO +# define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) +#endif + +#ifndef ELF64_ST_INFO +# define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) +#endif + +struct obj_string_patch; +struct obj_symbol_patch; + +struct obj_section +{ + ElfW(Shdr) header; + const char *name; + char *contents; + struct obj_section *load_next; + int idx; +}; + +struct obj_symbol +{ + struct obj_symbol *next; /* hash table link */ + const char *name; + unsigned long value; + unsigned long size; + int secidx; /* the defining section index/module */ + int info; + int ksymidx; /* for export to the kernel symtab */ + int referenced; /* actually used in the link */ +}; + +/* Hardcode the hash table size. We shouldn't be needing so many + symbols that we begin to degrade performance, and we get a big win + by giving the compiler a constant divisor. */ + +#define HASH_BUCKETS 521 + +struct obj_file +{ + ElfW(Ehdr) header; + ElfW(Addr) baseaddr; + struct obj_section **sections; + struct obj_section *load_order; + struct obj_section **load_order_search_start; + struct obj_string_patch *string_patches; + struct obj_symbol_patch *symbol_patches; + int (*symbol_cmp)(const char *, const char *); + unsigned long (*symbol_hash)(const char *); + unsigned long local_symtab_size; + struct obj_symbol **local_symtab; + struct obj_symbol *symtab[HASH_BUCKETS]; +}; + +enum obj_reloc +{ + obj_reloc_ok, + obj_reloc_overflow, + obj_reloc_dangerous, + obj_reloc_unhandled +}; + +struct obj_string_patch +{ + struct obj_string_patch *next; + int reloc_secidx; + ElfW(Addr) reloc_offset; + ElfW(Addr) string_offset; +}; + +struct obj_symbol_patch +{ + struct obj_symbol_patch *next; + int reloc_secidx; + ElfW(Addr) reloc_offset; + struct obj_symbol *sym; +}; + + +/* Generic object manipulation routines. */ + +static unsigned long obj_elf_hash(const char *); + +static unsigned long obj_elf_hash_n(const char *, unsigned long len); + +static struct obj_symbol *obj_find_symbol (struct obj_file *f, + const char *name); + +static ElfW(Addr) obj_symbol_final_value(struct obj_file *f, + struct obj_symbol *sym); + +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING +static void obj_set_symbol_compare(struct obj_file *f, + int (*cmp)(const char *, const char *), + unsigned long (*hash)(const char *)); +#endif + +static struct obj_section *obj_find_section (struct obj_file *f, + const char *name); + +static void obj_insert_section_load_order (struct obj_file *f, + struct obj_section *sec); + +static struct obj_section *obj_create_alloced_section (struct obj_file *f, + const char *name, + unsigned long align, + unsigned long size); + +static struct obj_section *obj_create_alloced_section_first (struct obj_file *f, + const char *name, + unsigned long align, + unsigned long size); + +static void *obj_extend_section (struct obj_section *sec, unsigned long more); + +static int obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, + const char *string); + +static int obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, + struct obj_symbol *sym); + +static int obj_check_undefineds(struct obj_file *f); + +static void obj_allocate_commons(struct obj_file *f); + +static unsigned long obj_load_size (struct obj_file *f); + +static int obj_relocate (struct obj_file *f, ElfW(Addr) base); + +static struct obj_file *obj_load(FILE *f, int loadprogbits); + +static int obj_create_image (struct obj_file *f, char *image); + +/* Architecture specific manipulation routines. */ + +static struct obj_file *arch_new_file (void); + +static struct obj_section *arch_new_section (void); + +static struct obj_symbol *arch_new_symbol (void); + +static enum obj_reloc arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + ElfW(RelM) *rel, ElfW(Addr) value); + +static void arch_create_got (struct obj_file *f); + +static int obj_gpl_license(struct obj_file *f, const char **license); + +#endif /* obj.h */ +//---------------------------------------------------------------------------- +//--------end of modutils obj.h +//---------------------------------------------------------------------------- + + +/* SPFX is always a string, so it can be concatenated to string constants. */ +#ifdef SYMBOL_PREFIX +#define SPFX SYMBOL_PREFIX +#else +#define SPFX "" +#endif + + +#define _PATH_MODULES "/lib/modules" +static const int STRVERSIONLEN = 32; + +/*======================================================================*/ + +static int flag_force_load = 0; +static int flag_autoclean = 0; +static int flag_verbose = 0; +static int flag_quiet = 0; +static int flag_export = 1; + + +/*======================================================================*/ + +#if defined(CONFIG_USE_LIST) + +struct arch_list_entry +{ + struct arch_list_entry *next; + CONFIG_LIST_ARCHTYPE addend; + int offset; + int inited : 1; +}; + +#endif + +#if defined(CONFIG_USE_SINGLE) + +struct arch_single_entry +{ + int offset; + int inited : 1; + int allocated : 1; +}; + +#endif + +#if defined(__mips__) +struct mips_hi16 +{ + struct mips_hi16 *next; + Elf32_Addr *addr; + Elf32_Addr value; +}; +#endif + +struct arch_file { + struct obj_file root; +#if defined(CONFIG_USE_PLT_ENTRIES) + struct obj_section *plt; +#endif +#if defined(CONFIG_USE_GOT_ENTRIES) + struct obj_section *got; +#endif +#if defined(__mips__) + struct mips_hi16 *mips_hi16_list; +#endif +}; + +struct arch_symbol { + struct obj_symbol root; +#if defined(CONFIG_USE_PLT_ENTRIES) +#if defined(CONFIG_USE_PLT_LIST) + struct arch_list_entry *pltent; +#else + struct arch_single_entry pltent; +#endif +#endif +#if defined(CONFIG_USE_GOT_ENTRIES) + struct arch_single_entry gotent; +#endif +}; + + +struct external_module { + const char *name; + ElfW(Addr) addr; + int used; + size_t nsyms; + struct new_module_symbol *syms; +}; + +static struct new_module_symbol *ksyms; +static size_t nksyms; + +static struct external_module *ext_modules; +static int n_ext_modules; +static int n_ext_modules_used; +extern int delete_module(const char *); + +static char *m_filename; +static char *m_fullName; + + + +/*======================================================================*/ + + +static int check_module_name_match(const char *filename, struct stat *statbuf, + void *userdata) +{ + char *fullname = (char *) userdata; + + if (fullname[0] == '\0') + return (FALSE); + else { + char *tmp, *tmp1 = bb_xstrdup(filename); + tmp = bb_get_last_path_component(tmp1); + if (strcmp(tmp, fullname) == 0) { + free(tmp1); + /* Stop searching if we find a match */ + m_filename = bb_xstrdup(filename); + return (FALSE); + } + free(tmp1); + } + return (TRUE); +} + + +/*======================================================================*/ + +static struct obj_file *arch_new_file(void) +{ + struct arch_file *f; + f = xmalloc(sizeof(*f)); + + memset(f, 0, sizeof(*f)); + + return &f->root; +} + +static struct obj_section *arch_new_section(void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +static struct obj_symbol *arch_new_symbol(void) +{ + struct arch_symbol *sym; + sym = xmalloc(sizeof(*sym)); + + memset(sym, 0, sizeof(*sym)); + + return &sym->root; +} + +static enum obj_reloc +arch_apply_relocation(struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + ElfW(RelM) *rel, ElfW(Addr) v) +{ + struct arch_file *ifile = (struct arch_file *) f; + enum obj_reloc ret = obj_reloc_ok; + ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset); + ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset; +#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) + struct arch_symbol *isym = (struct arch_symbol *) sym; +#endif +#if defined(CONFIG_USE_GOT_ENTRIES) + ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0; +#endif +#if defined(CONFIG_USE_PLT_ENTRIES) + ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0; + unsigned long *ip; +#if defined(CONFIG_USE_PLT_LIST) + struct arch_list_entry *pe; +#else + struct arch_single_entry *pe; +#endif +#endif + + switch (ELF32_R_TYPE(rel->r_info)) { + + +#if defined(__arm__) + case R_ARM_NONE: + break; + + case R_ARM_ABS32: + *loc += v; + break; + + case R_ARM_GOT32: + goto bb_use_got; + + case R_ARM_GOTPC: + /* relative reloc, always to _GLOBAL_OFFSET_TABLE_ + * (which is .got) similar to branch, + * but is full 32 bits relative */ + + assert(got); + *loc += got - dot; + break; + + case R_ARM_PC24: + case R_ARM_PLT32: + goto bb_use_plt; + + case R_ARM_GOTOFF: /* address relative to the got */ + assert(got); + *loc += v - got; + break; + +#elif defined(__s390__) + case R_390_32: + *(unsigned int *) loc += v; + break; + case R_390_16: + *(unsigned short *) loc += v; + break; + case R_390_8: + *(unsigned char *) loc += v; + break; + + case R_390_PC32: + *(unsigned int *) loc += v - dot; + break; + case R_390_PC16DBL: + *(unsigned short *) loc += (v - dot) >> 1; + break; + case R_390_PC16: + *(unsigned short *) loc += v - dot; + break; + + case R_390_PLT32: + case R_390_PLT16DBL: + /* find the plt entry and initialize it. */ + assert(isym != NULL); + pe = (struct arch_single_entry *) &isym->pltent; + assert(pe->allocated); + if (pe->inited == 0) { + ip = (unsigned long *)(ifile->plt->contents + pe->offset); + ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */ + ip[1] = 0x100607f1; + if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL) + ip[2] = v - 2; + else + ip[2] = v; + pe->inited = 1; + } + + /* Insert relative distance to target. */ + v = plt + pe->offset - dot; + if (ELF32_R_TYPE(rel->r_info) == R_390_PLT32) + *(unsigned int *) loc = (unsigned int) v; + else if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL) + *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1); + break; + + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + *loc = v; + break; + + case R_390_RELATIVE: + *loc += f->baseaddr; + break; + + case R_390_GOTPC: + assert(got != 0); + *(unsigned long *) loc += got - dot; + break; + + case R_390_GOT12: + case R_390_GOT16: + case R_390_GOT32: + assert(isym != NULL); + assert(got != 0); + if (!isym->gotent.inited) + { + isym->gotent.inited = 1; + *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v; + } + if (ELF32_R_TYPE(rel->r_info) == R_390_GOT12) + *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff; + else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT16) + *(unsigned short *) loc += isym->gotent.offset; + else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT32) + *(unsigned int *) loc += isym->gotent.offset; + break; + +#ifndef R_390_GOTOFF32 +#define R_390_GOTOFF32 R_390_GOTOFF +#endif + case R_390_GOTOFF32: + assert(got != 0); + *loc += v - got; + break; + +#elif defined(__i386__) + + case R_386_NONE: + break; + + case R_386_32: + *loc += v; + break; + + case R_386_PLT32: + case R_386_PC32: + *loc += v - dot; + break; + + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + *loc = v; + break; + + case R_386_RELATIVE: + *loc += f->baseaddr; + break; + + case R_386_GOTPC: + assert(got != 0); + *loc += got - dot; + break; + + case R_386_GOT32: + goto bb_use_got; + + case R_386_GOTOFF: + assert(got != 0); + *loc += v - got; + break; + +#elif defined(__mc68000__) + + case R_68K_NONE: + break; + + case R_68K_32: + *loc += v; + break; + + case R_68K_8: + if (v > 0xff) { + ret = obj_reloc_overflow; + } + *(char *)loc = v; + break; + + case R_68K_16: + if (v > 0xffff) { + ret = obj_reloc_overflow; + } + *(short *)loc = v; + break; + + case R_68K_PC8: + v -= dot; + if ((Elf32_Sword)v > 0x7f || + (Elf32_Sword)v < -(Elf32_Sword)0x80) { + ret = obj_reloc_overflow; + } + *(char *)loc = v; + break; + + case R_68K_PC16: + v -= dot; + if ((Elf32_Sword)v > 0x7fff || + (Elf32_Sword)v < -(Elf32_Sword)0x8000) { + ret = obj_reloc_overflow; + } + *(short *)loc = v; + break; + + case R_68K_PC32: + *(int *)loc = v - dot; + break; + + case R_68K_GLOB_DAT: + case R_68K_JMP_SLOT: + *loc = v; + break; + + case R_68K_RELATIVE: + *(int *)loc += f->baseaddr; + break; + + case R_68K_GOT32: + goto bb_use_got; + +#ifdef R_68K_GOTOFF + case R_68K_GOTOFF: + assert(got != 0); + *loc += v - got; + break; +#endif + +#elif defined(__mips__) + + case R_MIPS_NONE: + break; + + case R_MIPS_32: + *loc += v; + break; + + case R_MIPS_26: + if (v % 4) + ret = obj_reloc_dangerous; + if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000)) + ret = obj_reloc_overflow; + *loc = + (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) & + 0x03ffffff); + break; + + case R_MIPS_HI16: + { + struct mips_hi16 *n; + + /* We cannot relocate this one now because we don't know the value + of the carry we need to add. Save the information, and let LO16 + do the actual relocation. */ + n = (struct mips_hi16 *) xmalloc(sizeof *n); + n->addr = loc; + n->value = v; + n->next = ifile->mips_hi16_list; + ifile->mips_hi16_list = n; + break; + } + + case R_MIPS_LO16: + { + unsigned long insnlo = *loc; + Elf32_Addr val, vallo; + + /* Sign extend the addend we extract from the lo insn. */ + vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; + + if (ifile->mips_hi16_list != NULL) { + struct mips_hi16 *l; + + l = ifile->mips_hi16_list; + while (l != NULL) { + struct mips_hi16 *next; + unsigned long insn; + + /* The value for the HI16 had best be the same. */ + assert(v == l->value); + + /* Do the HI16 relocation. Note that we actually don't + need to know anything about the LO16 itself, except where + to find the low 16 bits of the addend needed by the LO16. */ + insn = *l->addr; + val = + ((insn & 0xffff) << 16) + + vallo; + val += v; + + /* Account for the sign extension that will happen in the + low bits. */ + val = + ((val >> 16) + + ((val & 0x8000) != + 0)) & 0xffff; + + insn = (insn & ~0xffff) | val; + *l->addr = insn; + + next = l->next; + free(l); + l = next; + } + + ifile->mips_hi16_list = NULL; + } + + /* Ok, we're done with the HI16 relocs. Now deal with the LO16. */ + val = v + vallo; + insnlo = (insnlo & ~0xffff) | (val & 0xffff); + *loc = insnlo; + break; + } + +#elif defined(__powerpc__) + + case R_PPC_ADDR16_HA: + *(unsigned short *)loc = (v + 0x8000) >> 16; + break; + + case R_PPC_ADDR16_HI: + *(unsigned short *)loc = v >> 16; + break; + + case R_PPC_ADDR16_LO: + *(unsigned short *)loc = v; + break; + + case R_PPC_REL24: + goto bb_use_plt; + + case R_PPC_REL32: + *loc = v - dot; + break; + + case R_PPC_ADDR32: + *loc = v; + break; + +#elif defined(__sh__) + + case R_SH_NONE: + break; + + case R_SH_DIR32: + *loc += v; + break; + + case R_SH_REL32: + *loc += v - dot; + break; + + case R_SH_PLT32: + *loc = v - dot; + break; + + case R_SH_GLOB_DAT: + case R_SH_JMP_SLOT: + *loc = v; + break; + + case R_SH_RELATIVE: + *loc = f->baseaddr + rel->r_addend; + break; + + case R_SH_GOTPC: + assert(got != 0); + *loc = got - dot + rel->r_addend; + break; + + case R_SH_GOT32: + goto bb_use_got; + + case R_SH_GOTOFF: + assert(got != 0); + *loc = v - got; + break; + +#if defined(__SH5__) + case R_SH_IMM_MEDLOW16: + case R_SH_IMM_LOW16: + { + Elf32_Addr word; + + if (ELF32_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16) + v >>= 16; + + /* + * movi and shori have the format: + * + * | op | imm | reg | reserved | + * 31..26 25..10 9.. 4 3 .. 0 + * + * so we simply mask and or in imm. + */ + word = *loc & ~0x3fffc00; + word |= (v & 0xffff) << 10; + + *loc = word; + + break; + } + + case R_SH_IMM_MEDLOW16_PCREL: + case R_SH_IMM_LOW16_PCREL: + { + Elf32_Addr word; + + word = *loc & ~0x3fffc00; + + v -= dot; + + if (ELF32_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL) + v >>= 16; + + word |= (v & 0xffff) << 10; + + *loc = word; + + break; + } +#endif /* __SH5__ */ +#endif /* __sh__ */ + + default: + printf("Warning: unhandled reloc %d\n",(int)ELF32_R_TYPE(rel->r_info)); + ret = obj_reloc_unhandled; + break; + +#if defined (__v850e__) + case R_V850_NONE: + break; + + case R_V850_32: + /* We write two shorts instead of a long because even + 32-bit insns only need half-word alignment, but + 32-bit data needs to be long-word aligned. */ + v += ((unsigned short *)loc)[0]; + v += ((unsigned short *)loc)[1] << 16; + ((unsigned short *)loc)[0] = v & 0xffff; + ((unsigned short *)loc)[1] = (v >> 16) & 0xffff; + break; + + case R_V850_22_PCREL: + goto bb_use_plt; +#endif + +#if defined (__cris__) + case R_CRIS_NONE: + break; + + case R_CRIS_32: + /* CRIS keeps the relocation value in the r_addend field and + * should not use whats in *loc at all + */ + *loc = v; + break; +#endif + +#if defined(__H8300H__) || defined(__H8300S__) + case R_H8_DIR24R8: + loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1); + *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v); + break; + case R_H8_DIR24A8: + *loc += v; + break; + case R_H8_DIR32: + case R_H8_DIR32A16: + *loc += v; + break; + case R_H8_PCREL16: + v -= dot + 2; + if ((Elf32_Sword)v > 0x7fff || + (Elf32_Sword)v < -(Elf32_Sword)0x8000) + ret = obj_reloc_overflow; + else + *(unsigned short *)loc = v; + break; + case R_H8_PCREL8: + v -= dot + 1; + if ((Elf32_Sword)v > 0x7f || + (Elf32_Sword)v < -(Elf32_Sword)0x80) + ret = obj_reloc_overflow; + else + *(unsigned char *)loc = v; + break; +#endif + +#if defined(CONFIG_USE_PLT_ENTRIES) + +bb_use_plt: + + /* find the plt entry and initialize it if necessary */ + assert(isym != NULL); + +#if defined(CONFIG_USE_PLT_LIST) + for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;) + pe = pe->next; + assert(pe != NULL); +#else + pe = &isym->pltent; +#endif + + if (! pe->inited) { + ip = (unsigned long *) (ifile->plt->contents + pe->offset); + + /* generate some machine code */ + +#if defined(__arm__) + ip[0] = 0xe51ff004; /* ldr pc,[pc,#-4] */ + ip[1] = v; /* sym@ */ +#endif +#if defined(__powerpc__) + ip[0] = 0x3d600000 + ((v + 0x8000) >> 16); /* lis r11,sym@ha */ + ip[1] = 0x396b0000 + (v & 0xffff); /* addi r11,r11,sym@l */ + ip[2] = 0x7d6903a6; /* mtctr r11 */ + ip[3] = 0x4e800420; /* bctr */ +#endif +#if defined (__v850e__) + /* We have to trash a register, so we assume that any control + transfer more than 21-bits away must be a function call + (so we can use a call-clobbered register). */ + ip[0] = 0x0621 + ((v & 0xffff) << 16); /* mov sym, r1 ... */ + ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */ +#endif + pe->inited = 1; + } + + /* relative distance to target */ + v -= dot; + /* if the target is too far away.... */ +#if defined (__arm__) || defined (__powerpc__) + if ((int)v < -0x02000000 || (int)v >= 0x02000000) +#elif defined (__v850e__) + if ((Elf32_Sword)v > 0x1fffff || (Elf32_Sword)v < (Elf32_Sword)-0x200000) +#endif + /* go via the plt */ + v = plt + pe->offset - dot; + +#if defined (__v850e__) + if (v & 1) +#else + if (v & 3) +#endif + ret = obj_reloc_dangerous; + + /* merge the offset into the instruction. */ +#if defined(__arm__) + /* Convert to words. */ + v >>= 2; + + *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff); +#endif +#if defined(__powerpc__) + *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc); +#endif +#if defined (__v850e__) + /* We write two shorts instead of a long because even 32-bit insns + only need half-word alignment, but the 32-bit data write needs + to be long-word aligned. */ + ((unsigned short *)loc)[0] = + (*(unsigned short *)loc & 0xffc0) /* opcode + reg */ + | ((v >> 16) & 0x3f); /* offs high part */ + ((unsigned short *)loc)[1] = + (v & 0xffff); /* offs low part */ +#endif + break; +#endif /* CONFIG_USE_PLT_ENTRIES */ + +#if defined(CONFIG_USE_GOT_ENTRIES) +bb_use_got: + + assert(isym != NULL); + /* needs an entry in the .got: set it, once */ + if (!isym->gotent.inited) { + isym->gotent.inited = 1; + *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v; + } + /* make the reloc with_respect_to_.got */ +#if defined(__sh__) + *loc += isym->gotent.offset + rel->r_addend; +#elif defined(__i386__) || defined(__arm__) || defined(__mc68000__) + *loc += isym->gotent.offset; +#endif + break; + +#endif /* CONFIG_USE_GOT_ENTRIES */ + } + + return ret; +} + + +#if defined(CONFIG_USE_LIST) + +static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list, + int offset, int size) +{ + struct arch_list_entry *pe; + + for (pe = *list; pe != NULL; pe = pe->next) { + if (pe->addend == rel->r_addend) { + break; + } + } + + if (pe == NULL) { + pe = xmalloc(sizeof(struct arch_list_entry)); + pe->next = *list; + pe->addend = rel->r_addend; + pe->offset = offset; + pe->inited = 0; + *list = pe; + return size; + } + return 0; +} + +#endif + +#if defined(CONFIG_USE_SINGLE) + +static int arch_single_init(ElfW(RelM) *rel, struct arch_single_entry *single, + int offset, int size) +{ + if (single->allocated == 0) { + single->allocated = 1; + single->offset = offset; + single->inited = 0; + return size; + } + return 0; +} + +#endif + +#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) + +static struct obj_section *arch_xsect_init(struct obj_file *f, char *name, + int offset, int size) +{ + struct obj_section *myrelsec = obj_find_section(f, name); + + if (offset == 0) { + offset += size; + } + + if (myrelsec) { + obj_extend_section(myrelsec, offset); + } else { + myrelsec = obj_create_alloced_section(f, name, + size, offset); + assert(myrelsec); + } + + return myrelsec; +} + +#endif + +static void arch_create_got(struct obj_file *f) +{ +#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) + struct arch_file *ifile = (struct arch_file *) f; + int i; +#if defined(CONFIG_USE_GOT_ENTRIES) + int got_offset = 0, got_needed = 0, got_allocate; +#endif +#if defined(CONFIG_USE_PLT_ENTRIES) + int plt_offset = 0, plt_needed = 0, plt_allocate; +#endif + struct obj_section *relsec, *symsec, *strsec; + ElfW(RelM) *rel, *relend; + ElfW(Sym) *symtab, *extsym; + const char *strtab, *name; + struct arch_symbol *intsym; + + for (i = 0; i < f->header.e_shnum; ++i) { + relsec = f->sections[i]; + if (relsec->header.sh_type != SHT_RELM) + continue; + + symsec = f->sections[relsec->header.sh_link]; + strsec = f->sections[symsec->header.sh_link]; + + rel = (ElfW(RelM) *) relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM))); + symtab = (ElfW(Sym) *) symsec->contents; + strtab = (const char *) strsec->contents; + + for (; rel < relend; ++rel) { + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + +#if defined(CONFIG_USE_GOT_ENTRIES) + got_allocate = 0; +#endif +#if defined(CONFIG_USE_PLT_ENTRIES) + plt_allocate = 0; +#endif + + switch (ELF32_R_TYPE(rel->r_info)) { +#if defined(__arm__) + case R_ARM_PC24: + case R_ARM_PLT32: + plt_allocate = 1; + break; + + case R_ARM_GOTOFF: + case R_ARM_GOTPC: + got_needed = 1; + continue; + + case R_ARM_GOT32: + got_allocate = 1; + break; + +#elif defined(__i386__) + case R_386_GOTPC: + case R_386_GOTOFF: + got_needed = 1; + continue; + + case R_386_GOT32: + got_allocate = 1; + break; + +#elif defined(__powerpc__) + case R_PPC_REL24: + plt_allocate = 1; + break; + +#elif defined(__mc68000__) + case R_68K_GOT32: + got_allocate = 1; + break; + +#ifdef R_68K_GOTOFF + case R_68K_GOTOFF: + got_needed = 1; + continue; +#endif + +#elif defined(__sh__) + case R_SH_GOT32: + got_allocate = 1; + break; + + case R_SH_GOTPC: + case R_SH_GOTOFF: + got_needed = 1; + continue; + +#elif defined (__v850e__) + case R_V850_22_PCREL: + plt_needed = 1; + break; + +#endif + default: + continue; + } + + if (extsym->st_name != 0) { + name = strtab + extsym->st_name; + } else { + name = f->sections[extsym->st_shndx]->name; + } + intsym = (struct arch_symbol *) obj_find_symbol(f, name); +#if defined(CONFIG_USE_GOT_ENTRIES) + if (got_allocate) { + got_offset += arch_single_init( + rel, &intsym->gotent, + got_offset, CONFIG_GOT_ENTRY_SIZE); + + got_needed = 1; + } +#endif +#if defined(CONFIG_USE_PLT_ENTRIES) + if (plt_allocate) { +#if defined(CONFIG_USE_PLT_LIST) + plt_offset += arch_list_add( + rel, &intsym->pltent, + plt_offset, CONFIG_PLT_ENTRY_SIZE); +#else + plt_offset += arch_single_init( + rel, &intsym->pltent, + plt_offset, CONFIG_PLT_ENTRY_SIZE); +#endif + plt_needed = 1; + } +#endif + } + } + +#if defined(CONFIG_USE_GOT_ENTRIES) + if (got_needed) { + ifile->got = arch_xsect_init(f, ".got", got_offset, + CONFIG_GOT_ENTRY_SIZE); + } +#endif + +#if defined(CONFIG_USE_PLT_ENTRIES) + if (plt_needed) { + ifile->plt = arch_xsect_init(f, ".plt", plt_offset, + CONFIG_PLT_ENTRY_SIZE); + } +#endif + +#endif /* defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) */ +} + +/*======================================================================*/ + +/* Standard ELF hash function. */ +static inline unsigned long obj_elf_hash_n(const char *name, unsigned long n) +{ + unsigned long h = 0; + unsigned long g; + unsigned char ch; + + while (n > 0) { + ch = *name++; + h = (h << 4) + ch; + if ((g = (h & 0xf0000000)) != 0) { + h ^= g >> 24; + h &= ~g; + } + n--; + } + return h; +} + +static unsigned long obj_elf_hash(const char *name) +{ + return obj_elf_hash_n(name, strlen(name)); +} + +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING +/* String comparison for non-co-versioned kernel and module. */ + +static int ncv_strcmp(const char *a, const char *b) +{ + size_t alen = strlen(a), blen = strlen(b); + + if (blen == alen + 10 && b[alen] == '_' && b[alen + 1] == 'R') + return strncmp(a, b, alen); + else if (alen == blen + 10 && a[blen] == '_' && a[blen + 1] == 'R') + return strncmp(a, b, blen); + else + return strcmp(a, b); +} + +/* String hashing for non-co-versioned kernel and module. Here + we are simply forced to drop the crc from the hash. */ + +static unsigned long ncv_symbol_hash(const char *str) +{ + size_t len = strlen(str); + if (len > 10 && str[len - 10] == '_' && str[len - 9] == 'R') + len -= 10; + return obj_elf_hash_n(str, len); +} + +static void +obj_set_symbol_compare(struct obj_file *f, + int (*cmp) (const char *, const char *), + unsigned long (*hash) (const char *)) +{ + if (cmp) + f->symbol_cmp = cmp; + if (hash) { + struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next; + int i; + + f->symbol_hash = hash; + + memcpy(tmptab, f->symtab, sizeof(tmptab)); + memset(f->symtab, 0, sizeof(f->symtab)); + + for (i = 0; i < HASH_BUCKETS; ++i) + for (sym = tmptab[i]; sym; sym = next) { + unsigned long h = hash(sym->name) % HASH_BUCKETS; + next = sym->next; + sym->next = f->symtab[h]; + f->symtab[h] = sym; + } + } +} + +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ + +static struct obj_symbol * +obj_add_symbol(struct obj_file *f, const char *name, + unsigned long symidx, int info, + int secidx, ElfW(Addr) value, + unsigned long size) +{ + struct obj_symbol *sym; + unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS; + int n_type = ELFW(ST_TYPE) (info); + int n_binding = ELFW(ST_BIND) (info); + + for (sym = f->symtab[hash]; sym; sym = sym->next) + if (f->symbol_cmp(sym->name, name) == 0) { + int o_secidx = sym->secidx; + int o_info = sym->info; + int o_type = ELFW(ST_TYPE) (o_info); + int o_binding = ELFW(ST_BIND) (o_info); + + /* A redefinition! Is it legal? */ + + if (secidx == SHN_UNDEF) + return sym; + else if (o_secidx == SHN_UNDEF) + goto found; + else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) { + /* Cope with local and global symbols of the same name + in the same object file, as might have been created + by ld -r. The only reason locals are now seen at this + level at all is so that we can do semi-sensible things + with parameters. */ + + struct obj_symbol *nsym, **p; + + nsym = arch_new_symbol(); + nsym->next = sym->next; + nsym->ksymidx = -1; + + /* Excise the old (local) symbol from the hash chain. */ + for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next) + continue; + *p = sym = nsym; + goto found; + } else if (n_binding == STB_LOCAL) { + /* Another symbol of the same name has already been defined. + Just add this to the local table. */ + sym = arch_new_symbol(); + sym->next = NULL; + sym->ksymidx = -1; + f->local_symtab[symidx] = sym; + goto found; + } else if (n_binding == STB_WEAK) + return sym; + else if (o_binding == STB_WEAK) + goto found; + /* Don't unify COMMON symbols with object types the programmer + doesn't expect. */ + else if (secidx == SHN_COMMON + && (o_type == STT_NOTYPE || o_type == STT_OBJECT)) + return sym; + else if (o_secidx == SHN_COMMON + && (n_type == STT_NOTYPE || n_type == STT_OBJECT)) + goto found; + else { + /* Don't report an error if the symbol is coming from + the kernel or some external module. */ + if (secidx <= SHN_HIRESERVE) + bb_error_msg("%s multiply defined", name); + return sym; + } + } + + /* Completely new symbol. */ + sym = arch_new_symbol(); + sym->next = f->symtab[hash]; + f->symtab[hash] = sym; + sym->ksymidx = -1; + + if (ELFW(ST_BIND)(info) == STB_LOCAL && symidx != -1) { + if (symidx >= f->local_symtab_size) + bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld", + name, (long) symidx, (long) f->local_symtab_size); + else + f->local_symtab[symidx] = sym; + } + +found: + sym->name = name; + sym->value = value; + sym->size = size; + sym->secidx = secidx; + sym->info = info; + + return sym; +} + +static struct obj_symbol * +obj_find_symbol(struct obj_file *f, const char *name) +{ + struct obj_symbol *sym; + unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS; + + for (sym = f->symtab[hash]; sym; sym = sym->next) + if (f->symbol_cmp(sym->name, name) == 0) + return sym; + + return NULL; +} + +static ElfW(Addr) + obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym) +{ + if (sym) { + if (sym->secidx >= SHN_LORESERVE) + return sym->value; + + return sym->value + f->sections[sym->secidx]->header.sh_addr; + } else { + /* As a special case, a NULL sym has value zero. */ + return 0; + } +} + +static struct obj_section *obj_find_section(struct obj_file *f, const char *name) +{ + int i, n = f->header.e_shnum; + + for (i = 0; i < n; ++i) + if (strcmp(f->sections[i]->name, name) == 0) + return f->sections[i]; + + return NULL; +} + +static int obj_load_order_prio(struct obj_section *a) +{ + unsigned long af, ac; + + af = a->header.sh_flags; + + ac = 0; + if (a->name[0] != '.' || strlen(a->name) != 10 || + strcmp(a->name + 5, ".init")) + ac |= 32; + if (af & SHF_ALLOC) + ac |= 16; + if (!(af & SHF_WRITE)) + ac |= 8; + if (af & SHF_EXECINSTR) + ac |= 4; + if (a->header.sh_type != SHT_NOBITS) + ac |= 2; + + return ac; +} + +static void +obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec) +{ + struct obj_section **p; + int prio = obj_load_order_prio(sec); + for (p = f->load_order_search_start; *p; p = &(*p)->load_next) + if (obj_load_order_prio(*p) < prio) + break; + sec->load_next = *p; + *p = sec; +} + +static struct obj_section *obj_create_alloced_section(struct obj_file *f, + const char *name, + unsigned long align, + unsigned long size) +{ + int newidx = f->header.e_shnum++; + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec)); + f->sections[newidx] = sec = arch_new_section(); + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = SHF_WRITE | SHF_ALLOC; + sec->header.sh_size = size; + sec->header.sh_addralign = align; + sec->name = name; + sec->idx = newidx; + if (size) + sec->contents = xmalloc(size); + + obj_insert_section_load_order(f, sec); + + return sec; +} + +static struct obj_section *obj_create_alloced_section_first(struct obj_file *f, + const char *name, + unsigned long align, + unsigned long size) +{ + int newidx = f->header.e_shnum++; + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec)); + f->sections[newidx] = sec = arch_new_section(); + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = SHF_WRITE | SHF_ALLOC; + sec->header.sh_size = size; + sec->header.sh_addralign = align; + sec->name = name; + sec->idx = newidx; + if (size) + sec->contents = xmalloc(size); + + sec->load_next = f->load_order; + f->load_order = sec; + if (f->load_order_search_start == &f->load_order) + f->load_order_search_start = &sec->load_next; + + return sec; +} + +static void *obj_extend_section(struct obj_section *sec, unsigned long more) +{ + unsigned long oldsize = sec->header.sh_size; + if (more) { + sec->contents = xrealloc(sec->contents, sec->header.sh_size += more); + } + return sec->contents + oldsize; +} + + +/* Conditionally add the symbols from the given symbol set to the + new module. */ + +static int +add_symbols_from( struct obj_file *f, + int idx, struct new_module_symbol *syms, size_t nsyms) +{ + struct new_module_symbol *s; + size_t i; + int used = 0; +#ifdef SYMBOL_PREFIX + char *name_buf = 0; + size_t name_alloced_size = 0; +#endif +#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE + int gpl; + + gpl = obj_gpl_license(f, NULL) == 0; +#endif + for (i = 0, s = syms; i < nsyms; ++i, ++s) { + /* Only add symbols that are already marked external. + If we override locals we may cause problems for + argument initialization. We will also create a false + dependency on the module. */ + struct obj_symbol *sym; + char *name; + + /* GPL licensed modules can use symbols exported with + * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the + * exported names. Non-GPL modules never see any GPLONLY_ + * symbols so they cannot fudge it by adding the prefix on + * their references. + */ + if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) { +#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE + if (gpl) + s->name += 8; + else +#endif + continue; + } + name = (char *)s->name; + +#ifdef SYMBOL_PREFIX + /* Prepend SYMBOL_PREFIX to the symbol's name (the + kernel exports `C names', but module object files + reference `linker names'). */ + size_t extra = sizeof SYMBOL_PREFIX; + size_t name_size = strlen (name) + extra; + if (name_size > name_alloced_size) { + name_alloced_size = name_size * 2; + name_buf = alloca (name_alloced_size); + } + strcpy (name_buf, SYMBOL_PREFIX); + strcpy (name_buf + extra - 1, name); + name = name_buf; +#endif /* SYMBOL_PREFIX */ + + sym = obj_find_symbol(f, name); + if (sym && !(ELFW(ST_BIND) (sym->info) == STB_LOCAL)) { +#ifdef SYMBOL_PREFIX + /* Put NAME_BUF into more permanent storage. */ + name = xmalloc (name_size); + strcpy (name, name_buf); +#endif + sym = obj_add_symbol(f, name, -1, + ELFW(ST_INFO) (STB_GLOBAL, + STT_NOTYPE), + idx, s->value, 0); + /* Did our symbol just get installed? If so, mark the + module as "used". */ + if (sym->secidx == idx) + used = 1; + } + } + + return used; +} + +static void add_kernel_symbols(struct obj_file *f) +{ + struct external_module *m; + int i, nused = 0; + + /* Add module symbols first. */ + + for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m) + if (m->nsyms + && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, + m->nsyms)) m->used = 1, ++nused; + + n_ext_modules_used = nused; + + /* And finally the symbols from the kernel proper. */ + + if (nksyms) + add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms); +} + +static char *get_modinfo_value(struct obj_file *f, const char *key) +{ + struct obj_section *sec; + char *p, *v, *n, *ep; + size_t klen = strlen(key); + + sec = obj_find_section(f, ".modinfo"); + if (sec == NULL) + return NULL; + p = sec->contents; + ep = p + sec->header.sh_size; + while (p < ep) { + v = strchr(p, '='); + n = strchr(p, '\0'); + if (v) { + if (p + klen == v && strncmp(p, key, klen) == 0) + return v + 1; + } else { + if (p + klen == n && strcmp(p, key) == 0) + return n; + } + p = n + 1; + } + + return NULL; +} + + +/*======================================================================*/ +/* Functions relating to module loading after 2.1.18. */ + +static int +new_process_module_arguments(struct obj_file *f, int argc, char **argv) +{ + while (argc > 0) { + char *p, *q, *key, *sym_name; + struct obj_symbol *sym; + char *contents, *loc; + int min, max, n; + + p = *argv; + if ((q = strchr(p, '=')) == NULL) { + argc--; + continue; + } + + key = alloca(q - p + 6); + memcpy(key, "parm_", 5); + memcpy(key + 5, p, q - p); + key[q - p + 5] = 0; + + p = get_modinfo_value(f, key); + key += 5; + if (p == NULL) { + bb_error_msg("invalid parameter %s", key); + return 0; + } + +#ifdef SYMBOL_PREFIX + sym_name = alloca (strlen (key) + sizeof SYMBOL_PREFIX); + strcpy (sym_name, SYMBOL_PREFIX); + strcat (sym_name, key); +#else + sym_name = key; +#endif + sym = obj_find_symbol(f, sym_name); + + /* Also check that the parameter was not resolved from the kernel. */ + if (sym == NULL || sym->secidx > SHN_HIRESERVE) { + bb_error_msg("symbol for parameter %s not found", key); + return 0; + } + + if (isdigit(*p)) { + min = strtoul(p, &p, 10); + if (*p == '-') + max = strtoul(p + 1, &p, 10); + else + max = min; + } else + min = max = 1; + + contents = f->sections[sym->secidx]->contents; + loc = contents + sym->value; + n = (*++q != '\0'); + + while (1) { + if ((*p == 's') || (*p == 'c')) { + char *str; + + /* Do C quoting if we begin with a ", else slurp the lot. */ + if (*q == '"') { + char *r; + + str = alloca(strlen(q)); + for (r = str, q++; *q != '"'; ++q, ++r) { + if (*q == '\0') { + bb_error_msg("improperly terminated string argument for %s", + key); + return 0; + } else if (*q == '\\') + switch (*++q) { + case 'a': + *r = '\a'; + break; + case 'b': + *r = '\b'; + break; + case 'e': + *r = '\033'; + break; + case 'f': + *r = '\f'; + break; + case 'n': + *r = '\n'; + break; + case 'r': + *r = '\r'; + break; + case 't': + *r = '\t'; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int c = *q - '0'; + if (q[1] >= '0' && q[1] <= '7') { + c = (c * 8) + *++q - '0'; + if (q[1] >= '0' && q[1] <= '7') + c = (c * 8) + *++q - '0'; + } + *r = c; + } + break; + + default: + *r = *q; + break; + } else + *r = *q; + } + *r = '\0'; + ++q; + } else { + char *r; + + /* In this case, the string is not quoted. We will break + it using the coma (like for ints). If the user wants to + include comas in a string, he just has to quote it */ + + /* Search the next coma */ + r = strchr(q, ','); + + /* Found ? */ + if (r != (char *) NULL) { + /* Recopy the current field */ + str = alloca(r - q + 1); + memcpy(str, q, r - q); + + /* I don't know if it is useful, as the previous case + doesn't nul terminate the string ??? */ + str[r - q] = '\0'; + + /* Keep next fields */ + q = r; + } else { + /* last string */ + str = q; + q = ""; + } + } + + if (*p == 's') { + /* Normal string */ + obj_string_patch(f, sym->secidx, loc - contents, str); + loc += tgt_sizeof_char_p; + } else { + /* Array of chars (in fact, matrix !) */ + unsigned long charssize; /* size of each member */ + + /* Get the size of each member */ + /* Probably we should do that outside the loop ? */ + if (!isdigit(*(p + 1))) { + bb_error_msg("parameter type 'c' for %s must be followed by" + " the maximum size", key); + return 0; + } + charssize = strtoul(p + 1, (char **) NULL, 10); + + /* Check length */ + if (strlen(str) >= charssize) { + bb_error_msg("string too long for %s (max %ld)", key, + charssize - 1); + return 0; + } + + /* Copy to location */ + strcpy((char *) loc, str); + loc += charssize; + } + } else { + long v = strtoul(q, &q, 0); + switch (*p) { + case 'b': + *loc++ = v; + break; + case 'h': + *(short *) loc = v; + loc += tgt_sizeof_short; + break; + case 'i': + *(int *) loc = v; + loc += tgt_sizeof_int; + break; + case 'l': + *(long *) loc = v; + loc += tgt_sizeof_long; + break; + + default: + bb_error_msg("unknown parameter type '%c' for %s", *p, key); + return 0; + } + } + +retry_end_of_value: + switch (*q) { + case '\0': + goto end_of_arg; + + case ' ': + case '\t': + case '\n': + case '\r': + ++q; + goto retry_end_of_value; + + case ',': + if (++n > max) { + bb_error_msg("too many values for %s (max %d)", key, max); + return 0; + } + ++q; + break; + + default: + bb_error_msg("invalid argument syntax for %s", key); + return 0; + } + } + +end_of_arg: + if (n < min) { + bb_error_msg("too few values for %s (min %d)", key, min); + return 0; + } + + argc--, argv++; + } + + return 1; +} + +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING +static int new_is_module_checksummed(struct obj_file *f) +{ + const char *p = get_modinfo_value(f, "using_checksums"); + if (p) + return atoi(p); + else + return 0; +} + +/* Get the module's kernel version in the canonical integer form. */ + +static int +new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) +{ + char *p, *q; + int a, b, c; + + p = get_modinfo_value(f, "kernel_version"); + if (p == NULL) + return -1; + safe_strncpy(str, p, STRVERSIONLEN); + + a = strtoul(p, &p, 10); + if (*p != '.') + return -1; + b = strtoul(p + 1, &p, 10); + if (*p != '.') + return -1; + c = strtoul(p + 1, &q, 10); + if (p + 1 == q) + return -1; + + return a << 16 | b << 8 | c; +} + +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ + + +/* Fetch the loaded modules, and all currently exported symbols. */ + +static int new_get_kernel_symbols(void) +{ + char *module_names, *mn; + struct external_module *modules, *m; + struct new_module_symbol *syms, *s; + size_t ret, bufsize, nmod, nsyms, i, j; + + /* Collect the loaded modules. */ + + module_names = xmalloc(bufsize = 256); +retry_modules_load: + if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) { + if (errno == ENOSPC && bufsize < ret) { + module_names = xrealloc(module_names, bufsize = ret); + goto retry_modules_load; + } + bb_perror_msg("QM_MODULES"); + return 0; + } + + n_ext_modules = nmod = ret; + + /* Collect the modules' symbols. */ + + if (nmod){ + ext_modules = modules = xmalloc(nmod * sizeof(*modules)); + memset(modules, 0, nmod * sizeof(*modules)); + for (i = 0, mn = module_names, m = modules; + i < nmod; ++i, ++m, mn += strlen(mn) + 1) { + struct new_module_info info; + + if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) { + if (errno == ENOENT) { + /* The module was removed out from underneath us. */ + continue; + } + bb_perror_msg("query_module: QM_INFO: %s", mn); + return 0; + } + + syms = xmalloc(bufsize = 1024); +retry_mod_sym_load: + if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) { + switch (errno) { + case ENOSPC: + syms = xrealloc(syms, bufsize = ret); + goto retry_mod_sym_load; + case ENOENT: + /* The module was removed out from underneath us. */ + continue; + default: + bb_perror_msg("query_module: QM_SYMBOLS: %s", mn); + return 0; + } + } + nsyms = ret; + + m->name = mn; + m->addr = info.addr; + m->nsyms = nsyms; + m->syms = syms; + + for (j = 0, s = syms; j < nsyms; ++j, ++s) { + s->name += (unsigned long) syms; + } + } + } + + /* Collect the kernel's symbols. */ + + syms = xmalloc(bufsize = 16 * 1024); +retry_kern_sym_load: + if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) { + if (errno == ENOSPC && bufsize < ret) { + syms = xrealloc(syms, bufsize = ret); + goto retry_kern_sym_load; + } + bb_perror_msg("kernel: QM_SYMBOLS"); + return 0; + } + nksyms = nsyms = ret; + ksyms = syms; + + for (j = 0, s = syms; j < nsyms; ++j, ++s) { + s->name += (unsigned long) syms; + } + return 1; +} + + +/* Return the kernel symbol checksum version, or zero if not used. */ + +static int new_is_kernel_checksummed(void) +{ + struct new_module_symbol *s; + size_t i; + + /* Using_Versions is not the first symbol, but it should be in there. */ + + for (i = 0, s = ksyms; i < nksyms; ++i, ++s) + if (strcmp((char *) s->name, "Using_Versions") == 0) + return s->value; + + return 0; +} + + +static int new_create_this_module(struct obj_file *f, const char *m_name) +{ + struct obj_section *sec; + + sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long, + sizeof(struct new_module)); + memset(sec->contents, 0, sizeof(struct new_module)); + + obj_add_symbol(f, SPFX "__this_module", -1, + ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), sec->idx, 0, + sizeof(struct new_module)); + + obj_string_patch(f, sec->idx, offsetof(struct new_module, name), + m_name); + + return 1; +} + +#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS +/* add an entry to the __ksymtab section, creating it if necessary */ +static void new_add_ksymtab(struct obj_file *f, struct obj_symbol *sym) +{ + struct obj_section *sec; + ElfW(Addr) ofs; + + /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section. + * If __ksymtab is defined but not marked alloc, x out the first character + * (no obj_delete routine) and create a new __ksymtab with the correct + * characteristics. + */ + sec = obj_find_section(f, "__ksymtab"); + if (sec && !(sec->header.sh_flags & SHF_ALLOC)) { + *((char *)(sec->name)) = 'x'; /* override const */ + sec = NULL; + } + if (!sec) + sec = obj_create_alloced_section(f, "__ksymtab", + tgt_sizeof_void_p, 0); + if (!sec) + return; + sec->header.sh_flags |= SHF_ALLOC; + sec->header.sh_addralign = tgt_sizeof_void_p; /* Empty section might + be byte-aligned */ + ofs = sec->header.sh_size; + obj_symbol_patch(f, sec->idx, ofs, sym); + obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name); + obj_extend_section(sec, 2 * tgt_sizeof_char_p); +} +#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */ + +static int new_create_module_ksymtab(struct obj_file *f) +{ + struct obj_section *sec; + int i; + + /* We must always add the module references. */ + + if (n_ext_modules_used) { + struct new_module_ref *dep; + struct obj_symbol *tm; + + sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p, + (sizeof(struct new_module_ref) + * n_ext_modules_used)); + if (!sec) + return 0; + + tm = obj_find_symbol(f, SPFX "__this_module"); + dep = (struct new_module_ref *) sec->contents; + for (i = 0; i < n_ext_modules; ++i) + if (ext_modules[i].used) { + dep->dep = ext_modules[i].addr; + obj_symbol_patch(f, sec->idx, + (char *) &dep->ref - sec->contents, tm); + dep->next_ref = 0; + ++dep; + } + } + + if (flag_export && !obj_find_section(f, "__ksymtab")) { + size_t nsyms; + int *loaded; + + sec = + obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, + 0); + + /* We don't want to export symbols residing in sections that + aren't loaded. There are a number of these created so that + we make sure certain module options don't appear twice. */ + + loaded = alloca(sizeof(int) * (i = f->header.e_shnum)); + while (--i >= 0) + loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0; + + for (nsyms = i = 0; i < HASH_BUCKETS; ++i) { + struct obj_symbol *sym; + for (sym = f->symtab[i]; sym; sym = sym->next) + if (ELFW(ST_BIND) (sym->info) != STB_LOCAL + && sym->secidx <= SHN_HIRESERVE + && (sym->secidx >= SHN_LORESERVE + || loaded[sym->secidx])) { + ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p; + + obj_symbol_patch(f, sec->idx, ofs, sym); + obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, + sym->name); + + nsyms++; + } + } + + obj_extend_section(sec, nsyms * 2 * tgt_sizeof_char_p); + } + + return 1; +} + + +static int +new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size) +{ + struct new_module *module; + struct obj_section *sec; + void *image; + int ret; + tgt_long m_addr; + + sec = obj_find_section(f, ".this"); + if (!sec || !sec->contents) { + bb_perror_msg_and_die("corrupt module %s?",m_name); + } + module = (struct new_module *) sec->contents; + m_addr = sec->header.sh_addr; + + module->size_of_struct = sizeof(*module); + module->size = m_size; + module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0; + + sec = obj_find_section(f, "__ksymtab"); + if (sec && sec->header.sh_size) { + module->syms = sec->header.sh_addr; + module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p); + } + + if (n_ext_modules_used) { + sec = obj_find_section(f, ".kmodtab"); + module->deps = sec->header.sh_addr; + module->ndeps = n_ext_modules_used; + } + + module->init = + obj_symbol_final_value(f, obj_find_symbol(f, SPFX "init_module")); + module->cleanup = + obj_symbol_final_value(f, obj_find_symbol(f, SPFX "cleanup_module")); + + sec = obj_find_section(f, "__ex_table"); + if (sec) { + module->ex_table_start = sec->header.sh_addr; + module->ex_table_end = sec->header.sh_addr + sec->header.sh_size; + } + + sec = obj_find_section(f, ".text.init"); + if (sec) { + module->runsize = sec->header.sh_addr - m_addr; + } + sec = obj_find_section(f, ".data.init"); + if (sec) { + if (!module->runsize || + module->runsize > sec->header.sh_addr - m_addr) + module->runsize = sec->header.sh_addr - m_addr; + } + sec = obj_find_section(f, ARCHDATA_SEC_NAME); + if (sec && sec->header.sh_size) { + module->archdata_start = (void*)sec->header.sh_addr; + module->archdata_end = module->archdata_start + sec->header.sh_size; + } + sec = obj_find_section(f, KALLSYMS_SEC_NAME); + if (sec && sec->header.sh_size) { + module->kallsyms_start = (void*)sec->header.sh_addr; + module->kallsyms_end = module->kallsyms_start + sec->header.sh_size; + } + + /* Whew! All of the initialization is complete. Collect the final + module image and give it to the kernel. */ + + image = xmalloc(m_size); + obj_create_image(f, image); + + ret = init_module(m_name, (struct new_module *) image); + if (ret) + bb_perror_msg("init_module: %s", m_name); + + free(image); + + return ret == 0; +} + + +/*======================================================================*/ + +static int +obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, + const char *string) +{ + struct obj_string_patch *p; + struct obj_section *strsec; + size_t len = strlen(string) + 1; + char *loc; + + p = xmalloc(sizeof(*p)); + p->next = f->string_patches; + p->reloc_secidx = secidx; + p->reloc_offset = offset; + f->string_patches = p; + + strsec = obj_find_section(f, ".kstrtab"); + if (strsec == NULL) { + strsec = obj_create_alloced_section(f, ".kstrtab", 1, len); + p->string_offset = 0; + loc = strsec->contents; + } else { + p->string_offset = strsec->header.sh_size; + loc = obj_extend_section(strsec, len); + } + memcpy(loc, string, len); + + return 1; +} + +static int +obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, + struct obj_symbol *sym) +{ + struct obj_symbol_patch *p; + + p = xmalloc(sizeof(*p)); + p->next = f->symbol_patches; + p->reloc_secidx = secidx; + p->reloc_offset = offset; + p->sym = sym; + f->symbol_patches = p; + + return 1; +} + +static int obj_check_undefineds(struct obj_file *f) +{ + unsigned long i; + int ret = 1; + + for (i = 0; i < HASH_BUCKETS; ++i) { + struct obj_symbol *sym; + for (sym = f->symtab[i]; sym; sym = sym->next) + if (sym->secidx == SHN_UNDEF) { + if (ELFW(ST_BIND) (sym->info) == STB_WEAK) { + sym->secidx = SHN_ABS; + sym->value = 0; + } else { + if (!flag_quiet) { + bb_error_msg("unresolved symbol %s", sym->name); + } + ret = 0; + } + } + } + + return ret; +} + +static void obj_allocate_commons(struct obj_file *f) +{ + struct common_entry { + struct common_entry *next; + struct obj_symbol *sym; + } *common_head = NULL; + + unsigned long i; + + for (i = 0; i < HASH_BUCKETS; ++i) { + struct obj_symbol *sym; + for (sym = f->symtab[i]; sym; sym = sym->next) + if (sym->secidx == SHN_COMMON) { + /* Collect all COMMON symbols and sort them by size so as to + minimize space wasted by alignment requirements. */ + { + struct common_entry **p, *n; + for (p = &common_head; *p; p = &(*p)->next) + if (sym->size <= (*p)->sym->size) + break; + + n = alloca(sizeof(*n)); + n->next = *p; + n->sym = sym; + *p = n; + } + } + } + + for (i = 1; i < f->local_symtab_size; ++i) { + struct obj_symbol *sym = f->local_symtab[i]; + if (sym && sym->secidx == SHN_COMMON) { + struct common_entry **p, *n; + for (p = &common_head; *p; p = &(*p)->next) + if (sym == (*p)->sym) + break; + else if (sym->size < (*p)->sym->size) { + n = alloca(sizeof(*n)); + n->next = *p; + n->sym = sym; + *p = n; + break; + } + } + } + + if (common_head) { + /* Find the bss section. */ + for (i = 0; i < f->header.e_shnum; ++i) + if (f->sections[i]->header.sh_type == SHT_NOBITS) + break; + + /* If for some reason there hadn't been one, create one. */ + if (i == f->header.e_shnum) { + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (i + 1) * sizeof(sec)); + f->sections[i] = sec = arch_new_section(); + f->header.e_shnum = i + 1; + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = SHF_WRITE | SHF_ALLOC; + sec->name = ".bss"; + sec->idx = i; + } + + /* Allocate the COMMONS. */ + { + ElfW(Addr) bss_size = f->sections[i]->header.sh_size; + ElfW(Addr) max_align = f->sections[i]->header.sh_addralign; + struct common_entry *c; + + for (c = common_head; c; c = c->next) { + ElfW(Addr) align = c->sym->value; + + if (align > max_align) + max_align = align; + if (bss_size & (align - 1)) + bss_size = (bss_size | (align - 1)) + 1; + + c->sym->secidx = i; + c->sym->value = bss_size; + + bss_size += c->sym->size; + } + + f->sections[i]->header.sh_size = bss_size; + f->sections[i]->header.sh_addralign = max_align; + } + } + + /* For the sake of patch relocation and parameter initialization, + allocate zeroed data for NOBITS sections now. Note that after + this we cannot assume NOBITS are really empty. */ + for (i = 0; i < f->header.e_shnum; ++i) { + struct obj_section *s = f->sections[i]; + if (s->header.sh_type == SHT_NOBITS) { + if (s->header.sh_size != 0) + s->contents = memset(xmalloc(s->header.sh_size), + 0, s->header.sh_size); + else + s->contents = NULL; + + s->header.sh_type = SHT_PROGBITS; + } + } +} + +static unsigned long obj_load_size(struct obj_file *f) +{ + unsigned long dot = 0; + struct obj_section *sec; + + /* Finalize the positions of the sections relative to one another. */ + + for (sec = f->load_order; sec; sec = sec->load_next) { + ElfW(Addr) align; + + align = sec->header.sh_addralign; + if (align && (dot & (align - 1))) + dot = (dot | (align - 1)) + 1; + + sec->header.sh_addr = dot; + dot += sec->header.sh_size; + } + + return dot; +} + +static int obj_relocate(struct obj_file *f, ElfW(Addr) base) +{ + int i, n = f->header.e_shnum; + int ret = 1; + + /* Finalize the addresses of the sections. */ + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + + /* And iterate over all of the relocations. */ + + for (i = 0; i < n; ++i) { + struct obj_section *relsec, *symsec, *targsec, *strsec; + ElfW(RelM) * rel, *relend; + ElfW(Sym) * symtab; + const char *strtab; + + relsec = f->sections[i]; + if (relsec->header.sh_type != SHT_RELM) + continue; + + symsec = f->sections[relsec->header.sh_link]; + targsec = f->sections[relsec->header.sh_info]; + strsec = f->sections[symsec->header.sh_link]; + + rel = (ElfW(RelM) *) relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM))); + symtab = (ElfW(Sym) *) symsec->contents; + strtab = (const char *) strsec->contents; + + for (; rel < relend; ++rel) { + ElfW(Addr) value = 0; + struct obj_symbol *intsym = NULL; + unsigned long symndx; + ElfW(Sym) * extsym = 0; + const char *errmsg; + + /* Attempt to find a value to use for this relocation. */ + + symndx = ELFW(R_SYM) (rel->r_info); + if (symndx) { + /* Note we've already checked for undefined symbols. */ + + extsym = &symtab[symndx]; + if (ELFW(ST_BIND) (extsym->st_info) == STB_LOCAL) { + /* Local symbols we look up in the local table to be sure + we get the one that is really intended. */ + intsym = f->local_symtab[symndx]; + } else { + /* Others we look up in the hash table. */ + const char *name; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = obj_find_symbol(f, name); + } + + value = obj_symbol_final_value(f, intsym); + intsym->referenced = 1; + } +#if SHT_RELM == SHT_RELA +#if defined(__alpha__) && defined(AXP_BROKEN_GAS) + /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9. */ + if (!extsym || !extsym->st_name || + ELFW(ST_BIND) (extsym->st_info) != STB_LOCAL) +#endif + value += rel->r_addend; +#endif + + /* Do it! */ + switch (arch_apply_relocation + (f, targsec, symsec, intsym, rel, value)) { + case obj_reloc_ok: + break; + + case obj_reloc_overflow: + errmsg = "Relocation overflow"; + goto bad_reloc; + case obj_reloc_dangerous: + errmsg = "Dangerous relocation"; + goto bad_reloc; + case obj_reloc_unhandled: + errmsg = "Unhandled relocation"; +bad_reloc: + if (extsym) { + bb_error_msg("%s of type %ld for %s", errmsg, + (long) ELFW(R_TYPE) (rel->r_info), + strtab + extsym->st_name); + } else { + bb_error_msg("%s of type %ld", errmsg, + (long) ELFW(R_TYPE) (rel->r_info)); + } + ret = 0; + break; + } + } + } + + /* Finally, take care of the patches. */ + + if (f->string_patches) { + struct obj_string_patch *p; + struct obj_section *strsec; + ElfW(Addr) strsec_base; + strsec = obj_find_section(f, ".kstrtab"); + strsec_base = strsec->header.sh_addr; + + for (p = f->string_patches; p; p = p->next) { + struct obj_section *targsec = f->sections[p->reloc_secidx]; + *(ElfW(Addr) *) (targsec->contents + p->reloc_offset) + = strsec_base + p->string_offset; + } + } + + if (f->symbol_patches) { + struct obj_symbol_patch *p; + + for (p = f->symbol_patches; p; p = p->next) { + struct obj_section *targsec = f->sections[p->reloc_secidx]; + *(ElfW(Addr) *) (targsec->contents + p->reloc_offset) + = obj_symbol_final_value(f, p->sym); + } + } + + return ret; +} + +static int obj_create_image(struct obj_file *f, char *image) +{ + struct obj_section *sec; + ElfW(Addr) base = f->baseaddr; + + for (sec = f->load_order; sec; sec = sec->load_next) { + char *secimg; + + if (sec->contents == 0 || sec->header.sh_size == 0) + continue; + + secimg = image + (sec->header.sh_addr - base); + + /* Note that we allocated data for NOBITS sections earlier. */ + memcpy(secimg, sec->contents, sec->header.sh_size); + } + + return 1; +} + +/*======================================================================*/ + +static struct obj_file *obj_load(FILE * fp, int loadprogbits) +{ + struct obj_file *f; + ElfW(Shdr) * section_headers; + int shnum, i; + char *shstrtab; + + /* Read the file header. */ + + f = arch_new_file(); + memset(f, 0, sizeof(*f)); + f->symbol_cmp = strcmp; + f->symbol_hash = obj_elf_hash; + f->load_order_search_start = &f->load_order; + + fseek(fp, 0, SEEK_SET); + if (fread(&f->header, sizeof(f->header), 1, fp) != 1) { + bb_perror_msg("error reading ELF header"); + return NULL; + } + + if (f->header.e_ident[EI_MAG0] != ELFMAG0 + || f->header.e_ident[EI_MAG1] != ELFMAG1 + || f->header.e_ident[EI_MAG2] != ELFMAG2 + || f->header.e_ident[EI_MAG3] != ELFMAG3) { + bb_error_msg("not an ELF file"); + return NULL; + } + if (f->header.e_ident[EI_CLASS] != ELFCLASSM + || f->header.e_ident[EI_DATA] != ELFDATAM + || f->header.e_ident[EI_VERSION] != EV_CURRENT + || !MATCH_MACHINE(f->header.e_machine)) { + bb_error_msg("ELF file not for this architecture"); + return NULL; + } + if (f->header.e_type != ET_REL) { + bb_error_msg("ELF file not a relocatable object"); + return NULL; + } + + /* Read the section headers. */ + + if (f->header.e_shentsize != sizeof(ElfW(Shdr))) { + bb_error_msg("section header size mismatch: %lu != %lu", + (unsigned long) f->header.e_shentsize, + (unsigned long) sizeof(ElfW(Shdr))); + return NULL; + } + + shnum = f->header.e_shnum; + f->sections = xmalloc(sizeof(struct obj_section *) * shnum); + memset(f->sections, 0, sizeof(struct obj_section *) * shnum); + + section_headers = alloca(sizeof(ElfW(Shdr)) * shnum); + fseek(fp, f->header.e_shoff, SEEK_SET); + if (fread(section_headers, sizeof(ElfW(Shdr)), shnum, fp) != shnum) { + bb_perror_msg("error reading ELF section headers"); + return NULL; + } + + /* Read the section data. */ + + for (i = 0; i < shnum; ++i) { + struct obj_section *sec; + + f->sections[i] = sec = arch_new_section(); + memset(sec, 0, sizeof(*sec)); + + sec->header = section_headers[i]; + sec->idx = i; + + if(sec->header.sh_size) switch (sec->header.sh_type) { + case SHT_NULL: + case SHT_NOTE: + case SHT_NOBITS: + /* ignore */ + break; + + case SHT_PROGBITS: +#if LOADBITS + if (!loadprogbits) { + sec->contents = NULL; + break; + } +#endif + case SHT_SYMTAB: + case SHT_STRTAB: + case SHT_RELM: + if (sec->header.sh_size > 0) { + sec->contents = xmalloc(sec->header.sh_size); + fseek(fp, sec->header.sh_offset, SEEK_SET); + if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) { + bb_perror_msg("error reading ELF section data"); + return NULL; + } + } else { + sec->contents = NULL; + } + break; + +#if SHT_RELM == SHT_REL + case SHT_RELA: + bb_error_msg("RELA relocations not supported on this architecture"); + return NULL; +#else + case SHT_REL: + bb_error_msg("REL relocations not supported on this architecture"); + return NULL; +#endif + + default: + if (sec->header.sh_type >= SHT_LOPROC) { + /* Assume processor specific section types are debug + info and can safely be ignored. If this is ever not + the case (Hello MIPS?), don't put ifdefs here but + create an arch_load_proc_section(). */ + break; + } + + bb_error_msg("can't handle sections of type %ld", + (long) sec->header.sh_type); + return NULL; + } + } + + /* Do what sort of interpretation as needed by each section. */ + + shstrtab = f->sections[f->header.e_shstrndx]->contents; + + for (i = 0; i < shnum; ++i) { + struct obj_section *sec = f->sections[i]; + sec->name = shstrtab + sec->header.sh_name; + } + + for (i = 0; i < shnum; ++i) { + struct obj_section *sec = f->sections[i]; + + /* .modinfo should be contents only but gcc has no attribute for that. + * The kernel may have marked .modinfo as ALLOC, ignore this bit. + */ + if (strcmp(sec->name, ".modinfo") == 0) + sec->header.sh_flags &= ~SHF_ALLOC; + + if (sec->header.sh_flags & SHF_ALLOC) + obj_insert_section_load_order(f, sec); + + switch (sec->header.sh_type) { + case SHT_SYMTAB: + { + unsigned long nsym, j; + char *strtab; + ElfW(Sym) * sym; + + if (sec->header.sh_entsize != sizeof(ElfW(Sym))) { + bb_error_msg("symbol size mismatch: %lu != %lu", + (unsigned long) sec->header.sh_entsize, + (unsigned long) sizeof(ElfW(Sym))); + return NULL; + } + + nsym = sec->header.sh_size / sizeof(ElfW(Sym)); + strtab = f->sections[sec->header.sh_link]->contents; + sym = (ElfW(Sym) *) sec->contents; + + /* Allocate space for a table of local symbols. */ + j = f->local_symtab_size = sec->header.sh_info; + f->local_symtab = xcalloc(j, sizeof(struct obj_symbol *)); + + /* Insert all symbols into the hash table. */ + for (j = 1, ++sym; j < nsym; ++j, ++sym) { + ElfW(Addr) val = sym->st_value; + const char *name; + if (sym->st_name) + name = strtab + sym->st_name; + else if (sym->st_shndx < shnum) + name = f->sections[sym->st_shndx]->name; + else + continue; + +#if defined(__SH5__) + /* + * For sh64 it is possible that the target of a branch + * requires a mode switch (32 to 16 and back again). + * + * This is implied by the lsb being set in the target + * address for SHmedia mode and clear for SHcompact. + */ + val |= sym->st_other & 4; +#endif + + obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, + val, sym->st_size); + } + } + break; + + case SHT_RELM: + if (sec->header.sh_entsize != sizeof(ElfW(RelM))) { + bb_error_msg("relocation entry size mismatch: %lu != %lu", + (unsigned long) sec->header.sh_entsize, + (unsigned long) sizeof(ElfW(RelM))); + return NULL; + } + break; + /* XXX Relocation code from modutils-2.3.19 is not here. + * Why? That's about 20 lines of code from obj/obj_load.c, + * which gets done in a second pass through the sections. + * This BusyBox insmod does similar work in obj_relocate(). */ + } + } + + return f; +} + +#ifdef CONFIG_FEATURE_INSMOD_LOADINKMEM +/* + * load the unloaded sections directly into the memory allocated by + * kernel for the module + */ + +static int obj_load_progbits(FILE * fp, struct obj_file* f, char* imagebase) +{ + ElfW(Addr) base = f->baseaddr; + struct obj_section* sec; + + for (sec = f->load_order; sec; sec = sec->load_next) { + + /* section already loaded? */ + if (sec->contents != NULL) + continue; + + if (sec->header.sh_size == 0) + continue; + + sec->contents = imagebase + (sec->header.sh_addr - base); + fseek(fp, sec->header.sh_offset, SEEK_SET); + if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) { + bb_error_msg("error reading ELF section data: %s\n", strerror(errno)); + return 0; + } + + } + return 1; +} +#endif + +static void hide_special_symbols(struct obj_file *f) +{ + static const char *const specials[] = { + SPFX "cleanup_module", + SPFX "init_module", + SPFX "kernel_version", + NULL + }; + + struct obj_symbol *sym; + const char *const *p; + + for (p = specials; *p; ++p) + if ((sym = obj_find_symbol(f, *p)) != NULL) + sym->info = + ELFW(ST_INFO) (STB_LOCAL, ELFW(ST_TYPE) (sym->info)); +} + + +#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE +static int obj_gpl_license(struct obj_file *f, const char **license) +{ + struct obj_section *sec; + /* This list must match *exactly* the list of allowable licenses in + * linux/include/linux/module.h. Checking for leading "GPL" will not + * work, somebody will use "GPL sucks, this is proprietary". + */ + static const char *gpl_licenses[] = { + "GPL", + "GPL v2", + "GPL and additional rights", + "Dual BSD/GPL", + "Dual MPL/GPL", + }; + + if ((sec = obj_find_section(f, ".modinfo"))) { + const char *value, *ptr, *endptr; + ptr = sec->contents; + endptr = ptr + sec->header.sh_size; + while (ptr < endptr) { + if ((value = strchr(ptr, '=')) && strncmp(ptr, "license", value-ptr) == 0) { + int i; + if (license) + *license = value+1; + for (i = 0; i < sizeof(gpl_licenses)/sizeof(gpl_licenses[0]); ++i) { + if (strcmp(value+1, gpl_licenses[i]) == 0) + return(0); + } + return(2); + } + if (strchr(ptr, '\0')) + ptr = strchr(ptr, '\0') + 1; + else + ptr = endptr; + } + } + return(1); +} + +#define TAINT_FILENAME "/proc/sys/kernel/tainted" +#define TAINT_PROPRIETORY_MODULE (1<<0) +#define TAINT_FORCED_MODULE (1<<1) +#define TAINT_UNSAFE_SMP (1<<2) +#define TAINT_URL "http://www.tux.org/lkml/#export-tainted" + +static void set_tainted(struct obj_file *f, int fd, char *m_name, + int kernel_has_tainted, int taint, const char *text1, const char *text2) +{ + char buf[80]; + int oldval; + static int first = 1; + if (fd < 0 && !kernel_has_tainted) + return; /* New modutils on old kernel */ + printf("Warning: loading %s will taint the kernel: %s%s\n", + m_name, text1, text2); + if (first) { + printf(" See %s for information about tainted modules\n", TAINT_URL); + first = 0; + } + if (fd >= 0) { + read(fd, buf, sizeof(buf)-1); + buf[sizeof(buf)-1] = '\0'; + oldval = strtoul(buf, NULL, 10); + sprintf(buf, "%d\n", oldval | taint); + write(fd, buf, strlen(buf)); + } +} + +/* Check if loading this module will taint the kernel. */ +static void check_tainted_module(struct obj_file *f, char *m_name) +{ + static const char tainted_file[] = TAINT_FILENAME; + int fd, kernel_has_tainted; + const char *ptr; + + kernel_has_tainted = 1; + if ((fd = open(tainted_file, O_RDWR)) < 0) { + if (errno == ENOENT) + kernel_has_tainted = 0; + else if (errno == EACCES) + kernel_has_tainted = 1; + else { + perror(tainted_file); + kernel_has_tainted = 0; + } + } + + switch (obj_gpl_license(f, &ptr)) { + case 0: + break; + case 1: + set_tainted(f, fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", ""); + break; + case 2: + /* The module has a non-GPL license so we pretend that the + * kernel always has a taint flag to get a warning even on + * kernels without the proc flag. + */ + set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr); + break; + default: + set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", ""); + break; + } + + if (flag_force_load) + set_tainted(f, fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", ""); + + if (fd >= 0) + close(fd); +} +#else /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */ +#define check_tainted_module(x, y) do { } while(0); +#endif /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */ + +#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS +/* add module source, timestamp, kernel version and a symbol for the + * start of some sections. this info is used by ksymoops to do better + * debugging. + */ +static int +get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) +{ +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING + return new_get_module_version(f, str); +#else /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ + strncpy(str, "???", sizeof(str)); + return -1; +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ +} + +/* add module source, timestamp, kernel version and a symbol for the + * start of some sections. this info is used by ksymoops to do better + * debugging. + */ +static void +add_ksymoops_symbols(struct obj_file *f, const char *filename, + const char *m_name) +{ + static const char symprefix[] = "__insmod_"; + struct obj_section *sec; + struct obj_symbol *sym; + char *name, *absolute_filename; + char str[STRVERSIONLEN], real[PATH_MAX]; + int i, l, lm_name, lfilename, use_ksymtab, version; + struct stat statbuf; + + static const char *section_names[] = { + ".text", + ".rodata", + ".data", + ".bss" + ".sbss" + }; + + if (realpath(filename, real)) { + absolute_filename = bb_xstrdup(real); + } + else { + int save_errno = errno; + bb_error_msg("cannot get realpath for %s", filename); + errno = save_errno; + perror(""); + absolute_filename = bb_xstrdup(filename); + } + + lm_name = strlen(m_name); + lfilename = strlen(absolute_filename); + + /* add to ksymtab if it already exists or there is no ksymtab and other symbols + * are not to be exported. otherwise leave ksymtab alone for now, the + * "export all symbols" compatibility code will export these symbols later. + */ + use_ksymtab = obj_find_section(f, "__ksymtab") || !flag_export; + + if ((sec = obj_find_section(f, ".this"))) { + /* tag the module header with the object name, last modified + * timestamp and module version. worst case for module version + * is 0xffffff, decimal 16777215. putting all three fields in + * one symbol is less readable but saves kernel space. + */ + l = sizeof(symprefix)+ /* "__insmod_" */ + lm_name+ /* module name */ + 2+ /* "_O" */ + lfilename+ /* object filename */ + 2+ /* "_M" */ + 2*sizeof(statbuf.st_mtime)+ /* mtime in hex */ + 2+ /* "_V" */ + 8+ /* version in dec */ + 1; /* nul */ + name = xmalloc(l); + if (stat(absolute_filename, &statbuf) != 0) + statbuf.st_mtime = 0; + version = get_module_version(f, str); /* -1 if not found */ + snprintf(name, l, "%s%s_O%s_M%0*lX_V%d", + symprefix, m_name, absolute_filename, + (int)(2*sizeof(statbuf.st_mtime)), statbuf.st_mtime, + version); + sym = obj_add_symbol(f, name, -1, + ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + new_add_ksymtab(f, sym); + } + free(absolute_filename); +#ifdef _NOT_SUPPORTED_ + /* record where the persistent data is going, same address as previous symbol */ + + if (f->persist) { + l = sizeof(symprefix)+ /* "__insmod_" */ + lm_name+ /* module name */ + 2+ /* "_P" */ + strlen(f->persist)+ /* data store */ + 1; /* nul */ + name = xmalloc(l); + snprintf(name, l, "%s%s_P%s", + symprefix, m_name, f->persist); + sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + new_add_ksymtab(f, sym); + } +#endif /* _NOT_SUPPORTED_ */ + /* tag the desired sections if size is non-zero */ + + for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); ++i) { + if ((sec = obj_find_section(f, section_names[i])) && + sec->header.sh_size) { + l = sizeof(symprefix)+ /* "__insmod_" */ + lm_name+ /* module name */ + 2+ /* "_S" */ + strlen(sec->name)+ /* section name */ + 2+ /* "_L" */ + 8+ /* length in dec */ + 1; /* nul */ + name = xmalloc(l); + snprintf(name, l, "%s%s_S%s_L%ld", + symprefix, m_name, sec->name, + (long)sec->header.sh_size); + sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + new_add_ksymtab(f, sym); + } + } +} +#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */ + +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP +static void print_load_map(struct obj_file *f) +{ + struct obj_symbol *sym; + struct obj_symbol **all, **p; + struct obj_section *sec; + int i, nsyms, *loaded; + + /* Report on the section layout. */ + + printf("Sections: Size %-*s Align\n", + (int) (2 * sizeof(void *)), "Address"); + + for (sec = f->load_order; sec; sec = sec->load_next) { + int a; + unsigned long tmp; + + for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a) + tmp >>= 1; + if (a == -1) + a = 0; + + printf("%-15s %08lx %0*lx 2**%d\n", + sec->name, + (long)sec->header.sh_size, + (int) (2 * sizeof(void *)), + (long)sec->header.sh_addr, + a); + } +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL + /* Quick reference which section indicies are loaded. */ + + loaded = alloca(sizeof(int) * (i = f->header.e_shnum)); + while (--i >= 0) + loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0; + + /* Collect the symbols we'll be listing. */ + + for (nsyms = i = 0; i < HASH_BUCKETS; ++i) + for (sym = f->symtab[i]; sym; sym = sym->next) + if (sym->secidx <= SHN_HIRESERVE + && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx])) + ++nsyms; + + all = alloca(nsyms * sizeof(struct obj_symbol *)); + + for (i = 0, p = all; i < HASH_BUCKETS; ++i) + for (sym = f->symtab[i]; sym; sym = sym->next) + if (sym->secidx <= SHN_HIRESERVE + && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx])) + *p++ = sym; + + /* And list them. */ + printf("\nSymbols:\n"); + for (p = all; p < all + nsyms; ++p) { + char type = '?'; + unsigned long value; + + sym = *p; + if (sym->secidx == SHN_ABS) { + type = 'A'; + value = sym->value; + } else if (sym->secidx == SHN_UNDEF) { + type = 'U'; + value = 0; + } else { + sec = f->sections[sym->secidx]; + + if (sec->header.sh_type == SHT_NOBITS) + type = 'B'; + else if (sec->header.sh_flags & SHF_ALLOC) { + if (sec->header.sh_flags & SHF_EXECINSTR) + type = 'T'; + else if (sec->header.sh_flags & SHF_WRITE) + type = 'D'; + else + type = 'R'; + } + value = sym->value + sec->header.sh_addr; + } + + if (ELFW(ST_BIND) (sym->info) == STB_LOCAL) + type = tolower(type); + + printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value, + type, sym->name); + } +#endif +} + +#endif + +extern int insmod_main( int argc, char **argv) +{ + int opt; + int len; + int k_crcs; + char *tmp, *tmp1; + unsigned long m_size; + ElfW(Addr) m_addr; + struct obj_file *f; + struct stat st; + char *m_name = 0; + int exit_status = EXIT_FAILURE; + int m_has_modinfo; +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING + struct utsname uts_info; + char m_strversion[STRVERSIONLEN]; + int m_version, m_crcs; +#endif +#ifdef CONFIG_FEATURE_CLEAN_UP + FILE *fp = 0; +#else + FILE *fp; +#endif +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + int flag_print_load_map = 0; +#endif + int k_version = 0; + struct utsname myuname; + + /* Parse any options */ +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + while ((opt = getopt(argc, argv, "fkqsvxmLo:")) > 0) +#else + while ((opt = getopt(argc, argv, "fkqsvxLo:")) > 0) +#endif + { + switch (opt) { + case 'f': /* force loading */ + flag_force_load = 1; + break; + case 'k': /* module loaded by kerneld, auto-cleanable */ + flag_autoclean = 1; + break; + case 's': /* log to syslog */ + /* log to syslog -- not supported */ + /* but kernel needs this for request_module(), */ + /* as this calls: modprobe -k -s -- */ + /* so silently ignore this flag */ + break; + case 'v': /* verbose output */ + flag_verbose = 1; + break; + case 'q': /* silent */ + flag_quiet = 1; + break; + case 'x': /* do not export externs */ + flag_export = 0; + break; + case 'o': /* name the output module */ + free(m_name); + m_name = bb_xstrdup(optarg); + break; + case 'L': /* Stub warning */ + /* This is needed for compatibility with modprobe. + * In theory, this does locking, but we don't do + * that. So be careful and plan your life around not + * loading the same module 50 times concurrently. */ + break; +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + case 'm': /* print module load map */ + flag_print_load_map = 1; + break; +#endif + default: + bb_show_usage(); + } + } + + if (argv[optind] == NULL) { + bb_show_usage(); + } + + /* Grab the module name */ + tmp1 = bb_xstrdup(argv[optind]); + tmp = basename(tmp1); + len = strlen(tmp); + + if (uname(&myuname) == 0) { + if (myuname.release[0] == '2') { + k_version = myuname.release[2] - '0'; + } + } + +#if defined(CONFIG_FEATURE_2_6_MODULES) + if (k_version > 4 && len > 3 && tmp[len - 3] == '.' && + tmp[len - 2] == 'k' && tmp[len - 1] == 'o') { + len-=3; + tmp[len] = '\0'; + } + else +#endif + if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') { + len-=2; + tmp[len] = '\0'; + } + + +#if defined(CONFIG_FEATURE_2_6_MODULES) + if (k_version > 4) + bb_xasprintf(&m_fullName, "%s.ko", tmp); + else +#endif + bb_xasprintf(&m_fullName, "%s.o", tmp); + + if (!m_name) { + m_name = tmp; + } else { + free(tmp1); + tmp1 = 0; /* flag for free(m_name) before exit() */ + } + + /* Get a filedesc for the module. Check we we have a complete path */ + if (stat(argv[optind], &st) < 0 || !S_ISREG(st.st_mode) || + (fp = fopen(argv[optind], "r")) == NULL) { + /* Hmm. Could not open it. First search under /lib/modules/`uname -r`, + * but do not error out yet if we fail to find it... */ + if (k_version) { /* uname succeedd */ + char *module_dir; + char *tmdn; + char real_module_dir[FILENAME_MAX]; + + tmdn = concat_path_file(_PATH_MODULES, myuname.release); + /* Jump through hoops in case /lib/modules/`uname -r` + * is a symlink. We do not want recursive_action to + * follow symlinks, but we do want to follow the + * /lib/modules/`uname -r` dir, So resolve it ourselves + * if it is a link... */ + if (realpath (tmdn, real_module_dir) == NULL) + module_dir = tmdn; + else + module_dir = real_module_dir; + recursive_action(module_dir, TRUE, FALSE, FALSE, + check_module_name_match, 0, m_fullName); + free(tmdn); + } + + /* Check if we have found anything yet */ + if (m_filename == 0 || ((fp = fopen(m_filename, "r")) == NULL)) + { + char module_dir[FILENAME_MAX]; + + free(m_filename); + m_filename = 0; + if (realpath (_PATH_MODULES, module_dir) == NULL) + strcpy(module_dir, _PATH_MODULES); + /* No module found under /lib/modules/`uname -r`, this + * time cast the net a bit wider. Search /lib/modules/ */ + if (! recursive_action(module_dir, TRUE, FALSE, FALSE, + check_module_name_match, 0, m_fullName)) + { + if (m_filename == 0 + || ((fp = fopen(m_filename, "r")) == NULL)) + { + bb_error_msg("%s: no module by that name found", m_fullName); + goto out; + } + } else + bb_error_msg_and_die("%s: no module by that name found", m_fullName); + } + } else + m_filename = bb_xstrdup(argv[optind]); + + if (!flag_quiet) + printf("Using %s\n", m_filename); + +#ifdef CONFIG_FEATURE_2_6_MODULES + if (k_version > 4) + { + optind--; + argv[optind + 1] = m_filename; + return insmod_ng_main(argc - optind, argv + optind); + } +#endif + + if ((f = obj_load(fp, LOADBITS)) == NULL) + bb_perror_msg_and_die("Could not load the module"); + + if (get_modinfo_value(f, "kernel_version") == NULL) + m_has_modinfo = 0; + else + m_has_modinfo = 1; + +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING + /* Version correspondence? */ + if (!flag_quiet) { + if (uname(&uts_info) < 0) + uts_info.release[0] = '\0'; + if (m_has_modinfo) { + m_version = new_get_module_version(f, m_strversion); + if (m_version == -1) { + bb_error_msg("couldn't find the kernel version the module was " + "compiled for"); + goto out; + } + } + + if (strncmp(uts_info.release, m_strversion, STRVERSIONLEN) != 0) { + if (flag_force_load) { + bb_error_msg("Warning: kernel-module version mismatch\n" + "\t%s was compiled for kernel version %s\n" + "\twhile this kernel is version %s", + m_filename, m_strversion, uts_info.release); + } else { + bb_error_msg("kernel-module version mismatch\n" + "\t%s was compiled for kernel version %s\n" + "\twhile this kernel is version %s.", + m_filename, m_strversion, uts_info.release); + goto out; + } + } + } + k_crcs = 0; +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ + + if (!query_module(NULL, 0, NULL, 0, NULL)) { + if (!new_get_kernel_symbols()) + goto out; + k_crcs = new_is_kernel_checksummed(); + } else { + bb_error_msg("Not configured to support old kernels"); + goto out; + } + +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING + m_crcs = 0; + if (m_has_modinfo) + m_crcs = new_is_module_checksummed(f); + + if (m_crcs != k_crcs) + obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash); +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ + + /* Let the module know about the kernel symbols. */ + add_kernel_symbols(f); + + /* Allocate common symbols, symbol tables, and string tables. */ + + if (!new_create_this_module(f, m_name)) + { + goto out; + } + + if (!obj_check_undefineds(f)) { + goto out; + } + obj_allocate_commons(f); + check_tainted_module(f, m_name); + + /* done with the module name, on to the optional var=value arguments */ + ++optind; + + if (optind < argc) { + if (!new_process_module_arguments(f, argc - optind, argv + optind)) + { + goto out; + } + } + + arch_create_got(f); + hide_special_symbols(f); + +#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS + add_ksymoops_symbols(f, m_filename, m_name); +#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */ + + new_create_module_ksymtab(f); + + /* Find current size of the module */ + m_size = obj_load_size(f); + + + m_addr = create_module(m_name, m_size); + if (m_addr == -1) switch (errno) { + case EEXIST: + bb_error_msg("A module named %s already exists", m_name); + goto out; + case ENOMEM: + bb_error_msg("Can't allocate kernel memory for module; needed %lu bytes", + m_size); + goto out; + default: + bb_perror_msg("create_module: %s", m_name); + goto out; + } + +#if !LOADBITS + /* + * the PROGBITS section was not loaded by the obj_load + * now we can load them directly into the kernel memory + */ + if (!obj_load_progbits(fp, f, (char*)m_addr)) { + delete_module(m_name); + goto out; + } +#endif + + if (!obj_relocate(f, m_addr)) { + delete_module(m_name); + goto out; + } + + if (!new_init_module(m_name, f, m_size)) + { + delete_module(m_name); + goto out; + } + +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + if(flag_print_load_map) + print_load_map(f); +#endif + + exit_status = EXIT_SUCCESS; + +out: +#ifdef CONFIG_FEATURE_CLEAN_UP + if(fp) + fclose(fp); + if(tmp1) { + free(tmp1); + } else { + free(m_name); + } + free(m_filename); +#endif + return(exit_status); +} + + +#endif + + +#ifdef CONFIG_FEATURE_2_6_MODULES + +#include +#include +#include + +/* We use error numbers in a loose translation... */ +static const char *moderror(int err) +{ + switch (err) { + case ENOEXEC: + return "Invalid module format"; + case ENOENT: + return "Unknown symbol in module"; + case ESRCH: + return "Module has wrong symbol version"; + case EINVAL: + return "Invalid parameters"; + default: + return strerror(err); + } +} + +extern int insmod_ng_main( int argc, char **argv) +{ + int i; + int fd; + long int ret; + struct stat st; + unsigned long len; + void *map; + char *filename, *options = bb_xstrdup(""); + + filename = argv[1]; + if (!filename) { + bb_show_usage(); + return -1; + } + + /* Rest is options */ + for (i = 2; i < argc; i++) { + options = xrealloc(options, strlen(options) + 2 + strlen(argv[i]) + 2); + /* Spaces handled by "" pairs, but no way of escaping quotes */ + if (strchr(argv[i], ' ')) { + strcat(options, "\""); + strcat(options, argv[i]); + strcat(options, "\""); + } else { + strcat(options, argv[i]); + } + strcat(options, " "); + } + + if ((fd = open(filename, O_RDONLY, 0)) < 0) { + bb_perror_msg_and_die("cannot open module `%s'", filename); + } + + fstat(fd, &st); + len = st.st_size; + map = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) { + bb_perror_msg_and_die("cannot mmap `%s'", filename); + } + + ret = syscall(__NR_init_module, map, len, options); + if (ret != 0) { + bb_perror_msg_and_die("cannot insert `%s': %s (%li)", + filename, moderror(errno), ret); + } + + return 0; +} + +#endif diff --git a/busybox/modutils/lsmod.c b/busybox/modutils/lsmod.c new file mode 100644 index 000000000..7bf314afe --- /dev/null +++ b/busybox/modutils/lsmod.c @@ -0,0 +1,174 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini lsmod implementation for busybox + * + * Copyright (C) 1999-2004 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" + + +#ifndef CONFIG_FEATURE_CHECK_TAINTED_MODULE +static inline void check_tainted(void) { printf("\n"); } +#else +#define TAINT_FILENAME "/proc/sys/kernel/tainted" +#define TAINT_PROPRIETORY_MODULE (1<<0) +#define TAINT_FORCED_MODULE (1<<1) +#define TAINT_UNSAFE_SMP (1<<2) + +static void check_tainted(void) +{ + int tainted; + FILE *f; + + tainted = 0; + if ((f = fopen(TAINT_FILENAME, "r"))) { + fscanf(f, "%d", &tainted); + fclose(f); + } + if (f && tainted) { + printf(" Tainted: %c%c%c\n", + tainted & TAINT_PROPRIETORY_MODULE ? 'P' : 'G', + tainted & TAINT_FORCED_MODULE ? 'F' : ' ', + tainted & TAINT_UNSAFE_SMP ? 'S' : ' '); + } + else { + printf(" Not tainted\n"); + } +} +#endif + +#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE + +struct module_info +{ + unsigned long addr; + unsigned long size; + unsigned long flags; + long usecount; +}; + + +int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret); + +/* Values for query_module's which. */ +static const int QM_MODULES = 1; +static const int QM_DEPS = 2; +static const int QM_REFS = 3; +static const int QM_SYMBOLS = 4; +static const int QM_INFO = 5; + +/* Bits of module.flags. */ +static const int NEW_MOD_RUNNING = 1; +static const int NEW_MOD_DELETED = 2; +static const int NEW_MOD_AUTOCLEAN = 4; +static const int NEW_MOD_VISITED = 8; +static const int NEW_MOD_USED_ONCE = 16; +static const int NEW_MOD_INITIALIZING = 64; + +extern int lsmod_main(int argc, char **argv) +{ + struct module_info info; + char *module_names, *mn, *deps, *dn; + size_t bufsize, depsize, nmod, count, i, j; + + module_names = xmalloc(bufsize = 256); + if (my_query_module(NULL, QM_MODULES, (void **)&module_names, &bufsize, + &nmod)) { + bb_perror_msg_and_die("QM_MODULES"); + } + + deps = xmalloc(depsize = 256); + printf("Module Size Used by"); + check_tainted(); + + for (i = 0, mn = module_names; i < nmod; mn += strlen(mn) + 1, i++) { + if (query_module(mn, QM_INFO, &info, sizeof(info), &count)) { + if (errno == ENOENT) { + /* The module was removed out from underneath us. */ + continue; + } + /* else choke */ + bb_perror_msg_and_die("module %s: QM_INFO", mn); + } + if (my_query_module(mn, QM_REFS, (void **)&deps, &depsize, &count)) { + if (errno == ENOENT) { + /* The module was removed out from underneath us. */ + continue; + } + bb_perror_msg_and_die("module %s: QM_REFS", mn); + } + printf("%-20s%8lu%4ld", mn, info.size, info.usecount); + if (info.flags & NEW_MOD_DELETED) + printf(" (deleted)"); + else if (info.flags & NEW_MOD_INITIALIZING) + printf(" (initializing)"); + else if (!(info.flags & NEW_MOD_RUNNING)) + printf(" (uninitialized)"); + else { + if (info.flags & NEW_MOD_AUTOCLEAN) + printf(" (autoclean) "); + if (!(info.flags & NEW_MOD_USED_ONCE)) + printf(" (unused)"); + } + if (count) printf(" ["); + for (j = 0, dn = deps; j < count; dn += strlen(dn) + 1, j++) { + printf("%s%s", dn, (j==count-1)? "":" "); + } + if (count) printf("]"); + + printf("\n"); + } + +#ifdef CONFIG_FEATURE_CLEAN_UP + free(module_names); +#endif + + return( 0); +} + +#else /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */ + +extern int lsmod_main(int argc, char **argv) +{ + printf("Module Size Used by"); + check_tainted(); + + if (bb_xprint_file_by_name("/proc/modules") < 0) { + return 0; + } + return 1; +} + +#endif /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */ diff --git a/busybox/modutils/modprobe.c b/busybox/modutils/modprobe.c new file mode 100644 index 000000000..83244fca5 --- /dev/null +++ b/busybox/modutils/modprobe.c @@ -0,0 +1,654 @@ +/* vi: set sw=4 ts=4: */ +/* + * Modprobe written from scratch for BusyBox + * + * Copyright (c) 2002 by Robert Griebl, griebl@gmx.de + * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + + +struct dep_t { + char * m_name; + char * m_path; + char * m_options; + + int m_isalias : 1; + int m_reserved : 15; + + int m_depcnt : 16; + char ** m_deparr; + + struct dep_t * m_next; +}; + +struct mod_list_t { + char * m_name; + char * m_path; + char * m_options; + + struct mod_list_t * m_prev; + struct mod_list_t * m_next; +}; + + +static struct dep_t *depend; +static int autoclean, show_only, quiet, do_syslog, verbose; +static int k_version; + +int parse_tag_value ( char *buffer, char **ptag, char **pvalue ) +{ + char *tag, *value; + + while ( isspace ( *buffer )) + buffer++; + tag = value = buffer; + while ( !isspace ( *value )) + if (!*value) return 0; + else value++; + *value++ = 0; + while ( isspace ( *value )) + value++; + if (!*value) return 0; + + *ptag = tag; + *pvalue = value; + + return 1; +} + +/* Jump through hoops to simulate how fgets() grabs just one line at a + * time... Don't use any stdio since modprobe gets called from a kernel + * thread and stdio junk can overflow the limited stack... + */ +static char *reads ( int fd, char *buffer, size_t len ) +{ + int n = read ( fd, buffer, len ); + + if ( n > 0 ) { + char *p; + + buffer [len-1] = 0; + p = strchr ( buffer, '\n' ); + + if ( p ) { + off_t offset; + + offset = lseek ( fd, 0L, SEEK_CUR ); // Get the current file descriptor offset + lseek ( fd, offset-n + (p-buffer) + 1, SEEK_SET ); // Set the file descriptor offset to right after the \n + + p[1] = 0; + } + return buffer; + } + + else + return 0; +} + +static struct dep_t *build_dep ( void ) +{ + int fd; + struct utsname un; + struct dep_t *first = 0; + struct dep_t *current = 0; + char buffer[2048]; + char *filename = buffer; + int continuation_line = 0; + + k_version = 0; + if ( uname ( &un )) + return 0; + + // check for buffer overflow in following code + if ( bb_strlen ( un.release ) > ( sizeof( buffer ) - 64 )) { + return 0; + } + if (un.release[0] == '2') { + k_version = un.release[2] - '0'; + } + + strcpy ( filename, "/lib/modules/" ); + strcat ( filename, un.release ); + strcat ( filename, "/modules.dep" ); + + if (( fd = open ( filename, O_RDONLY )) < 0 ) { + + /* Ok, that didn't work. Fall back to looking in /lib/modules */ + if (( fd = open ( "/lib/modules/modules.dep", O_RDONLY )) < 0 ) { + return 0; + } + } + + while ( reads ( fd, buffer, sizeof( buffer ))) { + int l = bb_strlen ( buffer ); + char *p = 0; + + while ( isspace ( buffer [l-1] )) { + buffer [l-1] = 0; + l--; + } + + if ( l == 0 ) { + continuation_line = 0; + continue; + } + + if ( !continuation_line ) { + char *col = strchr ( buffer, ':' ); + char *dot = col; + + if ( col ) { + char *mods; + char *modpath; + char *mod; + + *col = 0; + mods = strrchr ( buffer, '/' ); + + if ( !mods ) + mods = buffer; + else + mods++; + + modpath = strchr ( buffer, '/' ); + if ( !modpath ) + modpath = buffer; +#if defined(CONFIG_FEATURE_2_6_MODULES) + if ((k_version > 4) && ( *(col-3) == '.' ) && + ( *(col-2) == 'k' ) && ( *(col-1) == 'o' )) + dot = col - 3; + else +#endif + if (( *(col-2) == '.' ) && ( *(col-1) == 'o' )) + dot = col - 2; + + mod = bb_xstrndup ( mods, dot - mods ); + + if ( !current ) { + first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t )); + } + else { + current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t )); + current = current-> m_next; + } + current-> m_name = mod; + current-> m_path = bb_xstrdup(modpath); + current-> m_options = 0; + current-> m_isalias = 0; + current-> m_depcnt = 0; + current-> m_deparr = 0; + current-> m_next = 0; + + //printf ( "%s:\n", mod ); + p = col + 1; + } + else + p = 0; + } + else + p = buffer; + + while ( p && *p && isblank(*p)) + p++; + + if ( p && *p ) { + char *end = &buffer [l-1]; + char *deps; + char *dep; + char *next; + int ext = 0; + + while ( isblank ( *end ) || ( *end == '\\' )) + end--; + + do + { + next = strchr (p, ' ' ); + if (next) + { + *next = 0; + next--; + } + else + next = end; + + deps = strrchr ( p, '/' ); + + if ( !deps || ( deps < p )) { + deps = p; + + while ( isblank ( *deps )) + deps++; + } + else + deps++; + +#if defined(CONFIG_FEATURE_2_6_MODULES) + if ((k_version > 4) && ( *(next-2) == '.' ) && *(next-1) == 'k' && + ( *next == 'o' )) + ext = 3; + else +#endif + if (( *(next-1) == '.' ) && ( *next == 'o' )) + ext = 2; + + /* Cope with blank lines */ + if ((next-deps-ext+1) <= 0) + continue; + dep = bb_xstrndup ( deps, next - deps - ext + 1 ); + + current-> m_depcnt++; + current-> m_deparr = (char **) xrealloc ( current-> m_deparr, + sizeof ( char *) * current-> m_depcnt ); + current-> m_deparr [current-> m_depcnt - 1] = dep; + + //printf ( " %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] ); + p = next + 2; + } while (next < end); + } + + if ( buffer [l-1] == '\\' ) + continuation_line = 1; + else + continuation_line = 0; + } + close ( fd ); + + // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) ! + +#if defined(CONFIG_FEATURE_2_6_MODULES) + if (( fd = open ( "/etc/modprobe.conf", O_RDONLY )) < 0 ) +#endif + if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 ) + if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 ) + return first; + + continuation_line = 0; + while ( reads ( fd, buffer, sizeof( buffer ))) { + int l; + char *p; + + p = strchr ( buffer, '#' ); + if ( p ) + *p = 0; + + l = bb_strlen ( buffer ); + + while ( l && isspace ( buffer [l-1] )) { + buffer [l-1] = 0; + l--; + } + + if ( l == 0 ) { + continuation_line = 0; + continue; + } + + if ( !continuation_line ) { + if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) { + char *alias, *mod; + + if ( parse_tag_value ( buffer + 6, &alias, &mod )) { + // fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", alias, mod ); + + if ( !current ) { + first = current = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t )); + } + else { + current-> m_next = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t )); + current = current-> m_next; + } + current-> m_name = bb_xstrdup ( alias ); + current-> m_isalias = 1; + + if (( strcmp ( mod, "off" ) == 0 ) || ( strcmp ( mod, "null" ) == 0 )) { + current-> m_depcnt = 0; + current-> m_deparr = 0; + } + else { + current-> m_depcnt = 1; + current-> m_deparr = xmalloc ( 1 * sizeof( char * )); + current-> m_deparr[0] = bb_xstrdup ( mod ); + } + current-> m_next = 0; + } + } + else if (( strncmp ( buffer, "options", 7 ) == 0 ) && isspace ( buffer [7] )) { + char *mod, *opt; + + if ( parse_tag_value ( buffer + 8, &mod, &opt )) { + struct dep_t *dt; + + for ( dt = first; dt; dt = dt-> m_next ) { + if ( strcmp ( dt-> m_name, mod ) == 0 ) + break; + } + if ( dt ) { + dt-> m_options = xrealloc ( dt-> m_options, bb_strlen( opt ) + 1 ); + strcpy ( dt-> m_options, opt ); + + // fprintf ( stderr, "OPTION: '%s' -> '%s'\n", dt-> m_name, dt-> m_options ); + } + } + } + } + } + close ( fd ); + + return first; +} + +/* return 1 = loaded, 0 = not loaded, -1 = can't tell */ +static int already_loaded (const char *name) +{ + int fd; + char buffer[4096]; + + fd = open ("/proc/modules", O_RDONLY); + if (fd < 0) + return -1; + + while ( reads ( fd, buffer, sizeof( buffer ))) { + char *p; + + p = strchr (buffer, ' '); + if (p) { + *p = 0; + if (strcmp (name, buffer) == 0) { + close (fd); + return 1; + } + } + } + + close (fd); + return 0; +} + +static int mod_process ( struct mod_list_t *list, int do_insert ) +{ + char lcmd [4096]; + int rc = 0; + + while ( list ) { + *lcmd = '\0'; + if ( do_insert ) { + if (already_loaded (list->m_name) != 1) + snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s %s", + do_syslog ? "-s" : "", autoclean ? "-k" : "", + quiet ? "-q" : "", list-> m_path, list-> m_options ? + list-> m_options : "" ); + } else { + /* modutils uses short name for removal */ + if (already_loaded (list->m_name) != 0) + snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s", + do_syslog ? "-s" : "", list-> m_name ); + } + + if (*lcmd) { + if (verbose) { + printf("%s\n", lcmd); + } + if (!show_only) { + int rc2 = system(lcmd); + if (do_insert) { + rc = rc2; /* only last module matters */ + } + else if (!rc2) { + rc = 0; /* success if remove any mod */ + } + } + } + list = do_insert ? list-> m_prev : list-> m_next; + } + return (show_only) ? 0 : rc; +} + +static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail ) +{ + struct mod_list_t *find; + struct dep_t *dt; + char *opt = 0; + char *path = 0; + + // check dependencies + for ( dt = depend; dt; dt = dt-> m_next ) { + if ( strcmp ( dt-> m_name, mod ) == 0) { + mod = dt-> m_name; + path = dt-> m_path; + opt = dt-> m_options; + break; + } + } + + // resolve alias names + while ( dt && dt-> m_isalias ) { + if ( dt-> m_depcnt == 1 ) { + struct dep_t *adt; + + for ( adt = depend; adt; adt = adt-> m_next ) { + if ( strcmp ( adt-> m_name, dt-> m_deparr [0] ) == 0 ) + break; + } + if ( adt ) { + dt = adt; + mod = dt-> m_name; + path = dt-> m_path; + if ( !opt ) + opt = dt-> m_options; + } + else + return; + } + else + return; + } + + if ( !path ) { + bb_error_msg ("module %s not found.", mod); + return; + } + + // search for duplicates + for ( find = *head; find; find = find-> m_next ) { + if ( !strcmp ( mod, find-> m_name )) { + // found -> dequeue it + + if ( find-> m_prev ) + find-> m_prev-> m_next = find-> m_next; + else + *head = find-> m_next; + + if ( find-> m_next ) + find-> m_next-> m_prev = find-> m_prev; + else + *tail = find-> m_prev; + + break; // there can be only one duplicate + } + } + + if ( !find ) { // did not find a duplicate + find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t)); + find-> m_name = mod; + find-> m_path = path; + find-> m_options = opt; + } + + // enqueue at tail + if ( *tail ) + (*tail)-> m_next = find; + find-> m_prev = *tail; + find-> m_next = 0; + + if ( !*head ) + *head = find; + *tail = find; + + if ( dt ) { + int i; + + for ( i = 0; i < dt-> m_depcnt; i++ ) + check_dep ( dt-> m_deparr [i], head, tail ); + } +} + + + +static int mod_insert ( char *mod, int argc, char **argv ) +{ + struct mod_list_t *tail = 0; + struct mod_list_t *head = 0; + int rc; + + // get dep list for module mod + check_dep ( mod, &head, &tail ); + + if ( head && tail ) { +#if defined(CONFIG_FEATURE_2_6_MODULES) + if ( argc ) { + int i; + int l = 0; + + // append module args + for ( i = 0; i < argc; i++ ) + l += ( bb_strlen ( argv [i] ) + 1 ); + + head-> m_options = xrealloc ( head-> m_options, l + 1 ); + head-> m_options [0] = 0; + + for ( i = 0; i < argc; i++ ) { + strcat ( head-> m_options, argv [i] ); + strcat ( head-> m_options, " " ); + } + } +#endif + + // process tail ---> head + rc = mod_process ( tail, 1 ); + } + else + rc = 1; + + return rc; +} + +static int mod_remove ( char *mod ) +{ + int rc; + static struct mod_list_t rm_a_dummy = { "-a", 0, 0 }; + + struct mod_list_t *head = 0; + struct mod_list_t *tail = 0; + + if ( mod ) + check_dep ( mod, &head, &tail ); + else // autoclean + head = tail = &rm_a_dummy; + + if ( head && tail ) + rc = mod_process ( head, 0 ); // process head ---> tail + else + rc = 1; + return rc; + +} + +extern int modprobe_main(int argc, char** argv) +{ + int opt; + int remove_opt = 0; + + autoclean = show_only = quiet = do_syslog = verbose = 0; + + while ((opt = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) { + switch(opt) { + case 'c': // no config used + case 'l': // no pattern matching + return EXIT_SUCCESS; + break; + case 'C': // no config used + case 't': // no pattern matching + bb_error_msg_and_die("-t and -C not supported"); + + case 'a': // ignore + case 'd': // ignore + break; + case 'k': + autoclean++; + break; + case 'n': + show_only++; + break; + case 'q': + quiet++; + break; + case 'r': + remove_opt++; + break; + case 's': + do_syslog++; + break; + case 'v': + verbose++; + break; + case 'V': + default: + bb_show_usage(); + break; + } + } + + depend = build_dep ( ); + + if ( !depend ) + bb_error_msg_and_die ( "could not parse modules.dep\n" ); + + if (remove_opt) { + int rc = EXIT_SUCCESS; + do { + if (mod_remove ( optind < argc ? + bb_xstrdup (argv [optind]) : NULL )) { + bb_error_msg ("failed to remove module %s", + argv [optind] ); + rc = EXIT_FAILURE; + } + } while ( ++optind < argc ); + + return rc; + } + + if (optind >= argc) + bb_error_msg_and_die ( "No module or pattern provided\n" ); + + if ( mod_insert ( bb_xstrdup ( argv [optind] ), argc - optind - 1, argv + optind + 1 )) + bb_error_msg_and_die ( "failed to load module %s", argv [optind] ); + + return EXIT_SUCCESS; +} diff --git a/busybox/modutils/rmmod.c b/busybox/modutils/rmmod.c new file mode 100644 index 000000000..f4e65d0ce --- /dev/null +++ b/busybox/modutils/rmmod.c @@ -0,0 +1,124 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rmmod implementation for busybox + * + * Copyright (C) 1999-2004 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" + +#ifdef CONFIG_FEATURE_2_6_MODULES +static inline void filename2modname(char *modname, const char *filename) +{ + const char *afterslash; + unsigned int i; + + afterslash = strrchr(filename, '/'); + if (!afterslash) + afterslash = filename; + else + afterslash++; + + /* Convert to underscores, stop at first . */ + for (i = 0; afterslash[i] && afterslash[i] != '.'; i++) { + if (afterslash[i] == '-') + modname[i] = '_'; + else + modname[i] = afterslash[i]; + } + modname[i] = '\0'; +} +#endif + +extern int rmmod_main(int argc, char **argv) +{ + int n, ret = EXIT_SUCCESS; + size_t nmod = 0; /* number of modules */ + size_t pnmod = -1; /* previous number of modules */ + unsigned int flags = O_NONBLOCK|O_EXCL; +#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE + void *buf; /* hold the module names which we ignore but must get */ + size_t bufsize = 0; +#endif + + /* Parse command line. */ + while ((n = getopt(argc, argv, "a")) != EOF) { + switch (n) { + case 'w': // --wait + flags &= ~O_NONBLOCK; + break; + case 'f': // --force + flags |= O_TRUNC; + break; + case 'a': + /* Unload _all_ unused modules via NULL delete_module() call */ + /* until the number of modules does not change */ +#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE + buf = xmalloc(bufsize = 256); +#endif + while (nmod != pnmod) { + if (syscall(__NR_delete_module, NULL, flags) < 0) { + if (errno==EFAULT) + return(ret); + bb_perror_msg_and_die("rmmod"); + } + pnmod = nmod; +#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE + /* 1 == QM_MODULES */ + if (my_query_module(NULL, 1, &buf, &bufsize, &nmod)) { + bb_perror_msg_and_die("QM_MODULES"); + } +#endif + } +#if defined CONFIG_FEATURE_CLEAN_UP && CONFIG_FEATURE_QUERY_MODULE_INTERFACE + free(buf); +#endif + return EXIT_SUCCESS; + default: + bb_show_usage(); + } + } + + if (optind == argc) + bb_show_usage(); + + { + for (n = optind; n < argc; n++) { +#ifdef CONFIG_FEATURE_2_6_MODULES + char module_name[strlen(argv[n]) + 1]; + filename2modname(module_name, argv[n]); +#else +#define module_name argv[n] +#endif + if (syscall(__NR_delete_module, module_name, flags) < 0) { + bb_perror_msg("%s", argv[n]); + ret = EXIT_FAILURE; + } + } + } + + return(ret); +} diff --git a/busybox/networking/Config.in b/busybox/networking/Config.in new file mode 100644 index 000000000..42176f050 --- /dev/null +++ b/busybox/networking/Config.in @@ -0,0 +1,634 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Networking Utilities" + +config CONFIG_FEATURE_IPV6 + bool "Enable IPv6 support" + default n + help + Enable IPv6 support to busybox. This makes applets that talk IP + able to work with IPv6. + +config CONFIG_ARPING + bool "arping" + default n + help + Ping hosts by ARP packets + +config CONFIG_FTPGET + bool "ftpget" + default n + help + Retrieve a remote file via FTP. + +config CONFIG_FTPPUT + bool "ftpput" + default n + help + Store a remote file via FTP. + +config CONFIG_HOSTNAME + bool "hostname" + default n + help + Show or set the system's host name + +config CONFIG_HTTPD + bool "httpd" + default n + help + Serve web pages via an HTTP server. + +config CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + bool " Support using httpd only from inetd" + default n + depends on CONFIG_HTTPD + help + This option disables uid and port options for the httpd applet + but requires inetd server daemon. + +config CONFIG_FEATURE_HTTPD_BASIC_AUTH + bool " Enable Basic http Authentication" + default y + depends on CONFIG_HTTPD + help + Utilizes password settings from /etc/httpd.conf for basic + authentication on a per url basis. + +config CONFIG_FEATURE_HTTPD_AUTH_MD5 + bool " Support MD5 crypted passwords for http Authentication" + default n + depends on CONFIG_FEATURE_HTTPD_BASIC_AUTH + help + Enables basic per url authentication from /etc/httpd.conf + using md5 passwords. + + +if !CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY +config CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP + bool " Support reloading the global config file using hup signal" + default n + depends on CONFIG_HTTPD + help + This option enables processing of SIGHUP to reload cached + configuration settings. + +config CONFIG_FEATURE_HTTPD_SETUID + bool " Enable support -u option" + default n + depends on CONFIG_HTTPD + help + This option allows the server to run as a specific user + rather than defaulting to the user that starts the server. + Use of this option requires special privileges to change to a + different user. +endif + +config CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + bool " Support loading additional MIME types at run-time" + default n + depends on CONFIG_HTTPD + help + This option enables support for additional MIME types at + run-time to be specified in the configuration file. + +config CONFIG_FEATURE_HTTPD_CGI + bool " Support Common Gateway Interface (CGI)" + default y + depends on CONFIG_HTTPD + help + This option allows scripts and executables to be invoked + when specific urls are requested. + +config CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV + bool " Support the REMOTE_PORT environment variable for CGI" + default n + depends on CONFIG_FEATURE_HTTPD_CGI + help + Use of this option can assist scripts in generating + references that contain a unique port number. + +config CONFIG_FEATURE_HTTPD_ENCODE_URL_STR + bool " Enable the -e option for shell script CGI simplification." + default y + depends on CONFIG_HTTPD + help + After set, this option allows html encoding arbitrary + strings for display of the browser. Output goes to stdout. + For example, httpd -e "" as + "<Hello World>". + +config CONFIG_IFCONFIG + bool "ifconfig" + default n + help + Ifconfig is used to configure the kernel-resident network interfaces. + +config CONFIG_FEATURE_IFCONFIG_STATUS + bool " Enable status reporting output (+7k)" + default y + depends on CONFIG_IFCONFIG + help + If ifconfig is called with no arguments it will display the status + of the currently active interfaces. + +config CONFIG_FEATURE_IFCONFIG_SLIP + bool " Enable slip-specific options \"keepalive\" and \"outfill\"" + default n + depends on CONFIG_IFCONFIG + help + Allow "keepalive" and "outfill" support for SLIP. If you're not + planning on using serial lines, leave this unchecked. + +config CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + bool " Enable options \"mem_start\", \"io_addr\", and \"irq\"" + default n + depends on CONFIG_IFCONFIG + help + Allow the start address for shared memory, start address for I/O, + and/or the interrupt line used by the specified device. + +config CONFIG_FEATURE_IFCONFIG_HW + bool " Enable option \"hw\" (ether only)" + default y + depends on CONFIG_IFCONFIG + help + Set the hardware address of this interface, if the device driver + supports this operation. Currently, we only support the 'ether' + class. + +config CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + bool " Set the broadcast automatically" + default n + depends on CONFIG_IFCONFIG + help + Setting this will make ifconfig attempt to find the broadcast + automatically if the value '+' is used. + +config CONFIG_IFUPDOWN + bool "ifupdown" + default n + help + Activate or deactivate the specified interfaces. This applet makes + use of either "ifconfig" and "route" or the "ip" command to actually + configure network interfaces. Therefore, you will probably also want + to enable either CONFIG_IFCONFIG and CONFIG_ROUTE, or enable + CONFIG_FEATURE_IFUPDOWN_IP and the various CONFIG_IP options. Of + course you could use non-busybox versions of these programs, so + against my better judgement (since this will surely result in plenty + of support questions on the mailing list), I do not force you to + enable these additional options. It is up to you to supply either + "ifconfig" and "route" or the "ip" command, either via busybox or via + standalone utilities. + +config CONFIG_FEATURE_IFUPDOWN_IP + bool " Use ip applet" + default n + depends on CONFIG_IFUPDOWN + help + Use the iproute "ip" command to implement "ifup" and "ifdown", rather + than the default of using the older 'ifconfig' and 'route' utilities. + +config CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN + bool " Use busybox ip applet" + default y + depends on CONFIG_FEATURE_IFUPDOWN_IP + select CONFIG_IP + select CONFIG_FEATURE_IP_ADDRESS + select CONFIG_FEATURE_IP_LINK + select CONFIG_FEATURE_IP_ROUTE + help + Use the busybox iproute "ip" applet to implement "ifupdown". + + If leave this disabled, you must install the full-blown iproute2 + utility or the "ifup" and "ifdown" applets will not work. + +config CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN + bool " Use busybox ifconfig and route applets" + default y + depends on CONFIG_IFUPDOWN && !CONFIG_FEATURE_IFUPDOWN_IP + select CONFIG_IFCONFIG + select CONFIG_ROUTE + help + Use the busybox iproute "ifconfig" and "route" applets to + implement the "ifup" and "ifdown" utilities. + + If leave this disabled, you must install the full-blown ifconfig + and route utilities, or the "ifup" and "ifdown" applets will not + work. + +config CONFIG_FEATURE_IFUPDOWN_IPV4 + bool " Enable support for IPv4" + default y + depends on CONFIG_IFUPDOWN + help + If you want busybox to talk IPv4, leave this on. + +config CONFIG_FEATURE_IFUPDOWN_IPV6 + bool " Enable support for IPv6" + default n + depends on CONFIG_IFUPDOWN + help + If you need support for IPv6, turn this option on. + +config CONFIG_FEATURE_IFUPDOWN_IPX + bool " Enable support for IPX" + default n + depends on CONFIG_IFUPDOWN + help + If this option is selected you can use busybox to work with IPX + networks. + +config CONFIG_FEATURE_IFUPDOWN_MAPPING + bool " Enable mapping support" + default n + depends on CONFIG_IFUPDOWN + help + This enables support for the "mapping" stanza, unless you have + a weird network setup you don't need it. + +config CONFIG_INETD + bool "inetd" + default n + help + Internet superserver daemon + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO + bool " Support echo service" + default y + depends on CONFIG_INETD + help + Echo received data internal inetd service + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD + bool " Support discard service" + default y + depends on CONFIG_INETD + help + Internet /dev/null internal inetd service + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME + bool " Support time service" + default y + depends on CONFIG_INETD + help + Return 32 bit time since 1900 internal inetd service + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME + bool " Support daytime service" + default y + depends on CONFIG_INETD + help + Return human-readable time internal inetd service + +config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN + bool " Support chargen service" + default y + depends on CONFIG_INETD + help + Familiar character generator internal inetd service + + +config CONFIG_IP + bool "ip" + default n + help + The "ip" applet is a TCP/IP interface configuration and routing + utility. You generally don't need "ip" to use busybox with + TCP/IP. + +if CONFIG_IP && CONFIG_IPADDR + config CONFIG_FEATURE_IP_ADDRESS + default y + comment " address (forced enabled for ipaddr)" +endif +if ! (CONFIG_IP && CONFIG_IPADDR) + config CONFIG_FEATURE_IP_ADDRESS + bool " address" + default y + depends on CONFIG_IP + help + Address manipulation support for the "ip" applet. +endif + +if CONFIG_IP && CONFIG_IPLINK + config CONFIG_FEATURE_IP_LINK + default y + comment " link (forced enabled for iplink)" +endif +if !(CONFIG_IP && CONFIG_IPLINK) + config CONFIG_FEATURE_IP_LINK + bool " link" + default y + depends on CONFIG_IP + help + Configure network devices with "ip". +endif + +if CONFIG_IP && CONFIG_IPROUTE + config CONFIG_FEATURE_IP_ROUTE + default y + comment " route (forced enabled for iproute)" +endif +if !(CONFIG_IP && CONFIG_IPROUTE) + config CONFIG_FEATURE_IP_ROUTE + bool " route" + default y + depends on CONFIG_IP + help + Add support for routing table management to "ip". +endif + +if CONFIG_IP && CONFIG_IPTUNNEL + config CONFIG_FEATURE_IP_TUNNEL + default y + comment " tunnel (forced enabled for iptunnel)" +endif +if !(CONFIG_IP && CONFIG_IPTUNNEL) + config CONFIG_FEATURE_IP_TUNNEL + bool " tunnel" + default n + depends on CONFIG_IP + help + Add support for tunneling commands to "ip". +endif + +config CONFIG_IPCALC + bool "ipcalc" + default n + help + ipcalc takes an IP address and netmask and calculates the + resulting broadcast, network, and host range. + +config CONFIG_FEATURE_IPCALC_FANCY + bool " Fancy IPCALC, more options, adds 1 kbyte" + default y + depends on CONFIG_IPCALC + help + Adds the options hostname, prefix and silent to the output of "ipcalc". + +config CONFIG_IPADDR + bool "ipaddr" + default n + help + Equivalent to selecting address support to "ip", above. + +config CONFIG_IPLINK + bool "iplink" + default n + help + Equivalent to selecting link support to "ip", above. + +config CONFIG_IPROUTE + bool "iproute" + default n + help + Equivalent to selecting route support to "ip", above. + +config CONFIG_IPTUNNEL + bool "iptunnel" + default n + help + Equivalent to selecting tunnel support to "ip", above. + +config CONFIG_NAMEIF + bool "nameif" + default n + help + nameif is used to rename network interface by its MAC address. + Renamed interfaces MUST be in the down state. + It is possible to use a file (default: /etc/mactab) + with list of new interface names and MACs. + Maximum interface name length: IF_NAMESIZE = 16 + File fields are separated by space or tab. + File format: + # Comment + new_interface_name XX:XX:XX:XX:XX:XX + +config CONFIG_NC + bool "nc" + default n + help + A simple Unix utility which reads and writes data across network + connections. + +config CONFIG_NETSTAT + bool "netstat" + default n + help + netstat prints information about the Linux networking subsystem. + +config CONFIG_NSLOOKUP + bool "nslookup" + default n + help + nslookup is a tool to query Internet name servers. + +config CONFIG_PING + bool "ping" + default n + help + ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to + elicit an ICMP ECHO_RESPONSE from a host or gateway. + +config CONFIG_FEATURE_FANCY_PING + bool " Enable fancy ping output" + default y + depends on CONFIG_PING + help + Make the output from the ping applet include statistics, and at the + same time provide full support for ICMP packets. + +config CONFIG_PING6 + bool "ping6" + default n + depends on CONFIG_FEATURE_IPV6 + help + This will give you a ping that can talk IPv6. + +config CONFIG_FEATURE_FANCY_PING6 + bool " Enable fancy ping6 output" + default y + depends on CONFIG_PING6 + help + Make the output from the ping6 applet include statistics, and at the + same time provide full support for ICMP packets. + +config CONFIG_ROUTE + bool "route" + default n + help + Route displays or manipulates the kernel's IP routing tables. + +config CONFIG_TELNET + bool "telnet" + default n + help + Telnet is an interface to the TELNET protocol, but is also commonly + used to test other simple protocols. + +config CONFIG_FEATURE_TELNET_TTYPE + bool " Pass TERM type to remote host" + default y + depends on CONFIG_TELNET + help + Setting this option will forward the TERM environment variable to the + remote host you are connecting to. This is useful to make sure that + things like ANSI colors and other control sequences behave. + +config CONFIG_FEATURE_TELNET_AUTOLOGIN + bool " Pass USER type to remote host" + default y + depends on CONFIG_TELNET + help + Setting this option will forward the USER environment variable to the + remote host you are connecting to. This is useful when you need to + log into a machine without telling the username (autologin). This + option enables `-a' and `-l USER' arguments. + +config CONFIG_TELNETD + bool "telnetd" + default n + select CONFIG_LOGIN + help + A daemon for the TELNET protocol, allowing you to log onto the host + running the daemon. Please keep in mind that the TELNET protocol + sends passwords in plain text. If you can't afford the space for an + SSH daemon and you trust your network, you may say 'y' here. As a + more secure alternative, you should seriously consider installing the + very small Dropbear SSH daemon instead: + http://matt.ucc.asn.au/dropbear/dropbear.html + + Note that for busybox telnetd to work you need several things: + First of all, your kernel needs: + CONFIG_UNIX98_PTYS=y + CONFIG_DEVPTS_FS=y + + Next, you need a /dev/pts directory on your root filesystem: + + $ ls -ld /dev/pts + drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/ + + Next you need the pseudo terminal master multiplexer /dev/ptmx: + + $ ls -la /dev/ptmx + crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx + + Any /dev/ttyp[0-9]* files you may have can be removed. + Next, you need to mount the devpts filesystem on /dev/pts using: + + mount -t devpts devpts /dev/pts + + You need to be sure that Busybox has CONFIG_LOGIN and + CONFIG_FEATURE_SUID enabled. And finally, you should make + certain that Busybox has been installed setuid root: + + chown root.root /bin/busybox + chmod 4755 /bin/busybox + + with all that done, telnetd _should_ work.... + + +config CONFIG_FEATURE_TELNETD_INETD + bool " Support call from inetd only" + default n + depends on CONFIG_TELNETD + help + Selecting this will make telnetd only callable from inetd, + removing the standalone support. + +config CONFIG_TFTP + bool "tftp" + default n + help + This enables the Trivial File Transfer Protocol client program. TFTP + is usually used for simple, small transfers such as a root image + for a network-enabled bootloader. + +config CONFIG_FEATURE_TFTP_GET + bool " Enable \"get\" command" + default y + depends on CONFIG_TFTP + help + Add support for the GET command within the TFTP client. This allows + a client to retrieve a file from a TFTP server. + +config CONFIG_FEATURE_TFTP_PUT + bool " Enable \"put\" command" + default y + depends on CONFIG_TFTP + help + Add support for the PUT command within the TFTP client. This allows + a client to transfer a file to a TFTP server. + +config CONFIG_FEATURE_TFTP_BLOCKSIZE + bool " Enable \"blocksize\" command" + default n + depends on CONFIG_TFTP + help + Allow the client to specify the desired block size for transfers. + +config CONFIG_FEATURE_TFTP_DEBUG + bool " Enable debug" + default n + depends on CONFIG_TFTP + help + Enable debug settings for tftp. This is useful if you're running + into problems with tftp as the protocol doesn't help you much when + you run into problems. + +config CONFIG_TRACEROUTE + bool "traceroute" + default n + help + Utility to trace the route of IP packets + +config CONFIG_FEATURE_TRACEROUTE_VERBOSE + bool " Enable verbose output" + default n + depends on CONFIG_TRACEROUTE + help + Add some verbosity to traceroute. This includes amongst other things + hostnames and ICMP response types. + +config CONFIG_VCONFIG + bool "vconfig" + default n + help + Creates, removes, and configures VLAN interfaces + +config CONFIG_WGET + bool "wget" + default n + help + wget is a utility for non-interactive download of files from HTTP, + HTTPS, and FTP servers. + +config CONFIG_FEATURE_WGET_STATUSBAR + bool " Enable a nifty process meter (+2k)" + default y + depends on CONFIG_WGET + help + Enable the transfer progress bar for wget transfers. + +config CONFIG_FEATURE_WGET_AUTHENTICATION + bool " Enable HTTP authentication" + default y + depends on CONFIG_WGET + help + Support authenticated HTTP transfers. + +config CONFIG_FEATURE_WGET_IP6_LITERAL + bool " Enable IPv6 literal addresses" + default y + depends on CONFIG_WGET + help + Support IPv6 address literal notation in URLs. + +source networking/udhcp/Config.in + +endmenu + diff --git a/busybox/networking/Makefile b/busybox/networking/Makefile new file mode 100644 index 000000000..91726b1b2 --- /dev/null +++ b/busybox/networking/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/networking +NETWORKING_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/networking/Makefile.in b/busybox/networking/Makefile.in new file mode 100644 index 000000000..9bfe90176 --- /dev/null +++ b/busybox/networking/Makefile.in @@ -0,0 +1,68 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +NETWORKING_AR:=networking.a +ifndef $(NETWORKING_DIR) +NETWORKING_DIR:=$(top_builddir)/networking/ +endif +srcdir=$(top_srcdir)/networking +NETWORKING-y:= +NETWORKING-$(CONFIG_ARPING) += arping.o +NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o +NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o +NETWORKING-$(CONFIG_HOSTNAME) += hostname.o +NETWORKING-$(CONFIG_HTTPD) += httpd.o +NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o +NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o +NETWORKING-$(CONFIG_INETD) += inetd.o +NETWORKING-$(CONFIG_IP) += ip.o +NETWORKING-$(CONFIG_IPCALC) += ipcalc.o +NETWORKING-$(CONFIG_IPADDR) += ipaddr.o +NETWORKING-$(CONFIG_IPLINK) += iplink.o +NETWORKING-$(CONFIG_IPROUTE) += iproute.o +NETWORKING-$(CONFIG_IPTUNNEL) += iptunnel.o +NETWORKING-$(CONFIG_NAMEIF) += nameif.o +NETWORKING-$(CONFIG_NC) += nc.o +NETWORKING-$(CONFIG_NETSTAT) += netstat.o +NETWORKING-$(CONFIG_NSLOOKUP) += nslookup.o +NETWORKING-$(CONFIG_PING) += ping.o +NETWORKING-$(CONFIG_PING6) += ping6.o +NETWORKING-$(CONFIG_ROUTE) += route.o +NETWORKING-$(CONFIG_TELNET) += telnet.o +NETWORKING-$(CONFIG_TELNETD) += telnetd.o +NETWORKING-$(CONFIG_TFTP) += tftp.o +NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o +NETWORKING-$(CONFIG_VCONFIG) += vconfig.o +NETWORKING-$(CONFIG_WGET) += wget.o + +libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR) + +needcrypt-y:= +needcrypt-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) := y + +ifeq ($(needcrypt-y),y) + LIBRARIES += -lcrypt +endif + +$(NETWORKING_DIR)$(NETWORKING_AR): $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y)) + $(AR) -ro $@ $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y)) + +$(NETWORKING_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/networking/arping.c b/busybox/networking/arping.c new file mode 100644 index 000000000..7279e8653 --- /dev/null +++ b/busybox/networking/arping.c @@ -0,0 +1,499 @@ +/* + * arping.c - Ping hosts by ARP requests/replies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Author: Alexey Kuznetsov + * Busybox port: Nick Fedchik + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "busybox.h" + +#define APPLET_NAME "arping" + +static struct in_addr src; +static struct in_addr dst; +static struct sockaddr_ll me; +static struct sockaddr_ll he; +static struct timeval last; +static int dad; +static int unsolicited; +static int advert; +static int quiet; +static int quit_on_reply = 0; +static int count = -1; +static int timeout; +static int unicasting; +static int s; +static int broadcast_only; +static int sent; +static int brd_sent; +static int received; +static int brd_recv; +static int req_recv; + +#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \ + ((tv1).tv_usec-(tv2).tv_usec)/1000 ) +#if 0 +static void set_signal(int signo, void (*handler) (void)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = (void (*)(int)) handler; + sa.sa_flags = SA_RESTART; + sigaction(signo, &sa, NULL); +} +#endif + +static int send_pack(int sock, struct in_addr *src_addr, + struct in_addr *dst_addr, struct sockaddr_ll *ME, + struct sockaddr_ll *HE) +{ + int err; + struct timeval now; + unsigned char buf[256]; + struct arphdr *ah = (struct arphdr *) buf; + unsigned char *p = (unsigned char *) (ah + 1); + + ah->ar_hrd = htons(ME->sll_hatype); + ah->ar_hrd = htons(ARPHRD_ETHER); + ah->ar_pro = htons(ETH_P_IP); + ah->ar_hln = ME->sll_halen; + ah->ar_pln = 4; + ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); + + memcpy(p, &ME->sll_addr, ah->ar_hln); + p += ME->sll_halen; + + memcpy(p, src_addr, 4); + p += 4; + + if (advert) + memcpy(p, &ME->sll_addr, ah->ar_hln); + else + memcpy(p, &HE->sll_addr, ah->ar_hln); + p += ah->ar_hln; + + memcpy(p, dst_addr, 4); + p += 4; + + gettimeofday(&now, NULL); + err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE)); + if (err == p - buf) { + last = now; + sent++; + if (!unicasting) + brd_sent++; + } + return err; +} + +void finish(void) +{ + if (!quiet) { + printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent); + printf("Received %d repl%s", received, (received > 1) ? "ies" : "y"); + if (brd_recv || req_recv) { + printf(" ("); + if (req_recv) + printf("%d request(s)", req_recv); + if (brd_recv) + printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv); + putchar(')'); + } + putchar('\n'); + fflush(stdout); + } + if (dad) + exit(!!received); + if (unsolicited) + exit(0); + exit(!received); +} + +void catcher(void) +{ + struct timeval tv; + static struct timeval start; + + gettimeofday(&tv, NULL); + + if (start.tv_sec == 0) + start = tv; + + if (count-- == 0 + || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500)) + finish(); + + if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) { + send_pack(s, &src, &dst, &me, &he); + if (count == 0 && unsolicited) + finish(); + } + alarm(1); +} + +int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM) +{ + struct timeval tv; + struct arphdr *ah = (struct arphdr *) buf; + unsigned char *p = (unsigned char *) (ah + 1); + struct in_addr src_ip, dst_ip; + + gettimeofday(&tv, NULL); + + /* Filter out wild packets */ + if (FROM->sll_pkttype != PACKET_HOST && + FROM->sll_pkttype != PACKET_BROADCAST && + FROM->sll_pkttype != PACKET_MULTICAST) + return 0; + + /* Only these types are recognised */ + if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) + return 0; + + /* ARPHRD check and this darned FDDI hack here :-( */ + if (ah->ar_hrd != htons(FROM->sll_hatype) && + (FROM->sll_hatype != ARPHRD_FDDI + || ah->ar_hrd != htons(ARPHRD_ETHER))) + return 0; + + /* Protocol must be IP. */ + if (ah->ar_pro != htons(ETH_P_IP)) + return 0; + if (ah->ar_pln != 4) + return 0; + if (ah->ar_hln != me.sll_halen) + return 0; + if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln)) + return 0; + memcpy(&src_ip, p + ah->ar_hln, 4); + memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4); + if (!dad) { + if (src_ip.s_addr != dst.s_addr) + return 0; + if (src.s_addr != dst_ip.s_addr) + return 0; + if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)) + return 0; + } else { + /* DAD packet was: + src_ip = 0 (or some src) + src_hw = ME + dst_ip = tested address + dst_hw = + + We fail, if receive request/reply with: + src_ip = tested_address + src_hw != ME + if src_ip in request was not zero, check + also that it matches to dst_ip, otherwise + dst_ip/dst_hw do not matter. + */ + if (src_ip.s_addr != dst.s_addr) + return 0; + if (memcmp(p, &me.sll_addr, me.sll_halen) == 0) + return 0; + if (src.s_addr && src.s_addr != dst_ip.s_addr) + return 0; + } + if (!quiet) { + int s_printed = 0; + + printf("%s ", + FROM->sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast"); + printf("%s from ", + ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request"); + printf("%s ", inet_ntoa(src_ip)); + printf("[%s]", ether_ntoa((struct ether_addr *) p)); + if (dst_ip.s_addr != src.s_addr) { + printf("for %s ", inet_ntoa(dst_ip)); + s_printed = 1; + } + if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) { + if (!s_printed) + printf("for "); + printf("[%s]", + ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4)); + } + if (last.tv_sec) { + long usecs = (tv.tv_sec - last.tv_sec) * 1000000 + + tv.tv_usec - last.tv_usec; + long msecs = (usecs + 500) / 1000; + + usecs -= msecs * 1000 - 500; + printf(" %ld.%03ldms\n", msecs, usecs); + } else { + printf(" UNSOLICITED?\n"); + } + fflush(stdout); + } + received++; + if (FROM->sll_pkttype != PACKET_HOST) + brd_recv++; + if (ah->ar_op == htons(ARPOP_REQUEST)) + req_recv++; + if (quit_on_reply) + finish(); + if (!broadcast_only) { + memcpy(he.sll_addr, p, me.sll_halen); + unicasting = 1; + } + return 1; +} + +int arping_main(int argc, char **argv) +{ + int socket_errno; + int ch; + uid_t uid = getuid(); + char *device = "eth0"; + int ifindex = 0; + char *source = NULL; + char *target; + + s = socket(PF_PACKET, SOCK_DGRAM, 0); + socket_errno = errno; + + setuid(uid); + + while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:")) != EOF) { + switch (ch) { + case 'b': + broadcast_only = 1; + break; + case 'D': + dad++; + quit_on_reply = 1; + break; + case 'U': + unsolicited++; + break; + case 'A': + advert++; + unsolicited++; + break; + case 'q': + quiet++; + break; + case 'c': + count = atoi(optarg); + break; + case 'w': + timeout = atoi(optarg); + break; + case 'I': + if (optarg == NULL) + bb_show_usage(); + if (bb_strlen(optarg) > IF_NAMESIZE) { + bb_error_msg("Interface name `%s' must be less than %d", optarg, + IF_NAMESIZE); + exit(2); + } + device = optarg; + break; + case 'f': + quit_on_reply = 1; + break; + case 's': + source = optarg; + break; + case 'h': + case '?': + default: + bb_show_usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + bb_show_usage(); + + target = *argv; + + + if (s < 0) { + bb_error_msg("socket"); + exit(socket_errno); + } + + { + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device, IFNAMSIZ - 1); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + bb_error_msg("Interface %s not found", device); + exit(2); + } + ifindex = ifr.ifr_ifindex; + + if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) { + bb_error_msg("SIOCGIFFLAGS"); + exit(2); + } + if (!(ifr.ifr_flags & IFF_UP)) { + bb_error_msg("Interface %s is down", device); + exit(2); + } + if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) { + bb_error_msg("Interface %s is not ARPable", device); + exit(dad ? 0 : 2); + } + } + + if (!inet_aton(target, &dst)) { + struct hostent *hp; + + hp = gethostbyname2(target, AF_INET); + if (!hp) { + bb_error_msg("invalid or unknown target %s", target); + exit(2); + } + memcpy(&dst, hp->h_addr, 4); + } + + if (source && !inet_aton(source, &src)) { + bb_error_msg("invalid source address %s", source); + exit(2); + } + + if (!dad && unsolicited && src.s_addr == 0) + src = dst; + + if (!dad || src.s_addr) { + struct sockaddr_in saddr; + int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (probe_fd < 0) { + bb_error_msg("socket"); + exit(2); + } + if (device) { + if (setsockopt + (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, + strlen(device) + 1) == -1) + bb_error_msg("WARNING: interface %s is ignored", device); + } + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + if (src.s_addr) { + saddr.sin_addr = src; + if (bind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) { + bb_error_msg("bind"); + exit(2); + } + } else if (!dad) { + int on = 1; + int alen = sizeof(saddr); + + saddr.sin_port = htons(1025); + saddr.sin_addr = dst; + + if (setsockopt + (probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *) &on, + sizeof(on)) == -1) + perror("WARNING: setsockopt(SO_DONTROUTE)"); + if (connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) + == -1) { + bb_error_msg("connect"); + exit(2); + } + if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == + -1) { + bb_error_msg("getsockname"); + exit(2); + } + src = saddr.sin_addr; + } + close(probe_fd); + }; + + me.sll_family = AF_PACKET; + me.sll_ifindex = ifindex; + me.sll_protocol = htons(ETH_P_ARP); + if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) { + bb_error_msg("bind"); + exit(2); + } + + { + int alen = sizeof(me); + + if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) { + bb_error_msg("getsockname"); + exit(2); + } + } + if (me.sll_halen == 0) { + bb_error_msg("Interface \"%s\" is not ARPable (no ll address)", device); + exit(dad ? 0 : 2); + } + he = me; + memset(he.sll_addr, -1, he.sll_halen); + + if (!quiet) { + printf("ARPING to %s", inet_ntoa(dst)); + printf(" from %s via %s\n", inet_ntoa(src), + device ? device : "unknown"); + } + + if (!src.s_addr && !dad) { + bb_error_msg("no src address in the non-DAD mode"); + exit(2); + } + + { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_RESTART; + + sa.sa_handler = (void (*)(int)) finish; + sigaction(SIGINT, &sa, NULL); + + sa.sa_handler = (void (*)(int)) catcher; + sigaction(SIGALRM, &sa, NULL); + } + + catcher(); + + while (1) { + sigset_t sset, osset; + char packet[4096]; + struct sockaddr_ll from; + int alen = sizeof(from); + int cc; + + if ((cc = recvfrom(s, packet, sizeof(packet), 0, + (struct sockaddr *) &from, &alen)) < 0) { + perror("recvfrom"); + continue; + } + sigemptyset(&sset); + sigaddset(&sset, SIGALRM); + sigaddset(&sset, SIGINT); + sigprocmask(SIG_BLOCK, &sset, &osset); + recv_pack(packet, cc, &from); + sigprocmask(SIG_SETMASK, &osset, NULL); + } +} diff --git a/busybox/networking/ftpgetput.c b/busybox/networking/ftpgetput.c new file mode 100644 index 000000000..f6bd82bc8 --- /dev/null +++ b/busybox/networking/ftpgetput.c @@ -0,0 +1,378 @@ +/* vi: set sw=4 ts=4: */ +/* + * ftpget + * + * Mini implementation of FTP to retrieve a remote file. + * + * Copyright (C) 2002 Jeff Angielski, The PTR Group + * Copyright (C) 2002 Glenn McGrath + * + * Based on wget.c by Chip Rosenthal Covad Communications + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 + +#include "busybox.h" + +typedef struct ftp_host_info_s { + char *user; + char *password; + struct sockaddr_in *s_in; +} ftp_host_info_t; + +static char verbose_flag = 0; +static char do_continue = 0; + +static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf) +{ + if (verbose_flag) { + bb_error_msg("cmd %s%s", s1, s2); + } + + if (s1) { + if (s2) { + fprintf(stream, "%s%s\n", s1, s2); + } else { + fprintf(stream, "%s\n", s1); + } + } + do { + char *buf_ptr; + + if (fgets(buf, 510, stream) == NULL) { + bb_perror_msg_and_die("fgets()"); + } + buf_ptr = strstr(buf, "\r\n"); + if (buf_ptr) { + *buf_ptr = '\0'; + } + } while (! isdigit(buf[0]) || buf[3] != ' '); + + return atoi(buf); +} + +static int xconnect_ftpdata(ftp_host_info_t *server, const char *buf) +{ + char *buf_ptr; + unsigned short port_num; + + buf_ptr = strrchr(buf, ','); + *buf_ptr = '\0'; + port_num = atoi(buf_ptr + 1); + + buf_ptr = strrchr(buf, ','); + *buf_ptr = '\0'; + port_num += atoi(buf_ptr + 1) * 256; + + server->s_in->sin_port=htons(port_num); + return(xconnect(server->s_in)); +} + +static FILE *ftp_login(ftp_host_info_t *server) +{ + FILE *control_stream; + char buf[512]; + + /* Connect to the command socket */ + control_stream = fdopen(xconnect(server->s_in), "r+"); + if (control_stream == NULL) { + bb_perror_msg_and_die("Couldnt open control stream"); + } + + if (ftpcmd(NULL, NULL, control_stream, buf) != 220) { + bb_error_msg_and_die("%s", buf + 4); + } + + /* Login to the server */ + switch (ftpcmd("USER ", server->user, control_stream, buf)) { + case 230: + break; + case 331: + if (ftpcmd("PASS ", server->password, control_stream, buf) != 230) { + bb_error_msg_and_die("PASS error: %s", buf + 4); + } + break; + default: + bb_error_msg_and_die("USER error: %s", buf + 4); + } + + ftpcmd("TYPE I", NULL, control_stream, buf); + + return(control_stream); +} + +#ifdef CONFIG_FTPGET +static int ftp_recieve(ftp_host_info_t *server, FILE *control_stream, + const char *local_path, char *server_path) +{ + char buf[512]; + off_t filesize = 0; + int fd_data; + int fd_local = -1; + off_t beg_range = 0; + + /* Connect to the data socket */ + if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { + bb_error_msg_and_die("PASV error: %s", buf + 4); + } + fd_data = xconnect_ftpdata(server, buf); + + if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) { + unsigned long value=filesize; + if (safe_strtoul(buf + 4, &value)) + bb_error_msg_and_die("SIZE error: %s", buf + 4); + filesize = value; + } + + if ((local_path[0] == '-') && (local_path[1] == '\0')) { + fd_local = STDOUT_FILENO; + do_continue = 0; + } + + if (do_continue) { + struct stat sbuf; + if (lstat(local_path, &sbuf) < 0) { + bb_perror_msg_and_die("fstat()"); + } + if (sbuf.st_size > 0) { + beg_range = sbuf.st_size; + } else { + do_continue = 0; + } + } + + if (do_continue) { + sprintf(buf, "REST %ld", (long)beg_range); + if (ftpcmd(buf, NULL, control_stream, buf) != 350) { + do_continue = 0; + } else { + filesize -= beg_range; + } + } + + if (ftpcmd("RETR ", server_path, control_stream, buf) > 150) { + bb_error_msg_and_die("RETR error: %s", buf + 4); + } + + /* only make a local file if we know that one exists on the remote server */ + if (fd_local == -1) { + if (do_continue) { + fd_local = bb_xopen(local_path, O_APPEND | O_WRONLY); + } else { + fd_local = bb_xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY); + } + } + + /* Copy the file */ + if (bb_copyfd_size(fd_data, fd_local, filesize) == -1) { + exit(EXIT_FAILURE); + } + + /* close it all down */ + close(fd_data); + if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { + bb_error_msg_and_die("ftp error: %s", buf + 4); + } + ftpcmd("QUIT", NULL, control_stream, buf); + + return(EXIT_SUCCESS); +} +#endif + +#ifdef CONFIG_FTPPUT +static int ftp_send(ftp_host_info_t *server, FILE *control_stream, + const char *server_path, char *local_path) +{ + struct stat sbuf; + char buf[512]; + int fd_data; + int fd_local; + int response; + + /* Connect to the data socket */ + if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { + bb_error_msg_and_die("PASV error: %s", buf + 4); + } + fd_data = xconnect_ftpdata(server, buf); + + if (ftpcmd("CWD ", server_path, control_stream, buf) != 250) { + bb_error_msg_and_die("CWD error: %s", buf + 4); + } + + /* get the local file */ + if ((local_path[0] == '-') && (local_path[1] == '\0')) { + fd_local = STDIN_FILENO; + } else { + fd_local = bb_xopen(local_path, O_RDONLY); + fstat(fd_local, &sbuf); + + sprintf(buf, "ALLO %lu", (unsigned long)sbuf.st_size); + response = ftpcmd(buf, NULL, control_stream, buf); + switch (response) { + case 200: + case 202: + break; + default: + close(fd_local); + bb_error_msg_and_die("ALLO error: %s", buf + 4); + break; + } + } + response = ftpcmd("STOR ", local_path, control_stream, buf); + switch (response) { + case 125: + case 150: + break; + default: + close(fd_local); + bb_error_msg_and_die("STOR error: %s", buf + 4); + } + + /* transfer the file */ + if (bb_copyfd_eof(fd_local, fd_data) == -1) { + exit(EXIT_FAILURE); + } + + /* close it all down */ + close(fd_data); + if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { + bb_error_msg_and_die("error: %s", buf + 4); + } + ftpcmd("QUIT", NULL, control_stream, buf); + + return(EXIT_SUCCESS); +} +#endif + +#define FTPGETPUT_OPT_CONTINUE 1 +#define FTPGETPUT_OPT_VERBOSE 2 +#define FTPGETPUT_OPT_USER 4 +#define FTPGETPUT_OPT_PASSWORD 8 +#define FTPGETPUT_OPT_PORT 16 + +static const struct option ftpgetput_long_options[] = { + {"continue", 1, NULL, 'c'}, + {"verbose", 0, NULL, 'v'}, + {"username", 1, NULL, 'u'}, + {"password", 1, NULL, 'p'}, + {"port", 1, NULL, 'P'}, + {0, 0, 0, 0} +}; + +int ftpgetput_main(int argc, char **argv) +{ + /* content-length of the file */ + unsigned long opt; + char *port = "ftp"; + + /* socket to ftp server */ + FILE *control_stream; + struct sockaddr_in s_in; + + /* continue a prev transfer (-c) */ + ftp_host_info_t *server; + + int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = NULL; + + /* Check to see if the command is ftpget or ftput */ +#ifdef CONFIG_FTPPUT +# ifdef CONFIG_FTPGET + if (bb_applet_name[3] == 'p') { + ftp_action = ftp_send; + } +# else + ftp_action = ftp_send; +# endif +#endif +#ifdef CONFIG_FTPGET +# ifdef CONFIG_FTPPUT + if (bb_applet_name[3] == 'g') { + ftp_action = ftp_recieve; + } +# else + ftp_action = ftp_recieve; +# endif +#endif + + /* Set default values */ + server = xmalloc(sizeof(ftp_host_info_t)); + server->user = "anonymous"; + server->password = "busybox@"; + verbose_flag = 0; + + /* + * Decipher the command line + */ + bb_applet_long_options = ftpgetput_long_options; + opt = bb_getopt_ulflags(argc, argv, "cvu:p:P:", &server->user, &server->password, &port); + + /* Process the non-option command line arguments */ + if (argc - optind != 3) { + bb_show_usage(); + } + + if (opt & FTPGETPUT_OPT_CONTINUE) { + do_continue = 1; + } + if (opt & FTPGETPUT_OPT_VERBOSE) { + verbose_flag = 1; + } + + /* We want to do exactly _one_ DNS lookup, since some + * sites (i.e. ftp.us.debian.org) use round-robin DNS + * and we want to connect to only one IP... */ + server->s_in = &s_in; + bb_lookup_host(&s_in, argv[optind]); + s_in.sin_port = bb_lookup_port(port, "tcp", 21); + if (verbose_flag) { + printf("Connecting to %s[%s]:%d\n", + argv[optind], inet_ntoa(s_in.sin_addr), ntohs(s_in.sin_port)); + } + + /* Connect/Setup/Configure the FTP session */ + control_stream = ftp_login(server); + + return(ftp_action(server, control_stream, argv[optind + 1], argv[optind + 2])); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/networking/hostname.c b/busybox/networking/hostname.c new file mode 100644 index 000000000..a00263d8f --- /dev/null +++ b/busybox/networking/hostname.c @@ -0,0 +1,130 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: hostname.c,v 1.36 2003/07/14 21:21:01 andersen Exp $ + * Mini hostname implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * 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 +#include "busybox.h" + +extern char *optarg; /* in unistd.h */ +extern int optind, opterr, optopt; /* in unistd.h */ + +static void do_sethostname(char *s, int isfile) +{ + FILE *f; + char buf[255]; + + if (!s) + return; + if (!isfile) { + if (sethostname(s, strlen(s)) < 0) { + if (errno == EPERM) + bb_error_msg_and_die("you must be root to change the hostname"); + else + bb_perror_msg_and_die("sethostname"); + } + } else { + f = bb_xfopen(s, "r"); + while (fgets(buf, 255, f) != NULL) { + if (buf[0] =='#') { + continue; + } + chomp(buf); + do_sethostname(buf, 0); + } +#ifdef CONFIG_FEATURE_CLEAN_UP + fclose(f); +#endif + } +} + +int hostname_main(int argc, char **argv) +{ + int opt; + int type = 0; + struct hostent *hp; + char *filename = NULL; + char buf[255]; + char *p = NULL; + + if (argc < 1) + bb_show_usage(); + + while ((opt = getopt(argc, argv, "dfisF:")) > 0) { + switch (opt) { + case 'd': + case 'f': + case 'i': + case 's': + type = opt; + break; + case 'F': + filename = optarg; + break; + default: + bb_show_usage(); + } + } + + /* Output in desired format */ + if (type != 0) { + gethostname(buf, 255); + hp = xgethostbyname(buf); + p = strchr(hp->h_name, '.'); + if (type == 'f') { + puts(hp->h_name); + } else if (type == 's') { + if (p != NULL) { + *p = 0; + } + puts(hp->h_name); + } else if (type == 'd') { + if (p) puts(p + 1); + } else if (type == 'i') { + while (hp->h_addr_list[0]) { + printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++))); + } + printf("\n"); + } + } + /* Set the hostname */ + else if (filename != NULL) { + do_sethostname(filename, 1); + } else if (optind < argc) { + do_sethostname(argv[optind], 0); + } + /* Or if all else fails, + * just print the current hostname */ + else { + gethostname(buf, 255); + puts(buf); + } + return(0); +} diff --git a/busybox/networking/httpd.c b/busybox/networking/httpd.c new file mode 100644 index 000000000..83ded5330 --- /dev/null +++ b/busybox/networking/httpd.c @@ -0,0 +1,2091 @@ +/* + * httpd implementation for busybox + * + * Copyright (C) 2002,2003 Glenn Engel + * Copyright (C) 2003,2004 Vladimir Oleynik + * + * simplify patch stolen from libbb without using strdup + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ***************************************************************************** + * + * Typical usage: + * for non root user + * httpd -p 8080 -h $HOME/public_html + * or for daemon start from rc script with uid=0: + * httpd -u www + * This is equivalent if www user have uid=80 to + * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication" + * + * + * When a url contains "cgi-bin" it is assumed to be a cgi script. The + * server changes directory to the location of the script and executes it + * after setting QUERY_STRING and other environment variables. + * + * The server can also be invoked as a url arg decoder and html text encoder + * as follows: + * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" + * bar=`httpd -e ""` # encode as "<Hello World>" + * Note that url encoding for arguments is not the same as html encoding for + * presentation. -d decodes a url-encoded argument while -e encodes in html + * for page display. + * + * httpd.conf has the following format: + * + * A:172.20. # Allow address from 172.20.0.0/16 + * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127 + * A:10.0.0.0/255.255.255.128 # Allow any address that previous set + * A:127.0.0.1 # Allow local loopback connections + * D:* # Deny from other IP connections + * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ + * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ + * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ + * .au:audio/basic # additional mime type for audio.au files + * + * A/D may be as a/d or allow/deny - first char case insensitive + * Deny IP rules take precedence over allow rules. + * + * + * The Deny/Allow IP logic: + * + * - Default is to allow all. No addresses are denied unless + * denied with a D: rule. + * - Order of Deny/Allow rules is significant + * - Deny rules take precedence over allow rules. + * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched + * addresses. + * - Specification of Allow all (A:*) is a no-op + * + * Example: + * 1. Allow only specified addresses + * A:172.20 # Allow any address that begins with 172.20. + * A:10.10. # Allow any address that begins with 10.10. + * A:127.0.0.1 # Allow local loopback connections + * D:* # Deny from other IP connections + * + * 2. Only deny specified addresses + * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255 + * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255 + * A:* # (optional line added for clarity) + * + * If a sub directory contains a config file it is parsed and merged with + * any existing settings as if it was appended to the original configuration. + * + * subdir paths are relative to the containing subdir and thus cannot + * affect the parent rules. + * + * Note that since the sub dir is parsed in the forked thread servicing the + * subdir http request, any merge is discarded when the process exits. As a + * result, the subdir settings only have a lifetime of a single request. + * + * + * If -c is not set, an attempt will be made to open the default + * root configuration file. If -c is set and the file is not found, the + * server exits with an error. + * +*/ + + +#include +#include /* for isspace */ +#include +#include /* for malloc */ +#include +#include /* for close */ +#include +#include +#include /* for connect and socket*/ +#include /* for sockaddr_in */ +#include +#include +#include +#include /* for open modes */ +#include "busybox.h" + + +static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004"; +static const char default_path_httpd_conf[] = "/etc"; +static const char httpd_conf[] = "httpd.conf"; +static const char home[] = "./"; + +#ifdef CONFIG_LFS +# define cont_l_fmt "%lld" +#else +# define cont_l_fmt "%ld" +#endif + +#define TIMEOUT 60 + +// Note: busybox xfuncs are not used because we want the server to keep running +// if something bad happens due to a malformed user request. +// As a result, all memory allocation after daemonize +// is checked rigorously + +//#define DEBUG 1 + +/* Configure options, disabled by default as custom httpd feature */ + +/* disabled as optional features */ +//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR +//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV +//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES +//#define CONFIG_FEATURE_HTTPD_SETUID +//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP + +/* If set, use this server from internet superserver only */ +//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + +/* You can use this server as standalone, require libbb.a for linking */ +//#define HTTPD_STANDALONE + +/* Config options, disable this for do very small module */ +//#define CONFIG_FEATURE_HTTPD_CGI +//#define CONFIG_FEATURE_HTTPD_BASIC_AUTH +//#define CONFIG_FEATURE_HTTPD_AUTH_MD5 + +#ifdef HTTPD_STANDALONE +/* standalone, enable all features */ +#undef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY +/* unset config option for remove warning as redefined */ +#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH +#undef CONFIG_FEATURE_HTTPD_AUTH_MD5 +#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR +#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV +#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES +#undef CONFIG_FEATURE_HTTPD_CGI +#undef CONFIG_FEATURE_HTTPD_SETUID +#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP +/* enable all features now */ +#define CONFIG_FEATURE_HTTPD_BASIC_AUTH +#define CONFIG_FEATURE_HTTPD_AUTH_MD5 +#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR +#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV +#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES +#define CONFIG_FEATURE_HTTPD_CGI +#define CONFIG_FEATURE_HTTPD_SETUID +#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP + +/* require from libbb.a for linking */ +const char *bb_applet_name = "httpd"; + +void bb_show_usage(void) +{ + fprintf(stderr, "Usage: %s [-p ] [-c configFile] [-d/-e ] " + "[-r realm] [-u user] [-h homedir]\n", bb_applet_name); + exit(1); +} +#endif + +#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY +#undef CONFIG_FEATURE_HTTPD_SETUID /* use inetd user.group config settings */ +#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP /* so is not daemon */ +/* inetd set stderr to accepted socket and we can`t true see debug messages */ +#undef DEBUG +#endif + +#define MAX_MEMORY_BUFF 8192 /* IO buffer */ + +typedef struct HT_ACCESS { + char *after_colon; + struct HT_ACCESS *next; + char before_colon[1]; /* really bigger, must last */ +} Htaccess; + +typedef struct HT_ACCESS_IP { + unsigned int ip; + unsigned int mask; + int allow_deny; + struct HT_ACCESS_IP *next; +} Htaccess_IP; + +typedef struct +{ + char buf[MAX_MEMORY_BUFF]; + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + const char *realm; + char *remoteuser; +#endif + + const char *query; + +#ifdef CONFIG_FEATURE_HTTPD_CGI + char *referer; +#endif + + const char *configFile; + + unsigned int rmt_ip; +#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG) + char rmt_ip_str[16]; /* for set env REMOTE_ADDR */ +#endif + unsigned port; /* server initial port and for + set env REMOTE_PORT */ + union HTTPD_FOUND { + const char *found_mime_type; + const char *found_moved_temporarily; + } httpd_found; + + off_t ContentLength; /* -1 - unknown */ + time_t last_mod; + + Htaccess_IP *ip_a_d; /* config allow/deny lines */ + int flg_deny_all; +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + Htaccess *auth; /* config user:password lines */ +#endif +#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + Htaccess *mime_a; /* config mime types */ +#endif + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + int accepted_socket; +#define a_c_r config->accepted_socket +#define a_c_w config->accepted_socket + int debugHttpd; /* if seted, don`t stay daemon */ +#else +#define a_c_r 0 +#define a_c_w 1 +#endif + volatile int alarm_signaled; + +} HttpdConfig; + +static HttpdConfig *config; + +static const char request_GET[] = "GET"; /* size algorithic optimize */ + +static const char* const suffixTable [] = { +/* Warning: shorted equivalent suffix in one line must be first */ + ".htm.html", "text/html", + ".jpg.jpeg", "image/jpeg", + ".gif", "image/gif", + ".png", "image/png", + ".txt.h.c.cc.cpp", "text/plain", + ".css", "text/css", + ".wav", "audio/wav", + ".avi", "video/x-msvideo", + ".qt.mov", "video/quicktime", + ".mpe.mpeg", "video/mpeg", + ".mid.midi", "audio/midi", + ".mp3", "audio/mpeg", +#if 0 /* unpopular */ + ".au", "audio/basic", + ".pac", "application/x-ns-proxy-autoconfig", + ".vrml.wrl", "model/vrml", +#endif + 0, "application/octet-stream" /* default */ + }; + +typedef enum +{ + HTTP_OK = 200, + HTTP_MOVED_TEMPORARILY = 302, + HTTP_BAD_REQUEST = 400, /* malformed syntax */ + HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ + HTTP_NOT_FOUND = 404, + HTTP_FORBIDDEN = 403, + HTTP_REQUEST_TIMEOUT = 408, + HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ + HTTP_INTERNAL_SERVER_ERROR = 500, +#if 0 /* future use */ + HTTP_CONTINUE = 100, + HTTP_SWITCHING_PROTOCOLS = 101, + HTTP_CREATED = 201, + HTTP_ACCEPTED = 202, + HTTP_NON_AUTHORITATIVE_INFO = 203, + HTTP_NO_CONTENT = 204, + HTTP_MULTIPLE_CHOICES = 300, + HTTP_MOVED_PERMANENTLY = 301, + HTTP_NOT_MODIFIED = 304, + HTTP_PAYMENT_REQUIRED = 402, + HTTP_BAD_GATEWAY = 502, + HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ + HTTP_RESPONSE_SETSIZE=0xffffffff +#endif +} HttpResponseNum; + +typedef struct +{ + HttpResponseNum type; + const char *name; + const char *info; +} HttpEnumString; + +static const HttpEnumString httpResponseNames[] = { + { HTTP_OK, "OK" }, + { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." }, + { HTTP_REQUEST_TIMEOUT, "Request Timeout", + "No request appeared within a reasonable time period." }, + { HTTP_NOT_IMPLEMENTED, "Not Implemented", + "The requested method is not recognized by this server." }, +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + { HTTP_UNAUTHORIZED, "Unauthorized", "" }, +#endif + { HTTP_NOT_FOUND, "Not Found", + "The requested URL was not found on this server." }, + { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." }, + { HTTP_FORBIDDEN, "Forbidden", "" }, + { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error", + "Internal Server Error" }, +#if 0 /* not implemented */ + { HTTP_CREATED, "Created" }, + { HTTP_ACCEPTED, "Accepted" }, + { HTTP_NO_CONTENT, "No Content" }, + { HTTP_MULTIPLE_CHOICES, "Multiple Choices" }, + { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, + { HTTP_NOT_MODIFIED, "Not Modified" }, + { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, + { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, +#endif +}; + + +static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT"; +static const char Content_length[] = "Content-length:"; + + +static int +scan_ip (const char **ep, unsigned int *ip, unsigned char endc) +{ + const char *p = *ep; + int auto_mask = 8; + int j; + + *ip = 0; + for (j = 0; j < 4; j++) { + unsigned int octet; + + if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0) + return -auto_mask; + octet = 0; + while (*p >= '0' && *p <= '9') { + octet *= 10; + octet += *p - '0'; + if (octet > 255) + return -auto_mask; + p++; + } + if (*p == '.') + p++; + if (*p != '/' && *p != 0) + auto_mask += 8; + *ip = ((*ip) << 8) | octet; + } + if (*p != 0) { + if (*p != endc) + return -auto_mask; + p++; + if(*p == 0) + return -auto_mask; + } + *ep = p; + return auto_mask; +} + +static int +scan_ip_mask (const char *ipm, unsigned int *ip, unsigned int *mask) +{ + int i; + unsigned int msk; + + i = scan_ip(&ipm, ip, '/'); + if(i < 0) + return i; + if(*ipm) { + const char *p = ipm; + + i = 0; + while (*p) { + if (*p < '0' || *p > '9') { + if (*p == '.') { + i = scan_ip (&ipm, mask, 0); + return i != 32; + } + return -1; + } + i *= 10; + i += *p - '0'; + p++; + } + } + if (i > 32 || i < 0) + return -1; + msk = 0x80000000; + *mask = 0; + while (i > 0) { + *mask |= msk; + msk >>= 1; + i--; + } + return 0; +} + +#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) +static void free_config_lines(Htaccess **pprev) +{ + Htaccess *prev = *pprev; + + while( prev ) { + Htaccess *cur = prev; + + prev = cur->next; + free(cur); + } + *pprev = NULL; +} +#endif + +/* flag */ +#define FIRST_PARSE 0 +#define SUBDIR_PARSE 1 +#define SIGNALED_PARSE 2 +#define FIND_FROM_HTTPD_ROOT 3 +/**************************************************************************** + * + > $Function: parse_conf() + * + * $Description: parse configuration file into in-memory linked list. + * + * The first non-white character is examined to determine if the config line + * is one of the following: + * .ext:mime/type # new mime type not compiled into httpd + * [adAD]:from # ip address allow/deny, * for wildcard + * /path:user:pass # username/password + * + * Any previous IP rules are discarded. + * If the flag argument is not SUBDIR_PARSE then all /path and mime rules + * are also discarded. That is, previous settings are retained if flag is + * SUBDIR_PARSE. + * + * $Parameters: + * (const char *) path . . null for ip address checks, path for password + * checks. + * (int) flag . . . . . . the source of the parse request. + * + * $Return: (None) + * + ****************************************************************************/ +static void parse_conf(const char *path, int flag) +{ + FILE *f; +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + Htaccess *prev, *cur; +#elif CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + Htaccess *cur; +#endif + + const char *cf = config->configFile; + char buf[160]; + char *p0 = NULL; + char *c, *p; + + /* free previous ip setup if present */ + Htaccess_IP *pip = config->ip_a_d; + + while( pip ) { + Htaccess_IP *cur_ipl = pip; + + pip = cur_ipl->next; + free(cur_ipl); + } + config->ip_a_d = NULL; + + config->flg_deny_all = 0; + +#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) + /* retain previous auth and mime config only for subdir parse */ + if(flag != SUBDIR_PARSE) { +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + free_config_lines(&config->auth); +#endif +#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + free_config_lines(&config->mime_a); +#endif + } +#endif + + if(flag == SUBDIR_PARSE || cf == NULL) { + cf = alloca(strlen(path) + sizeof(httpd_conf) + 2); + if(cf == NULL) { + if(flag == FIRST_PARSE) + bb_error_msg_and_die(bb_msg_memory_exhausted); + return; + } + sprintf((char *)cf, "%s/%s", path, httpd_conf); + } + + while((f = fopen(cf, "r")) == NULL) { + if(flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) { + /* config file not found, no changes to config */ + return; + } + if(config->configFile && flag == FIRST_PARSE) /* if -c option given */ + bb_perror_msg_and_die("%s", cf); + flag = FIND_FROM_HTTPD_ROOT; + cf = httpd_conf; + } + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + prev = config->auth; +#endif + /* This could stand some work */ + while ( (p0 = fgets(buf, sizeof(buf), f)) != NULL) { + c = NULL; + for(p = p0; *p0 != 0 && *p0 != '#'; p0++) { + if(!isspace(*p0)) { + *p++ = *p0; + if(*p0 == ':' && c == NULL) + c = p; + } + } + *p = 0; + + /* test for empty or strange line */ + if (c == NULL || *c == 0) + continue; + p0 = buf; + if(*p0 == 'd') + *p0 = 'D'; + if(*c == '*') { + if(*p0 == 'D') { + /* memorize deny all */ + config->flg_deny_all++; + } + /* skip default other "word:*" config lines */ + continue; + } + + if(*p0 == 'a') + *p0 = 'A'; + else if(*p0 != 'D' && *p0 != 'A' +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + && *p0 != '/' +#endif +#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + && *p0 != '.' +#endif + ) + continue; + if(*p0 == 'A' || *p0 == 'D') { + /* storing current config IP line */ + pip = calloc(1, sizeof(Htaccess_IP)); + if(pip) { + if(scan_ip_mask (c, &(pip->ip), &(pip->mask))) { + /* syntax IP{/mask} error detected, protect all */ + *p0 = 'D'; + pip->mask = 0; + } + pip->allow_deny = *p0; + if(*p0 == 'D') { + /* Deny:form_IP move top */ + pip->next = config->ip_a_d; + config->ip_a_d = pip; + } else { + /* add to bottom A:form_IP config line */ + Htaccess_IP *prev_IP = config->ip_a_d; + + if(prev_IP == NULL) { + config->ip_a_d = pip; + } else { + while(prev_IP->next) + prev_IP = prev_IP->next; + prev_IP->next = pip; + } + } + } + continue; + } +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + if(*p0 == '/') { + /* make full path from httpd root / curent_path / config_line_path */ + cf = flag == SUBDIR_PARSE ? path : ""; + p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c)); + if(p0 == NULL) + continue; + c[-1] = 0; + sprintf(p0, "/%s%s", cf, buf); + + /* another call bb_simplify_path */ + cf = p = p0; + + do { + if (*p == '/') { + if (*cf == '/') { /* skip duplicate (or initial) slash */ + continue; + } else if (*cf == '.') { + if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */ + continue; + } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) { + ++cf; + if (p > p0) { + while (*--p != '/'); /* omit previous dir */ + } + continue; + } + } + } + *++p = *cf; + } while (*++cf); + + if ((p == p0) || (*p != '/')) { /* not a trailing slash */ + ++p; /* so keep last character */ + } + *p = 0; + sprintf(p0, "%s:%s", p0, c); + } +#endif + +#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) + /* storing current config line */ + cur = calloc(1, sizeof(Htaccess) + strlen(p0)); + if(cur) { + cf = strcpy(cur->before_colon, p0); + c = strchr(cf, ':'); + *c++ = 0; + cur->after_colon = c; +#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + if(*cf == '.') { + /* config .mime line move top for overwrite previous */ + cur->next = config->mime_a; + config->mime_a = cur; + continue; + } +#endif +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + free(p0); + if(prev == NULL) { + /* first line */ + config->auth = prev = cur; + } else { + /* sort path, if current lenght eq or bigger then move up */ + Htaccess *prev_hti = config->auth; + int l = strlen(cf); + Htaccess *hti; + + for(hti = prev_hti; hti; hti = hti->next) { + if(l >= strlen(hti->before_colon)) { + /* insert before hti */ + cur->next = hti; + if(prev_hti != hti) { + prev_hti->next = cur; + } else { + /* insert as top */ + config->auth = cur; + } + break; + } + if(prev_hti != hti) + prev_hti = prev_hti->next; + } + if(!hti) { /* not inserted, add to bottom */ + prev->next = cur; + prev = cur; + } + } +#endif + } +#endif + } + fclose(f); +} + +#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR +/**************************************************************************** + * + > $Function: encodeString() + * + * $Description: Given a string, html encode special characters. + * This is used for the -e command line option to provide an easy way + * for scripts to encode result data without confusing browsers. The + * returned string pointer is memory allocated by malloc(). + * + * $Parameters: + * (const char *) string . . The first string to encode. + * + * $Return: (char *) . . . .. . . A pointer to the encoded string. + * + * $Errors: Returns a null string ("") if memory is not available. + * + ****************************************************************************/ +static char *encodeString(const char *string) +{ + /* take the simple route and encode everything */ + /* could possibly scan once to get length. */ + int len = strlen(string); + char *out = malloc(len*5 +1); + char *p=out; + char ch; + + if (!out) return ""; + while ((ch = *string++)) { + // very simple check for what to encode + if (isalnum(ch)) *p++ = ch; + else p += sprintf(p, "&#%d", (unsigned char) ch); + } + *p=0; + return out; +} +#endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */ + +/**************************************************************************** + * + > $Function: decodeString() + * + * $Description: Given a URL encoded string, convert it to plain ascii. + * Since decoding always makes strings smaller, the decode is done in-place. + * Thus, callers should strdup() the argument if they do not want the + * argument modified. The return is the original pointer, allowing this + * function to be easily used as arguments to other functions. + * + * $Parameters: + * (char *) string . . . The first string to decode. + * (int) flag . . . 1 if require decode '+' as ' ' for CGI + * + * $Return: (char *) . . . . A pointer to the decoded string (same as input). + * + * $Errors: None + * + ****************************************************************************/ +static char *decodeString(char *orig, int flag_plus_to_space) +{ + /* note that decoded string is always shorter than original */ + char *string = orig; + char *ptr = string; + + while (*ptr) + { + if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; } + else if (*ptr != '%') *string++ = *ptr++; + else { + unsigned int value; + sscanf(ptr+1, "%2X", &value); + *string++ = value; + ptr += 3; + } + } + *string = '\0'; + return orig; +} + + +#ifdef CONFIG_FEATURE_HTTPD_CGI +/**************************************************************************** + * + > $Function: addEnv() + * + * $Description: Add an environment variable setting to the global list. + * A NAME=VALUE string is allocated, filled, and added to the list of + * environment settings passed to the cgi execution script. + * + * $Parameters: + * (char *) name_before_underline - The first part environment variable name. + * (char *) name_after_underline - The second part environment variable name. + * (char *) value . . The value to which the env variable is set. + * + * $Return: (void) + * + * $Errors: Silently returns if the env runs out of space to hold the new item + * + ****************************************************************************/ +static void addEnv(const char *name_before_underline, + const char *name_after_underline, const char *value) +{ + char *s = NULL; + const char *underline; + + if (!value) + value = ""; + underline = *name_after_underline ? "_" : ""; + asprintf(&s, "%s%s%s=%s", name_before_underline, underline, + name_after_underline, value); + if(s) { + putenv(s); + } +} + +#if defined(CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV) || !defined(CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY) +/* set environs SERVER_PORT and REMOTE_PORT */ +static void addEnvPort(const char *port_name) +{ + char buf[16]; + + sprintf(buf, "%u", config->port); + addEnv(port_name, "PORT", buf); +} +#endif +#endif /* CONFIG_FEATURE_HTTPD_CGI */ + + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH +/**************************************************************************** + * + > $Function: decodeBase64() + * + > $Description: Decode a base 64 data stream as per rfc1521. + * Note that the rfc states that none base64 chars are to be ignored. + * Since the decode always results in a shorter size than the input, it is + * OK to pass the input arg as an output arg. + * + * $Parameter: + * (char *) Data . . . . A pointer to a base64 encoded string. + * Where to place the decoded data. + * + * $Return: void + * + * $Errors: None + * + ****************************************************************************/ +static void decodeBase64(char *Data) +{ + + const unsigned char *in = Data; + // The decoded size will be at most 3/4 the size of the encoded + unsigned long ch = 0; + int i = 0; + + while (*in) { + int t = *in++; + + if(t >= '0' && t <= '9') + t = t - '0' + 52; + else if(t >= 'A' && t <= 'Z') + t = t - 'A'; + else if(t >= 'a' && t <= 'z') + t = t - 'a' + 26; + else if(t == '+') + t = 62; + else if(t == '/') + t = 63; + else if(t == '=') + t = 0; + else + continue; + + ch = (ch << 6) | t; + i++; + if (i == 4) { + *Data++ = (char) (ch >> 16); + *Data++ = (char) (ch >> 8); + *Data++ = (char) ch; + i = 0; + } + } + *Data = 0; +} +#endif + + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY +/**************************************************************************** + * + > $Function: openServer() + * + * $Description: create a listen server socket on the designated port. + * + * $Return: (int) . . . A connection socket. -1 for errors. + * + * $Errors: None + * + ****************************************************************************/ +static int openServer(void) +{ + struct sockaddr_in lsocket; + int fd; + + /* create the socket right now */ + /* inet_addr() returns a value that is already in network order */ + memset(&lsocket, 0, sizeof(lsocket)); + lsocket.sin_family = AF_INET; + lsocket.sin_addr.s_addr = INADDR_ANY; + lsocket.sin_port = htons(config->port) ; + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd >= 0) { + /* tell the OS it's OK to reuse a previous address even though */ + /* it may still be in a close down state. Allows bind to succeed. */ + int on = 1; +#ifdef SO_REUSEPORT + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ; +#else + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ; +#endif + if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) { + listen(fd, 9); + signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */ + } else { + bb_perror_msg_and_die("bind"); + } + } else { + bb_perror_msg_and_die("create socket"); + } + return fd; +} +#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ + +/**************************************************************************** + * + > $Function: sendHeaders() + * + * $Description: Create and send HTTP response headers. + * The arguments are combined and sent as one write operation. Note that + * IE will puke big-time if the headers are not sent in one packet and the + * second packet is delayed for any reason. + * + * $Parameter: + * (HttpResponseNum) responseNum . . . The result code to send. + * + * $Return: (int) . . . . writing errors + * + ****************************************************************************/ +static int sendHeaders(HttpResponseNum responseNum) +{ + char *buf = config->buf; + const char *responseString = ""; + const char *infoString = 0; + const char *mime_type; + unsigned int i; + time_t timer = time(0); + char timeStr[80]; + int len; + + for (i = 0; + i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) { + if (httpResponseNames[i].type == responseNum) { + responseString = httpResponseNames[i].name; + infoString = httpResponseNames[i].info; + break; + } + } + /* error message is HTML */ + mime_type = responseNum == HTTP_OK ? + config->httpd_found.found_mime_type : "text/html"; + + /* emit the current date */ + strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer)); + len = sprintf(buf, + "HTTP/1.0 %d %s\nContent-type: %s\r\n" + "Date: %s\r\nConnection: close\r\n", + responseNum, responseString, mime_type, timeStr); + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + if (responseNum == HTTP_UNAUTHORIZED) { + len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n", + config->realm); + } +#endif + if(responseNum == HTTP_MOVED_TEMPORARILY) { + len += sprintf(buf+len, "Location: %s/%s%s\r\n", + config->httpd_found.found_moved_temporarily, + (config->query ? "?" : ""), + (config->query ? config->query : "")); + } + + if (config->ContentLength != -1) { /* file */ + strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod)); + len += sprintf(buf+len, "Last-Modified: %s\r\n%s " cont_l_fmt "\r\n", + timeStr, Content_length, config->ContentLength); + } + strcat(buf, "\r\n"); + len += 2; + if (infoString) { + len += sprintf(buf+len, + "%d %s\n" + "

%d %s

\n%s\n\n", + responseNum, responseString, + responseNum, responseString, infoString); + } +#ifdef DEBUG + if (config->debugHttpd) fprintf(stderr, "Headers: '%s'", buf); +#endif + return bb_full_write(a_c_w, buf, len); +} + +/**************************************************************************** + * + > $Function: getLine() + * + * $Description: Read from the socket until an end of line char found. + * + * Characters are read one at a time until an eol sequence is found. + * + * $Return: (int) . . . . number of characters read. -1 if error. + * + ****************************************************************************/ +static int getLine(void) +{ + int count = 0; + char *buf = config->buf; + + while (read(a_c_r, buf + count, 1) == 1) { + if (buf[count] == '\r') continue; + if (buf[count] == '\n') { + buf[count] = 0; + return count; + } + if(count < (MAX_MEMORY_BUFF-1)) /* check owerflow */ + count++; + } + if (count) return count; + else return -1; +} + +#ifdef CONFIG_FEATURE_HTTPD_CGI +/**************************************************************************** + * + > $Function: sendCgi() + * + * $Description: Execute a CGI script and send it's stdout back + * + * Environment variables are set up and the script is invoked with pipes + * for stdin/stdout. If a post is being done the script is fed the POST + * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). + * + * $Parameters: + * (const char *) url . . . . . . The requested URL (with leading /). + * (int bodyLen) . . . . . . . . Length of the post body. + * (const char *cookie) . . . . . For set HTTP_COOKIE. + * (const char *content_type) . . For set CONTENT_TYPE. + + * + * $Return: (char *) . . . . A pointer to the decoded string (same as input). + * + * $Errors: None + * + ****************************************************************************/ +static int sendCgi(const char *url, + const char *request, int bodyLen, const char *cookie, + const char *content_type) +{ + int fromCgi[2]; /* pipe for reading data from CGI */ + int toCgi[2]; /* pipe for sending data to CGI */ + + static char * argp[] = { 0, 0 }; + int pid = 0; + int inFd; + int outFd; + int firstLine = 1; + + do { + if (pipe(fromCgi) != 0) { + break; + } + if (pipe(toCgi) != 0) { + break; + } + + pid = fork(); + if (pid < 0) { + pid = 0; + break; + } + + if (!pid) { + /* child process */ + char *script; + char *purl = strdup( url ); + char realpath_buff[MAXPATHLEN]; + + if(purl == NULL) + _exit(242); + + inFd = toCgi[0]; + outFd = fromCgi[1]; + + dup2(inFd, 0); // replace stdin with the pipe + dup2(outFd, 1); // replace stdout with the pipe + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + if (!config->debugHttpd) +#endif + dup2(outFd, 2); // replace stderr with the pipe + + close(toCgi[0]); + close(toCgi[1]); + close(fromCgi[0]); + close(fromCgi[1]); + + /* + * Find PATH_INFO. + */ + script = purl; + while((script = strchr( script + 1, '/' )) != NULL) { + /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ + struct stat sb; + + *script = '\0'; + if(is_directory(purl + 1, 1, &sb) == 0) { + /* not directory, found script.cgi/PATH_INFO */ + *script = '/'; + break; + } + *script = '/'; /* is directory, find next '/' */ + } + addEnv("PATH", "INFO", script); /* set /PATH_INFO or NULL */ + addEnv("PATH", "", getenv("PATH")); + addEnv("REQUEST", "METHOD", request); + if(config->query) { + char *uri = alloca(strlen(purl) + 2 + strlen(config->query)); + if(uri) + sprintf(uri, "%s?%s", purl, config->query); + addEnv("REQUEST", "URI", uri); + } else { + addEnv("REQUEST", "URI", purl); + } + if(script != NULL) + *script = '\0'; /* reduce /PATH_INFO */ + /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ + addEnv("SCRIPT_NAME", "", purl); + addEnv("QUERY_STRING", "", config->query); + addEnv("SERVER", "SOFTWARE", httpdVersion); + addEnv("SERVER", "PROTOCOL", "HTTP/1.0"); + addEnv("GATEWAY_INTERFACE", "", "CGI/1.1"); + addEnv("REMOTE", "ADDR", config->rmt_ip_str); +#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV + addEnvPort("REMOTE"); +#endif + if(bodyLen) { + char sbl[32]; + + sprintf(sbl, "%d", bodyLen); + addEnv("CONTENT", "LENGTH", sbl); + } + if(cookie) + addEnv("HTTP", "COOKIE", cookie); + if(content_type) + addEnv("CONTENT", "TYPE", content_type); +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + if(config->remoteuser) { + addEnv("REMOTE", "USER", config->remoteuser); + addEnv("AUTH_TYPE", "", "Basic"); + } +#endif + if(config->referer) + addEnv("HTTP", "REFERER", config->referer); + + /* set execve argp[0] without path */ + argp[0] = strrchr( purl, '/' ) + 1; + /* but script argp[0] must have absolute path and chdiring to this */ + if(realpath(purl + 1, realpath_buff) != NULL) { + script = strrchr(realpath_buff, '/'); + if(script) { + *script = '\0'; + if(chdir(realpath_buff) == 0) { + *script = '/'; + // now run the program. If it fails, + // use _exit() so no destructors + // get called and make a mess. + execv(realpath_buff, argp); + } + } + } +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + config->accepted_socket = 1; /* send to stdout */ +#endif + sendHeaders(HTTP_NOT_FOUND); + _exit(242); + } /* end child */ + + } while (0); + + if (pid) { + /* parent process */ + int status; + size_t post_readed_size = 0, post_readed_idx = 0; + + inFd = fromCgi[0]; + outFd = toCgi[1]; + close(fromCgi[1]); + close(toCgi[0]); + signal(SIGPIPE, SIG_IGN); + + while (1) { + fd_set readSet; + fd_set writeSet; + char wbuf[128]; + int nfound; + int count; + + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_SET(inFd, &readSet); + if(bodyLen > 0 || post_readed_size > 0) { + FD_SET(outFd, &writeSet); + nfound = outFd > inFd ? outFd : inFd; + if(post_readed_size == 0) { + FD_SET(a_c_r, &readSet); + if(nfound < a_c_r) + nfound = a_c_r; + } + /* Now wait on the set of sockets! */ + nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL); + } else { + if(!bodyLen) { + close(outFd); + bodyLen = -1; + } + nfound = select(inFd + 1, &readSet, 0, 0, NULL); + } + + if (nfound <= 0) { + if (waitpid(pid, &status, WNOHANG) > 0) { + close(inFd); +#ifdef DEBUG + if (config->debugHttpd) { + if (WIFEXITED(status)) + bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status)); + if (WIFSIGNALED(status)) + bb_error_msg("piped has exited with signal=%d", WTERMSIG(status)); + } +#endif + break; + } + } else if(post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) { + count = bb_full_write(outFd, wbuf + post_readed_idx, post_readed_size); + if(count > 0) { + post_readed_size -= count; + post_readed_idx += count; + if(post_readed_size == 0) + post_readed_idx = 0; + } + } else if(bodyLen > 0 && post_readed_size == 0 && FD_ISSET(a_c_r, &readSet)) { + count = bodyLen > sizeof(wbuf) ? sizeof(wbuf) : bodyLen; + count = safe_read(a_c_r, wbuf, count); + if(count > 0) { + post_readed_size += count; + bodyLen -= count; + } else { + bodyLen = 0; /* closed */ + } + } + if(FD_ISSET(inFd, &readSet)) { + int s = a_c_w; + char *rbuf = config->buf; + +#ifndef PIPE_BUF +# define PIPESIZE 4096 /* amount of buffering in a pipe */ +#else +# define PIPESIZE PIPE_BUF +#endif +#if PIPESIZE >= MAX_MEMORY_BUFF +# error "PIPESIZE >= MAX_MEMORY_BUFF" +#endif + + // There is something to read + count = safe_read(inFd, rbuf, PIPESIZE); + if (count == 0) + break; /* closed */ + if (count > 0) { + if (firstLine) { + rbuf[count] = 0; + /* check to see if the user script added headers */ + if(strncmp(rbuf, "HTTP/1.0 200 OK\n", 4) != 0) { + bb_full_write(s, "HTTP/1.0 200 OK\n", 16); + } + if (strstr(rbuf, "ontent-") == 0) { + bb_full_write(s, "Content-type: text/plain\n\n", 26); + } + firstLine = 0; + } + if (bb_full_write(s, rbuf, count) != count) + break; + +#ifdef DEBUG + if (config->debugHttpd) + fprintf(stderr, "cgi read %d bytes\n", count); +#endif + } + } + } + } + return 0; +} +#endif /* CONFIG_FEATURE_HTTPD_CGI */ + +/**************************************************************************** + * + > $Function: sendFile() + * + * $Description: Send a file response to an HTTP request + * + * $Parameter: + * (const char *) url . . The URL requested. + * + * $Return: (int) . . . . . . Always 0. + * + ****************************************************************************/ +static int sendFile(const char *url) +{ + char * suffix; + int f; + const char * const * table; + const char * try_suffix; + + suffix = strrchr(url, '.'); + + for (table = suffixTable; *table; table += 2) + if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) { + try_suffix += strlen(suffix); + if(*try_suffix == 0 || *try_suffix == '.') + break; + } + /* also, if not found, set default as "application/octet-stream"; */ + config->httpd_found.found_mime_type = *(table+1); +#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES + if (suffix) { + Htaccess * cur; + + for (cur = config->mime_a; cur; cur = cur->next) { + if(strcmp(cur->before_colon, suffix) == 0) { + config->httpd_found.found_mime_type = cur->after_colon; + break; + } + } + } +#endif /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */ + +#ifdef DEBUG + if (config->debugHttpd) + fprintf(stderr, "Sending file '%s' Content-type: %s\n", + url, config->httpd_found.found_mime_type); +#endif + + f = open(url, O_RDONLY); + if (f >= 0) { + int count; + char *buf = config->buf; + + sendHeaders(HTTP_OK); + while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) { + if (bb_full_write(a_c_w, buf, count) != count) + break; + } + close(f); + } else { +#ifdef DEBUG + if (config->debugHttpd) + bb_perror_msg("Unable to open '%s'", url); +#endif + sendHeaders(HTTP_NOT_FOUND); + } + + return 0; +} + +static int checkPermIP(void) +{ + Htaccess_IP * cur; + + /* This could stand some work */ + for (cur = config->ip_a_d; cur; cur = cur->next) { +#ifdef DEBUG + if (config->debugHttpd) { + fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str); + fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n", + (unsigned char)(cur->ip >> 24), + (unsigned char)(cur->ip >> 16), + (unsigned char)(cur->ip >> 8), + cur->ip & 0xff, + (unsigned char)(cur->mask >> 24), + (unsigned char)(cur->mask >> 16), + (unsigned char)(cur->mask >> 8), + cur->mask & 0xff); + } +#endif + if((config->rmt_ip & cur->mask) == cur->ip) + return cur->allow_deny == 'A'; /* Allow/Deny */ + } + + /* if unconfigured, return 1 - access from all */ + return !config->flg_deny_all; +} + +/**************************************************************************** + * + > $Function: checkPerm() + * + * $Description: Check the permission file for access password protected. + * + * If config file isn't present, everything is allowed. + * Entries are of the form you can see example from header source + * + * $Parameters: + * (const char *) path . . . . The file path. + * (const char *) request . . . User information to validate. + * + * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise. + * + ****************************************************************************/ + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH +static int checkPerm(const char *path, const char *request) +{ + Htaccess * cur; + const char *p; + const char *p0; + + const char *prev = NULL; + + /* This could stand some work */ + for (cur = config->auth; cur; cur = cur->next) { + p0 = cur->before_colon; + if(prev != NULL && strcmp(prev, p0) != 0) + continue; /* find next identical */ + p = cur->after_colon; +#ifdef DEBUG + if (config->debugHttpd) + fprintf(stderr,"checkPerm: '%s' ? '%s'\n", p0, request); +#endif + { + int l = strlen(p0); + + if(strncmp(p0, path, l) == 0 && + (l == 1 || path[l] == '/' || path[l] == 0)) { + char *u; + /* path match found. Check request */ + /* for check next /path:user:password */ + prev = p0; + u = strchr(request, ':'); + if(u == NULL) { + /* bad request, ':' required */ + break; + } + +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + { + char *cipher; + char *pp; + + if(strncmp(p, request, u-request) != 0) { + /* user uncompared */ + continue; + } + pp = strchr(p, ':'); + if(pp && pp[1] == '$' && pp[2] == '1' && + pp[3] == '$' && pp[4]) { + pp++; + cipher = pw_encrypt(u+1, pp); + if (strcmp(cipher, pp) == 0) + goto set_remoteuser_var; /* Ok */ + /* unauthorized */ + continue; + } + } +#endif + if (strcmp(p, request) == 0) { +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 +set_remoteuser_var: +#endif + config->remoteuser = strdup(request); + if(config->remoteuser) + config->remoteuser[(u - request)] = 0; + return 1; /* Ok */ + } + /* unauthorized */ + } + } + } /* for */ + + return prev == NULL; +} + +#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ + +/**************************************************************************** + * + > $Function: handleIncoming() + * + * $Description: Handle an incoming http request. + * + ****************************************************************************/ + +static void +handle_sigalrm( int sig ) +{ + sendHeaders(HTTP_REQUEST_TIMEOUT); + config->alarm_signaled = sig; +} + +/**************************************************************************** + * + > $Function: handleIncoming() + * + * $Description: Handle an incoming http request. + * + ****************************************************************************/ +static void handleIncoming(void) +{ + char *buf = config->buf; + char *url; + char *purl; + int blank = -1; + char *test; + struct stat sb; + int ip_allowed; +#ifdef CONFIG_FEATURE_HTTPD_CGI + const char *prequest = request_GET; + long length=0; + char *cookie = 0; + char *content_type = 0; +#endif +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + fd_set s_fd; + struct timeval tv; + int retval; +#endif + struct sigaction sa; + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + int credentials = -1; /* if not requred this is Ok */ +#endif + + sa.sa_handler = handle_sigalrm; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* no SA_RESTART */ + sigaction(SIGALRM, &sa, NULL); + + do { + int count; + + (void) alarm( TIMEOUT ); + if (getLine() <= 0) + break; /* closed */ + + purl = strpbrk(buf, " \t"); + if(purl == NULL) { +BAD_REQUEST: + sendHeaders(HTTP_BAD_REQUEST); + break; + } + *purl = 0; +#ifdef CONFIG_FEATURE_HTTPD_CGI + if(strcasecmp(buf, prequest) != 0) { + prequest = "POST"; + if(strcasecmp(buf, prequest) != 0) { + sendHeaders(HTTP_NOT_IMPLEMENTED); + break; + } + } +#else + if(strcasecmp(buf, request_GET) != 0) { + sendHeaders(HTTP_NOT_IMPLEMENTED); + break; + } +#endif + *purl = ' '; + count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank); + + decodeString(buf, 0); + if (count < 1 || buf[0] != '/') { + /* Garbled request/URL */ + goto BAD_REQUEST; + } + url = alloca(strlen(buf) + 12); /* + sizeof("/index.html\0") */ + if(url == NULL) { + sendHeaders(HTTP_INTERNAL_SERVER_ERROR); + break; + } + strcpy(url, buf); + /* extract url args if present */ + test = strchr(url, '?'); + if (test) { + *test++ = 0; + config->query = test; + } + + /* algorithm stolen from libbb bb_simplify_path(), + but don`t strdup and reducing trailing slash and protect out root */ + purl = test = url; + + do { + if (*purl == '/') { + if (*test == '/') { /* skip duplicate (or initial) slash */ + continue; + } else if (*test == '.') { + if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */ + continue; + } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) { + ++test; + if (purl == url) { + /* protect out root */ + goto BAD_REQUEST; + } + while (*--purl != '/'); /* omit previous dir */ + continue; + } + } + } + *++purl = *test; + } while (*++test); + + *++purl = 0; /* so keep last character */ + test = purl; /* end ptr */ + + /* If URL is directory, adding '/' */ + /* If URL is directory, adding '/' */ + if(test[-1] != '/') { + if ( is_directory(url + 1, 1, &sb) ) { + config->httpd_found.found_moved_temporarily = url; + } + } +#ifdef DEBUG + if (config->debugHttpd) + fprintf(stderr, "url='%s', args=%s\n", url, config->query); +#endif + + test = url; + ip_allowed = checkPermIP(); + while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) { + /* have path1/path2 */ + *test = '\0'; + if( is_directory(url + 1, 1, &sb) ) { + /* may be having subdir config */ + parse_conf(url + 1, SUBDIR_PARSE); + ip_allowed = checkPermIP(); + } + *test = '/'; + } + + // read until blank line for HTTP version specified, else parse immediate + while (blank >= 0 && alarm(TIMEOUT) >= 0 && (count = getLine()) > 0) { + +#ifdef DEBUG + if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf); +#endif + +#ifdef CONFIG_FEATURE_HTTPD_CGI + /* try and do our best to parse more lines */ + if ((strncasecmp(buf, Content_length, 15) == 0)) { + if(prequest != request_GET) + length = strtol(buf + 15, 0, 0); // extra read only for POST + } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) { + for(test = buf + 7; isspace(*test); test++) + ; + cookie = strdup(test); + } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) { + for(test = buf + 13; isspace(*test); test++) + ; + content_type = strdup(test); + } else if ((strncasecmp(buf, "Referer:", 8) == 0)) { + for(test = buf + 8; isspace(*test); test++) + ; + config->referer = strdup(test); + } +#endif + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + if (strncasecmp(buf, "Authorization:", 14) == 0) { + /* We only allow Basic credentials. + * It shows up as "Authorization: Basic " where + * the userid:password is base64 encoded. + */ + for(test = buf + 14; isspace(*test); test++) + ; + if (strncasecmp(test, "Basic", 5) != 0) + continue; + + test += 5; /* decodeBase64() skiping space self */ + decodeBase64(test); + credentials = checkPerm(url, test); + } +#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ + + } /* while extra header reading */ + + (void) alarm( 0 ); + if(config->alarm_signaled) + break; + + if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) { + /* protect listing [/path]/httpd_conf or IP deny */ +#ifdef CONFIG_FEATURE_HTTPD_CGI +FORBIDDEN: /* protect listing /cgi-bin */ +#endif + sendHeaders(HTTP_FORBIDDEN); + break; + } + +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + if (credentials <= 0 && checkPerm(url, ":") == 0) { + sendHeaders(HTTP_UNAUTHORIZED); + break; + } +#endif + + if(config->httpd_found.found_moved_temporarily) { + sendHeaders(HTTP_MOVED_TEMPORARILY); +#ifdef DEBUG + /* clear unforked memory flag */ + if(config->debugHttpd) + config->httpd_found.found_moved_temporarily = NULL; +#endif + break; + } + + test = url + 1; /* skip first '/' */ + +#ifdef CONFIG_FEATURE_HTTPD_CGI + /* if strange Content-Length */ + if (length < 0) + break; + + if (strncmp(test, "cgi-bin", 7) == 0) { + if(test[7] == '/' && test[8] == 0) + goto FORBIDDEN; // protect listing cgi-bin/ + sendCgi(url, prequest, length, cookie, content_type); + } else { + if (prequest != request_GET) + sendHeaders(HTTP_NOT_IMPLEMENTED); + else { +#endif /* CONFIG_FEATURE_HTTPD_CGI */ + if(purl[-1] == '/') + strcpy(purl, "index.html"); + if ( stat(test, &sb ) == 0 ) { + config->ContentLength = sb.st_size; + config->last_mod = sb.st_mtime; + } + sendFile(test); +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + /* unset if non inetd looped */ + config->ContentLength = -1; +#endif + +#ifdef CONFIG_FEATURE_HTTPD_CGI + } + } +#endif + + } while (0); + + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY +/* from inetd don`t looping: freeing, closing automatic from exit always */ +# ifdef DEBUG + if (config->debugHttpd) fprintf(stderr, "closing socket\n"); +# endif +# ifdef CONFIG_FEATURE_HTTPD_CGI + free(cookie); + free(content_type); + free(config->referer); +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + free(config->remoteuser); +#endif +# endif + shutdown(a_c_w, SHUT_WR); + + /* Properly wait for remote to closed */ + FD_ZERO (&s_fd) ; + FD_SET (a_c_w, &s_fd) ; + + do { + tv.tv_sec = 2 ; + tv.tv_usec = 0 ; + retval = select (a_c_w + 1, &s_fd, NULL, NULL, &tv); + } while (retval > 0 && (read (a_c_w, buf, sizeof (config->buf)) > 0)); + + shutdown(a_c_r, SHUT_RD); + close(config->accepted_socket); +#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ +} + +/**************************************************************************** + * + > $Function: miniHttpd() + * + * $Description: The main http server function. + * + * Given an open socket fildes, listen for new connections and farm out + * the processing as a forked process. + * + * $Parameters: + * (int) server. . . The server socket fildes. + * + * $Return: (int) . . . . Always 0. + * + ****************************************************************************/ +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY +static int miniHttpd(int server) +{ + fd_set readfd, portfd; + + FD_ZERO(&portfd); + FD_SET(server, &portfd); + + /* copy the ports we are watching to the readfd set */ + while (1) { + readfd = portfd; + + /* Now wait INDEFINITELY on the set of sockets! */ + if (select(server + 1, &readfd, 0, 0, 0) > 0) { + if (FD_ISSET(server, &readfd)) { + int on; + struct sockaddr_in fromAddr; + + socklen_t fromAddrLen = sizeof(fromAddr); + int s = accept(server, + (struct sockaddr *)&fromAddr, &fromAddrLen); + + if (s < 0) { + continue; + } + config->accepted_socket = s; + config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr); +#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG) + sprintf(config->rmt_ip_str, "%u.%u.%u.%u", + (unsigned char)(config->rmt_ip >> 24), + (unsigned char)(config->rmt_ip >> 16), + (unsigned char)(config->rmt_ip >> 8), + config->rmt_ip & 0xff); + config->port = ntohs(fromAddr.sin_port); +#ifdef DEBUG + if (config->debugHttpd) { + bb_error_msg("connection from IP=%s, port %u\n", + config->rmt_ip_str, config->port); + } +#endif +#endif /* CONFIG_FEATURE_HTTPD_CGI */ + + /* set the KEEPALIVE option to cull dead connections */ + on = 1; + setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on)); + + if (config->debugHttpd || fork() == 0) { + /* This is the spawned thread */ +#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP + /* protect reload config, may be confuse checking */ + signal(SIGHUP, SIG_IGN); +#endif + handleIncoming(); + if(!config->debugHttpd) + exit(0); + } + close(s); + } + } + } // while (1) + return 0; +} + +#else + /* from inetd */ + +static int miniHttpd(void) +{ + struct sockaddr_in fromAddrLen; + socklen_t sinlen = sizeof (struct sockaddr_in); + + getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen); + config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr); +#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG) + sprintf(config->rmt_ip_str, "%u.%u.%u.%u", + (unsigned char)(config->rmt_ip >> 24), + (unsigned char)(config->rmt_ip >> 16), + (unsigned char)(config->rmt_ip >> 8), + config->rmt_ip & 0xff); +#endif + config->port = ntohs(fromAddrLen.sin_port); + handleIncoming(); + return 0; +} +#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ + +#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP +static void sighup_handler(int sig) +{ + /* set and reset */ + struct sigaction sa; + + parse_conf(default_path_httpd_conf, + sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); + sa.sa_handler = sighup_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGHUP, &sa, NULL); +} +#endif + + +static const char httpd_opts[]="c:d:h:" +#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR + "e:" +#define OPT_INC_1 1 +#else +#define OPT_INC_1 0 +#endif +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + "r:" +# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + "m:" +# define OPT_INC_2 2 +# else +# define OPT_INC_2 1 +#endif +#else +#define OPT_INC_2 0 +#endif +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + "p:v" +#ifdef CONFIG_FEATURE_HTTPD_SETUID + "u:" +#endif +#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ + ; + +#define OPT_CONFIG_FILE (1<<0) +#define OPT_DECODE_URL (1<<1) +#define OPT_HOME_HTTPD (1<<2) +#define OPT_ENCODE_URL (1<<(2+OPT_INC_1)) +#define OPT_REALM (1<<(3+OPT_INC_1)) +#define OPT_MD5 (1<<(4+OPT_INC_1)) +#define OPT_PORT (1<<(3+OPT_INC_1+OPT_INC_2)) +#define OPT_DEBUG (1<<(4+OPT_INC_1+OPT_INC_2)) +#define OPT_SETUID (1<<(5+OPT_INC_1+OPT_INC_2)) + + +#ifdef HTTPD_STANDALONE +int main(int argc, char *argv[]) +#else +int httpd_main(int argc, char *argv[]) +#endif +{ + unsigned long opt; + const char *home_httpd = home; + char *url_for_decode; +#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR + const char *url_for_encode; +#endif +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + const char *s_port; +#endif + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + int server; +#endif + +#ifdef CONFIG_FEATURE_HTTPD_SETUID + const char *s_uid; + long uid = -1; +#endif + +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + const char *pass; +#endif + + config = xcalloc(1, sizeof(*config)); +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + config->realm = "Web Server Authentication"; +#endif + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + config->port = 80; +#endif + + config->ContentLength = -1; + + opt = bb_getopt_ulflags(argc, argv, httpd_opts, + &(config->configFile), &url_for_decode, &home_httpd +#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR + , &url_for_encode +#endif +#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH + , &(config->realm) +# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + , &pass +# endif +#endif +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + , &s_port +#ifdef CONFIG_FEATURE_HTTPD_SETUID + , &s_uid +#endif +#endif + ); + + if(opt & OPT_DECODE_URL) { + printf("%s", decodeString(url_for_decode, 1)); + return 0; + } +#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR + if(opt & OPT_ENCODE_URL) { + printf("%s", encodeString(url_for_encode)); + return 0; + } +#endif +#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 + if(opt & OPT_MD5) { + printf("%s\n", pw_encrypt(pass, "$1$")); + return 0; + } +#endif +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + if(opt & OPT_PORT) + config->port = bb_xgetlarg(s_port, 10, 1, 0xffff); + config->debugHttpd = opt & OPT_DEBUG; +#ifdef CONFIG_FEATURE_HTTPD_SETUID + if(opt & OPT_SETUID) { + char *e; + + uid = strtol(s_uid, &e, 0); + if(*e != '\0') { + /* not integer */ + uid = my_getpwnam(s_uid); + } + } +#endif +#endif + + if(chdir(home_httpd)) { + bb_perror_msg_and_die("can`t chdir to %s", home_httpd); + } +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + server = openServer(); +# ifdef CONFIG_FEATURE_HTTPD_SETUID + /* drop privileges */ + if(uid > 0) + setuid(uid); +# endif +#endif + +#ifdef CONFIG_FEATURE_HTTPD_CGI + { + char *p = getenv("PATH"); + if(p) { + p = bb_xstrdup(p); + } + clearenv(); + if(p) + setenv("PATH", p, 1); +# ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + addEnvPort("SERVER"); +# endif + } +#endif + +#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP + sighup_handler(0); +#else + parse_conf(default_path_httpd_conf, FIRST_PARSE); +#endif + +#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY + if (!config->debugHttpd) { + if (daemon(1, 0) < 0) /* don`t change curent directory */ + bb_perror_msg_and_die("daemon"); + } + return miniHttpd(server); +#else + return miniHttpd(); +#endif +} diff --git a/busybox/networking/ifconfig.c b/busybox/networking/ifconfig.c new file mode 100644 index 000000000..4e3df2982 --- /dev/null +++ b/busybox/networking/ifconfig.c @@ -0,0 +1,605 @@ +/* ifconfig + * + * Similar to the standard Unix ifconfig, but with only the necessary + * parts for AF_INET, and without any printing of if info (for now). + * + * Bjorn Wesen, Axis Communications AB + * + * + * Authors of the original ifconfig was: + * Fred N. van Kempen, + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * $Id: ifconfig.c,v 1.30 2004/03/31 11:30:08 andersen Exp $ + * + */ + +/* + * Heavily modified by Manuel Novoa III Mar 6, 2001 + * + * From initial port to busybox, removed most of the redundancy by + * converting to a table-driven approach. Added several (optional) + * args missing from initial port. + * + * Still missing: media, tunnel. + * + * 2002-04-20 + * IPV6 support added by Bart Visscher + */ + +#include +#include +#include /* strcmp and friends */ +#include /* isdigit and friends */ +#include /* offsetof */ +#include +#include +#include +#include +#include +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#include +#else +#include +#include +#endif +#include "inet_common.h" +#include "busybox.h" + +#ifdef CONFIG_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 + +#ifdef CONFIG_FEATURE_IPV6 +struct in6_ifreq { + struct in6_addr ifr6_addr; + uint32_t ifr6_prefixlen; + int ifr6_ifindex; +}; +#endif + +/* + * Here are the bit masks for the "flags" member of struct options below. + * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'. + * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg. + */ +#define N_CLR 0x01 +#define M_CLR 0x02 +#define N_SET 0x04 +#define M_SET 0x08 +#define N_ARG 0x10 +#define M_ARG 0x20 + +#define M_MASK (M_CLR | M_SET | M_ARG) +#define N_MASK (N_CLR | N_SET | N_ARG) +#define SET_MASK (N_SET | M_SET) +#define CLR_MASK (N_CLR | M_CLR) +#define SET_CLR_MASK (SET_MASK | CLR_MASK) +#define ARG_MASK (M_ARG | N_ARG) + +/* + * Here are the bit masks for the "arg_flags" member of struct options below. + */ + +/* + * cast type: + * 00 int + * 01 char * + * 02 HOST_COPY in_ether + * 03 HOST_COPY INET_resolve + */ +#define A_CAST_TYPE 0x03 +/* + * map type: + * 00 not a map type (mem_start, io_addr, irq) + * 04 memstart (unsigned long) + * 08 io_addr (unsigned short) + * 0C irq (unsigned char) + */ +#define A_MAP_TYPE 0x0C +#define A_ARG_REQ 0x10 /* Set if an arg is required. */ +#define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */ +#define A_SET_AFTER 0x40 /* Set a flag at the end. */ +#define A_COLON_CHK 0x80 /* Is this needed? See below. */ +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS +#define A_HOSTNAME 0x100 /* Set if it is ip addr. */ +#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */ +#else +#define A_HOSTNAME 0 +#define A_BROADCAST 0 +#endif + +/* + * These defines are for dealing with the A_CAST_TYPE field. + */ +#define A_CAST_CHAR_PTR 0x01 +#define A_CAST_RESOLVE 0x01 +#define A_CAST_HOST_COPY 0x02 +#define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY +#define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE) + +/* + * These defines are for dealing with the A_MAP_TYPE field. + */ +#define A_MAP_ULONG 0x04 /* memstart */ +#define A_MAP_USHORT 0x08 /* io_addr */ +#define A_MAP_UCHAR 0x0C /* irq */ + +/* + * Define the bit masks signifying which operations to perform for each arg. + */ + +#define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG) +#define ARG_IO_ADDR (A_ARG_REQ | A_MAP_ULONG) +#define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR) +#define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE) +#define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK) +#define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST) +#define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER) +#define ARG_POINTOPOINT (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) +#define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR) +#define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR) +#define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME) +#define ARG_ADD_DEL (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) + + +/* + * Set up the tables. Warning! They must have corresponding order! + */ + +struct arg1opt { + const char *name; + unsigned short selector; + unsigned short ifr_offset; +}; + +struct options { + const char *name; +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + const unsigned int flags:6; + const unsigned int arg_flags:10; +#else + const unsigned char flags; + const unsigned char arg_flags; +#endif + const unsigned short selector; +}; + +#define ifreq_offsetof(x) offsetof(struct ifreq, x) + +static const struct arg1opt Arg1Opt[] = { + {"SIOCSIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric)}, + {"SIOCSIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu)}, + {"SIOCSIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen)}, + {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)}, + {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)}, + {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)}, +#ifdef CONFIG_FEATURE_IFCONFIG_HW + {"SIOCSIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr)}, +#endif + {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)}, +#ifdef SIOCSKEEPALIVE + {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)}, +#endif +#ifdef SIOCSOUTFILL + {"SIOCSOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data)}, +#endif +#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start)}, + {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr)}, + {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq)}, +#endif + /* Last entry if for unmatched (possibly hostname) arg. */ +#ifdef CONFIG_FEATURE_IPV6 + {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */ + {"SIOCDIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */ +#endif + {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, +}; + +static const struct options OptArray[] = { + {"metric", N_ARG, ARG_METRIC, 0}, + {"mtu", N_ARG, ARG_MTU, 0}, + {"txqueuelen", N_ARG, ARG_TXQUEUELEN, 0}, + {"dstaddr", N_ARG, ARG_DSTADDR, 0}, + {"netmask", N_ARG, ARG_NETMASK, 0}, + {"broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST}, +#ifdef CONFIG_FEATURE_IFCONFIG_HW + {"hw", N_ARG, ARG_HW, 0}, +#endif + {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT}, +#ifdef SIOCSKEEPALIVE + {"keepalive", N_ARG, ARG_KEEPALIVE, 0}, +#endif +#ifdef SIOCSOUTFILL + {"outfill", N_ARG, ARG_OUTFILL, 0}, +#endif +#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + {"mem_start", N_ARG, ARG_MEM_START, 0}, + {"io_addr", N_ARG, ARG_IO_ADDR, 0}, + {"irq", N_ARG, ARG_IRQ, 0}, +#endif +#ifdef CONFIG_FEATURE_IPV6 + {"add", N_ARG, ARG_ADD_DEL, 0}, + {"del", N_ARG, ARG_ADD_DEL, 0}, +#endif + {"arp", N_CLR | M_SET, 0, IFF_NOARP}, + {"trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS}, + {"promisc", N_SET | M_CLR, 0, IFF_PROMISC}, + {"multicast", N_SET | M_CLR, 0, IFF_MULTICAST}, + {"allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI}, + {"dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC}, + {"up", N_SET, 0, (IFF_UP | IFF_RUNNING)}, + {"down", N_CLR, 0, IFF_UP}, + {NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING)} +}; + +/* + * A couple of prototypes. + */ + +#ifdef CONFIG_FEATURE_IFCONFIG_HW +static int in_ether(char *bufp, struct sockaddr *sap); +#endif + +#ifdef CONFIG_FEATURE_IFCONFIG_STATUS +extern int interface_opt_a; +extern int display_interfaces(char *ifname); +#endif + +/* + * Our main function. + */ + +int ifconfig_main(int argc, char **argv) +{ + struct ifreq ifr; + struct sockaddr_in sai; +#ifdef CONFIG_FEATURE_IPV6 + struct sockaddr_in6 sai6; +#endif +#ifdef CONFIG_FEATURE_IFCONFIG_HW + struct sockaddr sa; +#endif + const struct arg1opt *a1op; + const struct options *op; + int sockfd; /* socket fd we use to manipulate stuff with */ + int goterr; + int selector; +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + unsigned int mask; + unsigned int did_flags; + unsigned int sai_hostname, sai_netmask; +#else + unsigned char mask; + unsigned char did_flags; +#endif + char *p; + char host[128]; + + goterr = 0; + did_flags = 0; +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + sai_hostname = 0; + sai_netmask = 0; +#endif + + /* skip argv[0] */ + ++argv; + --argc; + +#ifdef CONFIG_FEATURE_IFCONFIG_STATUS + if ((argc > 0) && (((*argv)[0] == '-') && ((*argv)[1] == 'a') && !(*argv)[2])) { + interface_opt_a = 1; + --argc; + ++argv; + } +#endif + + if (argc <= 1) { +#ifdef CONFIG_FEATURE_IFCONFIG_STATUS + return display_interfaces(argc ? *argv : NULL); +#else + bb_error_msg_and_die + ("ifconfig was not compiled with interface status display support."); +#endif + } + + /* Create a channel to the NET kernel. */ + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg_and_die("socket"); + } + + /* get interface name */ + safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ); + + /* Process the remaining arguments. */ + while (*++argv != (char *) NULL) { + p = *argv; + mask = N_MASK; + if (*p == '-') { /* If the arg starts with '-'... */ + ++p; /* advance past it and */ + mask = M_MASK; /* set the appropriate mask. */ + } + for (op = OptArray; op->name; op++) { /* Find table entry. */ + if (strcmp(p, op->name) == 0) { /* If name matches... */ + if ((mask &= op->flags)) { /* set the mask and go. */ + goto FOUND_ARG;; + } + /* If we get here, there was a valid arg with an */ + /* invalid '-' prefix. */ + ++goterr; + goto LOOP; + } + } + + /* We fell through, so treat as possible hostname. */ + a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1; + mask = op->arg_flags; + goto HOSTNAME; + + FOUND_ARG: + if (mask & ARG_MASK) { + mask = op->arg_flags; + a1op = Arg1Opt + (op - OptArray); + if (mask & A_NETMASK & did_flags) { + bb_show_usage(); + } + if (*++argv == NULL) { + if (mask & A_ARG_REQ) { + bb_show_usage(); + } else { + --argv; + mask &= A_SET_AFTER; /* just for broadcast */ + } + } else { /* got an arg so process it */ + HOSTNAME: + did_flags |= (mask & (A_NETMASK|A_HOSTNAME)); + if (mask & A_CAST_HOST_COPY) { +#ifdef CONFIG_FEATURE_IFCONFIG_HW + if (mask & A_CAST_RESOLVE) { +#endif +#ifdef CONFIG_FEATURE_IPV6 + char *prefix; + int prefix_len = 0; +#endif + + safe_strncpy(host, *argv, (sizeof host)); +#ifdef CONFIG_FEATURE_IPV6 + if ((prefix = strchr(host, '/'))) { + if (safe_strtoi(prefix + 1, &prefix_len) || + (prefix_len < 0) || (prefix_len > 128)) + { + ++goterr; + goto LOOP; + } + *prefix = 0; + } +#endif + + sai.sin_family = AF_INET; + sai.sin_port = 0; + if (!strcmp(host, bb_INET_default)) { + /* Default is special, meaning 0.0.0.0. */ + sai.sin_addr.s_addr = INADDR_ANY; +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + } else if (((host[0] == '+') && !host[1]) && (mask & A_BROADCAST) && + (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)) { + /* + is special, meaning broadcast is derived. */ + sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask); +#endif +#ifdef CONFIG_FEATURE_IPV6 + } else if (inet_pton(AF_INET6, host, &sai6.sin6_addr) > 0) { + int sockfd6; + struct in6_ifreq ifr6; + + memcpy((char *) &ifr6.ifr6_addr, + (char *) &sai6.sin6_addr, + sizeof(struct in6_addr)); + + /* Create a channel to the NET kernel. */ + if ((sockfd6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg_and_die("socket6"); + } + if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0) { + perror("SIOGIFINDEX"); + ++goterr; + continue; + } + ifr6.ifr6_ifindex = ifr.ifr_ifindex; + ifr6.ifr6_prefixlen = prefix_len; + if (ioctl(sockfd6, a1op->selector, &ifr6) < 0) { + perror(a1op->name); + ++goterr; + } + continue; +#endif + } else if (inet_aton(host, &sai.sin_addr) == 0) { + /* It's not a dotted quad. */ + struct hostent *hp; + if ((hp = gethostbyname(host)) == (struct hostent *)NULL) { + ++goterr; + continue; + } + memcpy((char *) &sai.sin_addr, (char *) hp->h_addr_list[0], + sizeof(struct in_addr)); + } +#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS + if (mask & A_HOSTNAME) { + sai_hostname = sai.sin_addr.s_addr; + } + if (mask & A_NETMASK) { + sai_netmask = sai.sin_addr.s_addr; + } +#endif + p = (char *) &sai; +#ifdef CONFIG_FEATURE_IFCONFIG_HW + } else { /* A_CAST_HOST_COPY_IN_ETHER */ + /* This is the "hw" arg case. */ + if (strcmp("ether", *argv) || (*++argv == NULL)) { + bb_show_usage(); + } + safe_strncpy(host, *argv, (sizeof host)); + if (in_ether(host, &sa)) { + bb_error_msg("invalid hw-addr %s", host); + ++goterr; + continue; + } + p = (char *) &sa; + } +#endif + memcpy((((char *) (&ifr)) + a1op->ifr_offset), + p, sizeof(struct sockaddr)); + } else { + unsigned int i = strtoul(*argv, NULL, 0); + + p = ((char *) (&ifr)) + a1op->ifr_offset; +#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + if (mask & A_MAP_TYPE) { + if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) { + ++goterr; + continue; + } + if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) { + *((unsigned char *) p) = i; + } else if (mask & A_MAP_USHORT) { + *((unsigned short *) p) = i; + } else { + *((unsigned long *) p) = i; + } + } else +#endif + if (mask & A_CAST_CHAR_PTR) { + *((caddr_t *) p) = (caddr_t) i; + } else { /* A_CAST_INT */ + *((int *) p) = i; + } + } + + if (ioctl(sockfd, a1op->selector, &ifr) < 0) { + perror(a1op->name); + ++goterr; + continue; + } +#ifdef QUESTIONABLE_ALIAS_CASE + if (mask & A_COLON_CHK) { + /* + * Don't do the set_flag() if the address is an alias with + * a - at the end, since it's deleted already! - Roman + * + * Should really use regex.h here, not sure though how well + * it'll go with the cross-platform support etc. + */ + char *ptr; + short int found_colon = 0; + + for (ptr = ifr.ifr_name; *ptr; ptr++) { + if (*ptr == ':') { + found_colon++; + } + } + + if (found_colon && *(ptr - 1) == '-') { + continue; + } + } +#endif + } + if (!(mask & A_SET_AFTER)) { + continue; + } + mask = N_SET; + } + + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + perror("SIOCGIFFLAGS"); + ++goterr; + } else { + selector = op->selector; + if (mask & SET_MASK) { + ifr.ifr_flags |= selector; + } else { + ifr.ifr_flags &= ~selector; + } + if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { + perror("SIOCSIFFLAGS"); + ++goterr; + } + } + LOOP: + continue; + } /* end of while-loop */ + + return goterr; +} + +#ifdef CONFIG_FEATURE_IFCONFIG_HW +/* Input an Ethernet address and convert to binary. */ +static int in_ether(char *bufp, struct sockaddr *sap) +{ + unsigned char *ptr; + int i, j; + unsigned char val; + unsigned char c; + + sap->sa_family = ARPHRD_ETHER; + ptr = sap->sa_data; + + i = 0; + do { + j = val = 0; + + /* We might get a semicolon here - not required. */ + if (i && (*bufp == ':')) { + bufp++; + } + + do { + c = *bufp; + if (((unsigned char)(c - '0')) <= 9) { + c -= '0'; + } else if (((unsigned char)((c|0x20) - 'a')) <= 5) { + c = (c|0x20) - ('a'-10); + } else if (j && (c == ':' || c == 0)) { + break; + } else { + return -1; + } + ++bufp; + val <<= 4; + val += c; + } while (++j < 2); + *ptr++ = val; + } while (++i < ETH_ALEN); + + return (int) (*bufp); /* Error if we don't end at end of string. */ +} +#endif diff --git a/busybox/networking/ifupdown.c b/busybox/networking/ifupdown.c new file mode 100644 index 000000000..1842be58b --- /dev/null +++ b/busybox/networking/ifupdown.c @@ -0,0 +1,1463 @@ +/* vi: set sw=4 ts=4: */ +/* + * ifupdown for busybox + * Copyright (c) 2002 Glenn McGrath + * Copyright (c) 2003-2004 Erik Andersen + * + * Based on ifupdown v 0.6.4 by Anthony Towns + * Copyright (c) 1999 Anthony Towns + * + * Changes to upstream version + * Remove checks for kernel version, assume kernel version 2.2.0 or better. + * Lines in the interfaces file cannot wrap. + * To adhere to the FHS, the default state file is /var/run/ifstate. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* TODO: standardise execute() return codes to return 0 for success and 1 for failure */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libbb.h" + +#define MAX_OPT_DEPTH 10 +#define EUNBALBRACK 10001 +#define EUNDEFVAR 10002 +#define EUNBALPER 10000 + +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING +#define MAX_INTERFACE_LENGTH 10 +#endif + +#if 0 +#define debug_noise(fmt, args...) printf(fmt, ## args) +#else +#define debug_noise(fmt, args...) +#endif + +/* Forward declaration */ +struct interface_defn_t; + +typedef int (execfn)(char *command); +typedef int (command_set)(struct interface_defn_t *ifd, execfn *e); + +extern llist_t *llist_add_to_end(llist_t *list_head, char *data) +{ + llist_t *new_item, *tmp, *prev; + + new_item = xmalloc(sizeof(llist_t)); + new_item->data = data; + new_item->link = NULL; + + prev = NULL; + tmp = list_head; + while(tmp) { + prev = tmp; + tmp = tmp->link; + } + if (prev) { + prev->link = new_item; + } else { + list_head = new_item; + } + + return(list_head); +} + +struct method_t +{ + char *name; + command_set *up; + command_set *down; +}; + +struct address_family_t +{ + char *name; + int n_methods; + struct method_t *method; +}; + +struct mapping_defn_t +{ + struct mapping_defn_t *next; + + int max_matches; + int n_matches; + char **match; + + char *script; + + int max_mappings; + int n_mappings; + char **mapping; +}; + +struct variable_t +{ + char *name; + char *value; +}; + +struct interface_defn_t +{ + struct interface_defn_t *prev; + struct interface_defn_t *next; + + char *iface; + struct address_family_t *address_family; + struct method_t *method; + + int automatic; + + int max_options; + int n_options; + struct variable_t *option; +}; + +struct interfaces_file_t +{ + llist_t *autointerfaces; + llist_t *ifaces; + struct mapping_defn_t *mappings; +}; + +static char no_act = 0; +static char verbose = 0; +static char **environ = NULL; + +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + +static unsigned int count_bits(unsigned int a) +{ + unsigned int result; + result = (a & 0x55) + ((a >> 1) & 0x55); + result = (result & 0x33) + ((result >> 2) & 0x33); + return((result & 0x0F) + ((result >> 4) & 0x0F)); +} + +static int count_netmask_bits(char *dotted_quad) +{ + unsigned int result, a, b, c, d; + /* Found a netmask... Check if it is dotted quad */ + if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) + return -1; + result = count_bits(a); + result += count_bits(b); + result += count_bits(c); + result += count_bits(d); + return ((int)result); +} +#endif + +static void addstr(char **buf, size_t *len, size_t *pos, char *str, size_t str_length) +{ + if (*pos + str_length >= *len) { + char *newbuf; + + newbuf = xrealloc(*buf, *len * 2 + str_length + 1); + *buf = newbuf; + *len = *len * 2 + str_length + 1; + } + + while (str_length-- >= 1) { + (*buf)[(*pos)++] = *str; + str++; + } + (*buf)[*pos] = '\0'; +} + +static int strncmpz(char *l, char *r, size_t llen) +{ + int i = strncmp(l, r, llen); + + if (i == 0) { + return(-r[llen]); + } else { + return(i); + } +} + +static char *get_var(char *id, size_t idlen, struct interface_defn_t *ifd) +{ + int i; + + if (strncmpz(id, "iface", idlen) == 0) { + char *result; + static char label_buf[20]; + strncpy(label_buf, ifd->iface, 19); + label_buf[19]=0; + result = strchr(label_buf, ':'); + if (result) { + *result=0; + } + return( label_buf); + } else if (strncmpz(id, "label", idlen) == 0) { + return (ifd->iface); + } else { + for (i = 0; i < ifd->n_options; i++) { + if (strncmpz(id, ifd->option[i].name, idlen) == 0) { + return (ifd->option[i].value); + } + } + } + + return(NULL); +} + +static char *parse(char *command, struct interface_defn_t *ifd) +{ + + char *result = NULL; + size_t pos = 0, len = 0; + size_t old_pos[MAX_OPT_DEPTH] = { 0 }; + int okay[MAX_OPT_DEPTH] = { 1 }; + int opt_depth = 1; + + while (*command) { + switch (*command) { + + default: + addstr(&result, &len, &pos, command, 1); + command++; + break; + case '\\': + if (command[1]) { + addstr(&result, &len, &pos, command + 1, 1); + command += 2; + } else { + addstr(&result, &len, &pos, command, 1); + command++; + } + break; + case '[': + if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) { + old_pos[opt_depth] = pos; + okay[opt_depth] = 1; + opt_depth++; + command += 2; + } else { + addstr(&result, &len, &pos, "[", 1); + command++; + } + break; + case ']': + if (command[1] == ']' && opt_depth > 1) { + opt_depth--; + if (!okay[opt_depth]) { + pos = old_pos[opt_depth]; + result[pos] = '\0'; + } + command += 2; + } else { + addstr(&result, &len, &pos, "]", 1); + command++; + } + break; + case '%': + { + char *nextpercent; + char *varvalue; + + command++; + nextpercent = strchr(command, '%'); + if (!nextpercent) { + errno = EUNBALPER; + free(result); + return (NULL); + } + + varvalue = get_var(command, nextpercent - command, ifd); + + if (varvalue) { + addstr(&result, &len, &pos, varvalue, bb_strlen(varvalue)); + } else { +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + /* Sigh... Add a special case for 'ip' to convert from + * dotted quad to bit count style netmasks. */ + if (strncmp(command, "bnmask", 6)==0) { + int res; + varvalue = get_var("netmask", 7, ifd); + if (varvalue && (res=count_netmask_bits(varvalue)) > 0) { + char argument[255]; + sprintf(argument, "%d", res); + addstr(&result, &len, &pos, argument, bb_strlen(argument)); + command = nextpercent + 1; + break; + } + } +#endif + okay[opt_depth - 1] = 0; + } + + command = nextpercent + 1; + } + break; + } + } + + if (opt_depth > 1) { + errno = EUNBALBRACK; + free(result); + return(NULL); + } + + if (!okay[0]) { + errno = EUNDEFVAR; + free(result); + return(NULL); + } + + return(result); +} + +static int execute(char *command, struct interface_defn_t *ifd, execfn *exec) +{ + char *out; + int ret; + + out = parse(command, ifd); + if (!out) { + return(0); + } + ret = (*exec) (out); + + free(out); + if (ret != 1) { + return(0); + } + return(1); +} + +#ifdef CONFIG_FEATURE_IFUPDOWN_IPX +static int static_up_ipx(struct interface_defn_t *ifd, execfn *exec) +{ + return(execute("ipx_interface add %iface% %frame% %netnum%", ifd, exec)); +} + +static int static_down_ipx(struct interface_defn_t *ifd, execfn *exec) +{ + return(execute("ipx_interface del %iface% %frame%", ifd, exec)); +} + +static int dynamic_up(struct interface_defn_t *ifd, execfn *exec) +{ + return(execute("ipx_interface add %iface% %frame%", ifd, exec)); +} + +static int dynamic_down(struct interface_defn_t *ifd, execfn *exec) +{ + return(execute("ipx_interface del %iface% %frame%", ifd, exec)); +} + +static struct method_t methods_ipx[] = { + { "dynamic", dynamic_up, dynamic_down, }, + { "static", static_up_ipx, static_down_ipx, }, +}; + +struct address_family_t addr_ipx = { + "ipx", + sizeof(methods_ipx) / sizeof(struct method_t), + methods_ipx +}; +#endif /* IFUP_FEATURE_IPX */ + +#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6 +static int loopback_up6(struct interface_defn_t *ifd, execfn *exec) +{ +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + int result; + result =execute("ip addr add ::1 dev %iface%", ifd, exec); + result += execute("ip link set %iface% up", ifd, exec); + return ((result == 2) ? 2 : 0); +#else + return( execute("ifconfig %iface% add ::1", ifd, exec)); +#endif +} + +static int loopback_down6(struct interface_defn_t *ifd, execfn *exec) +{ +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + return(execute("ip link set %iface% down", ifd, exec)); +#else + return(execute("ifconfig %iface% del ::1", ifd, exec)); +#endif +} + +static int static_up6(struct interface_defn_t *ifd, execfn *exec) +{ + int result; +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + result = execute("ip addr add %address%/%netmask% dev %iface% [[label %label%]]", ifd, exec); + result += execute("ip link set [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec); + result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec); +#else + result = execute("ifconfig %iface% [[media %media%]] [[hw %hwaddress%]] [[mtu %mtu%]] up", ifd, exec); + result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec); + result += execute("[[ route -A inet6 add ::/0 gw %gateway% ]]", ifd, exec); +#endif + return ((result == 3) ? 3 : 0); +} + +static int static_down6(struct interface_defn_t *ifd, execfn *exec) +{ +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + return(execute("ip link set %iface% down", ifd, exec)); +#else + return(execute("ifconfig %iface% down", ifd, exec)); +#endif +} + +#ifdef CONFIG_FEATURE_IFUPDOWN_IP +static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec) +{ + int result; + result = execute("ip tunnel add %iface% mode sit remote " + "%endpoint% [[local %local%]] [[ttl %ttl%]]", ifd, exec); + result += execute("ip link set %iface% up", ifd, exec); + result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec); + result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec); + return ((result == 4) ? 4 : 0); +} + +static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec) +{ + return( execute("ip tunnel del %iface%", ifd, exec)); +} +#endif + +static struct method_t methods6[] = { +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + { "v4tunnel", v4tunnel_up, v4tunnel_down, }, +#endif + { "static", static_up6, static_down6, }, + { "loopback", loopback_up6, loopback_down6, }, +}; + +struct address_family_t addr_inet6 = { + "inet6", + sizeof(methods6) / sizeof(struct method_t), + methods6 +}; +#endif /* CONFIG_FEATURE_IFUPDOWN_IPV6 */ + +#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 +static int loopback_up(struct interface_defn_t *ifd, execfn *exec) +{ +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + int result; + result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec); + result += execute("ip link set %iface% up", ifd, exec); + return ((result == 2) ? 2 : 0); +#else + return( execute("ifconfig %iface% 127.0.0.1 up", ifd, exec)); +#endif +} + +static int loopback_down(struct interface_defn_t *ifd, execfn *exec) +{ +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + int result; + result = execute("ip addr flush dev %iface%", ifd, exec); + result += execute("ip link set %iface% down", ifd, exec); + return ((result == 2) ? 2 : 0); +#else + return( execute("ifconfig %iface% 127.0.0.1 down", ifd, exec)); +#endif +} + +static int static_up(struct interface_defn_t *ifd, execfn *exec) +{ + int result; +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + result = execute("ip addr add %address%/%bnmask% [[broadcast %broadcast%]] " + "dev %iface% [[peer %pointopoint%]] [[label %label%]]", ifd, exec); + result += execute("ip link set [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec); + result += execute("[[ ip route add default via %gateway% dev %iface% ]]", ifd, exec); + return ((result == 3) ? 3 : 0); +#else + result = execute("ifconfig %iface% %address% netmask %netmask% " + "[[broadcast %broadcast%]] [[pointopoint %pointopoint%]] " + "[[media %media%]] [[mtu %mtu%]] [[hw %hwaddress%]] up", + ifd, exec); + result += execute("[[ route add default gw %gateway% %iface% ]]", ifd, exec); + return ((result == 2) ? 2 : 0); +#endif +} + +static int static_down(struct interface_defn_t *ifd, execfn *exec) +{ + int result; +#ifdef CONFIG_FEATURE_IFUPDOWN_IP + result = execute("ip addr flush dev %iface%", ifd, exec); + result += execute("ip link set %iface% down", ifd, exec); +#else + result = execute("[[ route del default gw %gateway% %iface% ]]", ifd, exec); + result += execute("ifconfig %iface% down", ifd, exec); +#endif + return ((result == 2) ? 2 : 0); +} + +static int execable(char *program) +{ + struct stat buf; + if (0 == stat(program, &buf)) { + if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode)) { + return(1); + } + } + return(0); +} + +static int dhcp_up(struct interface_defn_t *ifd, execfn *exec) +{ + if (execable("/sbin/udhcpc")) { + return( execute("udhcpc -n -p /var/run/udhcpc.%iface%.pid -i " + "%iface% [[-H %hostname%]] [[-c %clientid%]]", ifd, exec)); + } else if (execable("/sbin/pump")) { + return( execute("pump -i %iface% [[-h %hostname%]] [[-l %leasehours%]]", ifd, exec)); + } else if (execable("/sbin/dhclient")) { + return( execute("dhclient -pf /var/run/dhclient.%iface%.pid %iface%", ifd, exec)); + } else if (execable("/sbin/dhcpcd")) { + return( execute("dhcpcd [[-h %hostname%]] [[-i %vendor%]] [[-I %clientid%]] " + "[[-l %leasetime%]] %iface%", ifd, exec)); + } + return(0); +} + +static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) +{ + int result = 0; + if (execable("/sbin/udhcpc")) { + /* SIGUSR2 forces udhcpc to release the current lease and go inactive, + * and SIGTERM causes udhcpc to exit. Signals are queued and processed + * sequentially so we don't need to sleep */ + result = execute("kill -USR2 `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); + result += execute("kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); + } else if (execable("/sbin/pump")) { + result = execute("pump -i %iface% -k", ifd, exec); + } else if (execable("/sbin/dhclient")) { + result = execute("kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", ifd, exec); + } else if (execable("/sbin/dhcpcd")) { + result = execute("dhcpcd -k %iface%", ifd, exec); + } + return (result || static_down(ifd, exec)); +} + +static int bootp_up(struct interface_defn_t *ifd, execfn *exec) +{ + return( execute("bootpc [[--bootfile %bootfile%]] --dev %iface% " + "[[--server %server%]] [[--hwaddr %hwaddr%]] " + "--returniffail --serverbcast", ifd, exec)); +} + +static int ppp_up(struct interface_defn_t *ifd, execfn *exec) +{ + return( execute("pon [[%provider%]]", ifd, exec)); +} + +static int ppp_down(struct interface_defn_t *ifd, execfn *exec) +{ + return( execute("poff [[%provider%]]", ifd, exec)); +} + +static int wvdial_up(struct interface_defn_t *ifd, execfn *exec) +{ + return( execute("/sbin/start-stop-daemon --start -x /usr/bin/wvdial " + "-p /var/run/wvdial.%iface% -b -m -- [[ %provider% ]]", ifd, exec)); +} + +static int wvdial_down(struct interface_defn_t *ifd, execfn *exec) +{ + return( execute("/sbin/start-stop-daemon --stop -x /usr/bin/wvdial " + "-p /var/run/wvdial.%iface% -s 2", ifd, exec)); +} + +static struct method_t methods[] = +{ + { "wvdial", wvdial_up, wvdial_down, }, + { "ppp", ppp_up, ppp_down, }, + { "static", static_up, static_down, }, + { "bootp", bootp_up, static_down, }, + { "dhcp", dhcp_up, dhcp_down, }, + { "loopback", loopback_up, loopback_down, }, +}; + +struct address_family_t addr_inet = +{ + "inet", + sizeof(methods) / sizeof(struct method_t), + methods +}; + +#endif /* ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 */ + +static char *next_word(char **buf) +{ + unsigned short length; + char *word; + + if ((buf == NULL) || (*buf == NULL) || (**buf == '\0')) { + return NULL; + } + + /* Skip over leading whitespace */ + word = *buf; + while (isspace(*word)) { + ++word; + } + + /* Skip over comments */ + if (*word == '#') { + return(NULL); + } + + /* Find the length of this word */ + length = strcspn(word, " \t\n"); + if (length == 0) { + return(NULL); + } + *buf = word + length; + /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */ + if (**buf) { + **buf = '\0'; + (*buf)++; + } + + return word; +} + +static struct address_family_t *get_address_family(struct address_family_t *af[], char *name) +{ + int i; + + for (i = 0; af[i]; i++) { + if (strcmp(af[i]->name, name) == 0) { + return af[i]; + } + } + return NULL; +} + +static struct method_t *get_method(struct address_family_t *af, char *name) +{ + int i; + + for (i = 0; i < af->n_methods; i++) { + if (strcmp(af->method[i].name, name) == 0) { + return &af->method[i]; + } + } + return(NULL); +} + +static int duplicate_if(struct interface_defn_t *ifa, struct interface_defn_t *ifb) +{ + if (strcmp(ifa->iface, ifb->iface) != 0) { + return(0); + } + if (ifa->address_family != ifb->address_family) { + return(0); + } + return(1); +} + +static const llist_t *find_list_string(const llist_t *list, const char *string) +{ + while (list) { + if (strcmp(list->data, string) == 0) { + return(list); + } + list = list->link; + } + return(NULL); +} + +static struct interfaces_file_t *read_interfaces(const char *filename) +{ +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + struct mapping_defn_t *currmap = NULL; +#endif + struct interface_defn_t *currif = NULL; + struct interfaces_file_t *defn; + FILE *f; + char *firstword; + char *buf; + + enum { NONE, IFACE, MAPPING } currently_processing = NONE; + + defn = xmalloc(sizeof(struct interfaces_file_t)); + defn->autointerfaces = NULL; + defn->mappings = NULL; + defn->ifaces = NULL; + + f = bb_xfopen(filename, "r"); + + while ((buf = bb_get_chomped_line_from_file(f)) != NULL) { + char *buf_ptr = buf; + + firstword = next_word(&buf_ptr); + if (firstword == NULL) { + free(buf); + continue; /* blank line */ + } + + if (strcmp(firstword, "mapping") == 0) { +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + currmap = xmalloc(sizeof(struct mapping_defn_t)); + currmap->max_matches = 0; + currmap->n_matches = 0; + currmap->match = NULL; + + while ((firstword = next_word(&buf_ptr)) != NULL) { + if (currmap->max_matches == currmap->n_matches) { + currmap->max_matches = currmap->max_matches * 2 + 1; + currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches); + } + + currmap->match[currmap->n_matches++] = bb_xstrdup(firstword); + } + currmap->max_mappings = 0; + currmap->n_mappings = 0; + currmap->mapping = NULL; + currmap->script = NULL; + { + struct mapping_defn_t **where = &defn->mappings; + while (*where != NULL) { + where = &(*where)->next; + } + *where = currmap; + currmap->next = NULL; + } + debug_noise("Added mapping\n"); +#endif + currently_processing = MAPPING; + } else if (strcmp(firstword, "iface") == 0) { + { + char *iface_name; + char *address_family_name; + char *method_name; + struct address_family_t *addr_fams[] = { +#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 + &addr_inet, +#endif +#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6 + &addr_inet6, +#endif +#ifdef CONFIG_FEATURE_IFUPDOWN_IPX + &addr_ipx, +#endif + NULL + }; + + currif = xmalloc(sizeof(struct interface_defn_t)); + iface_name = next_word(&buf_ptr); + address_family_name = next_word(&buf_ptr); + method_name = next_word(&buf_ptr); + + if (buf_ptr == NULL) { + bb_error_msg("too few parameters for line \"%s\"", buf); + return NULL; + } + + /* ship any trailing whitespace */ + while (isspace(*buf_ptr)) { + ++buf_ptr; + } + + if (buf_ptr[0] != '\0') { + bb_error_msg("too many parameters \"%s\"", buf); + return NULL; + } + + currif->iface = bb_xstrdup(iface_name); + + currif->address_family = get_address_family(addr_fams, address_family_name); + if (!currif->address_family) { + bb_error_msg("unknown address type \"%s\"", address_family_name); + return NULL; + } + + currif->method = get_method(currif->address_family, method_name); + if (!currif->method) { + bb_error_msg("unknown method \"%s\"", method_name); + return NULL; + } + + currif->automatic = 1; + currif->max_options = 0; + currif->n_options = 0; + currif->option = NULL; + + { + struct interface_defn_t *tmp; + llist_t *iface_list; + iface_list = defn->ifaces; + while (iface_list) { + tmp = (struct interface_defn_t *) iface_list->data; + if (duplicate_if(tmp, currif)) { + bb_error_msg("duplicate interface \"%s\"", tmp->iface); + return NULL; + } + iface_list = iface_list->link; + } + + defn->ifaces = llist_add_to_end(defn->ifaces, (char*)currif); + } + debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name); + } + currently_processing = IFACE; + } else if (strcmp(firstword, "auto") == 0) { + while ((firstword = next_word(&buf_ptr)) != NULL) { + + /* Check the interface isnt already listed */ + if (find_list_string(defn->autointerfaces, firstword)) { + bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf); + } + + /* Add the interface to the list */ + defn->autointerfaces = llist_add_to_end(defn->autointerfaces, strdup(firstword)); + debug_noise("\nauto %s\n", firstword); + } + currently_processing = NONE; + } else { + switch (currently_processing) { + case IFACE: + { + int i; + + if (bb_strlen(buf_ptr) == 0) { + bb_error_msg("option with empty value \"%s\"", buf); + return NULL; + } + + if (strcmp(firstword, "up") != 0 + && strcmp(firstword, "down") != 0 + && strcmp(firstword, "pre-up") != 0 + && strcmp(firstword, "post-down") != 0) { + for (i = 0; i < currif->n_options; i++) { + if (strcmp(currif->option[i].name, firstword) == 0) { + bb_error_msg("duplicate option \"%s\"", buf); + return NULL; + } + } + } + } + if (currif->n_options >= currif->max_options) { + struct variable_t *opt; + + currif->max_options = currif->max_options + 10; + opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options); + currif->option = opt; + } + currif->option[currif->n_options].name = bb_xstrdup(firstword); + currif->option[currif->n_options].value = bb_xstrdup(buf_ptr); + if (!currif->option[currif->n_options].name) { + perror(filename); + return NULL; + } + if (!currif->option[currif->n_options].value) { + perror(filename); + return NULL; + } + debug_noise("\t%s=%s\n", currif->option[currif->n_options].name, + currif->option[currif->n_options].value); + currif->n_options++; + break; + case MAPPING: +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + if (strcmp(firstword, "script") == 0) { + if (currmap->script != NULL) { + bb_error_msg("duplicate script in mapping \"%s\"", buf); + return NULL; + } else { + currmap->script = bb_xstrdup(next_word(&buf_ptr)); + } + } else if (strcmp(firstword, "map") == 0) { + if (currmap->max_mappings == currmap->n_mappings) { + currmap->max_mappings = currmap->max_mappings * 2 + 1; + currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings); + } + currmap->mapping[currmap->n_mappings] = bb_xstrdup(next_word(&buf_ptr)); + currmap->n_mappings++; + } else { + bb_error_msg("misplaced option \"%s\"", buf); + return NULL; + } +#endif + break; + case NONE: + default: + bb_error_msg("misplaced option \"%s\"", buf); + return NULL; + } + } + free(buf); + } + if (ferror(f) != 0) { + bb_perror_msg_and_die("%s", filename); + } + fclose(f); + + return defn; +} + +static char *setlocalenv(char *format, char *name, char *value) +{ + char *result; + char *here; + char *there; + + result = xmalloc(bb_strlen(format) + bb_strlen(name) + bb_strlen(value) + 1); + + sprintf(result, format, name, value); + + for (here = there = result; *there != '=' && *there; there++) { + if (*there == '-') + *there = '_'; + if (isalpha(*there)) + *there = toupper(*there); + + if (isalnum(*there) || *there == '_') { + *here = *there; + here++; + } + } + memmove(here, there, bb_strlen(there) + 1); + + return result; +} + +static void set_environ(struct interface_defn_t *iface, char *mode) +{ + char **environend; + int i; + const int n_env_entries = iface->n_options + 5; + char **ppch; + + if (environ != NULL) { + for (ppch = environ; *ppch; ppch++) { + free(*ppch); + *ppch = NULL; + } + free(environ); + environ = NULL; + } + environ = xmalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ )); + environend = environ; + *environend = NULL; + + for (i = 0; i < iface->n_options; i++) { + if (strcmp(iface->option[i].name, "up") == 0 + || strcmp(iface->option[i].name, "down") == 0 + || strcmp(iface->option[i].name, "pre-up") == 0 + || strcmp(iface->option[i].name, "post-down") == 0) { + continue; + } + *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value); + *environend = NULL; + } + + *(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface); + *environend = NULL; + *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name); + *environend = NULL; + *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name); + *environend = NULL; + *(environend++) = setlocalenv("%s=%s", "MODE", mode); + *environend = NULL; + *(environend++) = setlocalenv("%s=%s", "PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"); + *environend = NULL; +} + +static int doit(char *str) +{ + if (verbose || no_act) { + printf("%s\n", str); + } + if (!no_act) { + pid_t child; + int status; + + fflush(NULL); + switch (child = fork()) { + case -1: /* failure */ + return 0; + case 0: /* child */ + execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, environ); + exit(127); + } + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + return 0; + } + } + return (1); +} + +static int execute_all(struct interface_defn_t *ifd, execfn *exec, const char *opt) +{ + int i; + char *buf; + for (i = 0; i < ifd->n_options; i++) { + if (strcmp(ifd->option[i].name, opt) == 0) { + if (!(*exec) (ifd->option[i].value)) { + return 0; + } + } + } + + bb_xasprintf(&buf, "run-parts /etc/network/if-%s.d", opt); + if ((*exec)(buf) != 1) { + return 0; + } + return 1; +} + +static int check(char *str) { + return str != NULL; +} + +static int iface_up(struct interface_defn_t *iface) +{ + if (!iface->method->up(iface,check)) return -1; + set_environ(iface, "start"); + if (!execute_all(iface, doit, "pre-up")) return 0; + if (!iface->method->up(iface, doit)) return 0; + if (!execute_all(iface, doit, "up")) return 0; + return 1; +} + +static int iface_down(struct interface_defn_t *iface) +{ + if (!iface->method->down(iface,check)) return -1; + set_environ(iface, "stop"); + if (!execute_all(iface, doit, "down")) return 0; + if (!iface->method->down(iface, doit)) return 0; + if (!execute_all(iface, doit, "post-down")) return 0; + return 1; +} + +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING +static int popen2(FILE **in, FILE **out, char *command, ...) +{ + va_list ap; + char *argv[11] = { command }; + int argc; + int infd[2], outfd[2]; + pid_t pid; + + argc = 1; + va_start(ap, command); + while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) { + argc++; + } + argv[argc] = NULL; /* make sure */ + va_end(ap); + + if (pipe(infd) != 0) { + return 0; + } + + if (pipe(outfd) != 0) { + close(infd[0]); + close(infd[1]); + return 0; + } + + fflush(NULL); + switch (pid = fork()) { + case -1: /* failure */ + close(infd[0]); + close(infd[1]); + close(outfd[0]); + close(outfd[1]); + return 0; + case 0: /* child */ + dup2(infd[0], 0); + dup2(outfd[1], 1); + close(infd[0]); + close(infd[1]); + close(outfd[0]); + close(outfd[1]); + execvp(command, argv); + exit(127); + default: /* parent */ + *in = fdopen(infd[1], "w"); + *out = fdopen(outfd[0], "r"); + close(infd[0]); + close(outfd[1]); + return pid; + } + /* unreached */ +} + +static char *run_mapping(char *physical, struct mapping_defn_t * map) +{ + FILE *in, *out; + int i, status; + pid_t pid; + + char *logical = bb_xstrdup(physical); + + /* Run the mapping script. */ + pid = popen2(&in, &out, map->script, physical, NULL); + + /* popen2() returns 0 on failure. */ + if (pid == 0) + return logical; + + /* Write mappings to stdin of mapping script. */ + for (i = 0; i < map->n_mappings; i++) { + fprintf(in, "%s\n", map->mapping[i]); + } + fclose(in); + waitpid(pid, &status, 0); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + /* If the mapping script exited successfully, try to + * grab a line of output and use that as the name of the + * logical interface. */ + char *new_logical = (char *)xmalloc(MAX_INTERFACE_LENGTH); + + if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) { + /* If we are able to read a line of output from the script, + * remove any trailing whitespace and use this value + * as the name of the logical interface. */ + char *pch = new_logical + bb_strlen(new_logical) - 1; + + while (pch >= new_logical && isspace(*pch)) + *(pch--) = '\0'; + + free(logical); + logical = new_logical; + } else { + /* If we are UNABLE to read a line of output, discard are + * freshly allocated memory. */ + free(new_logical); + } + } + + fclose(out); + + return logical; +} +#endif /* CONFIG_FEATURE_IFUPDOWN_MAPPING */ + +static llist_t *find_iface_state(llist_t *state_list, const char *iface) +{ + unsigned short iface_len = bb_strlen(iface); + llist_t *search = state_list; + + while (search) { + if ((strncmp(search->data, iface, iface_len) == 0) && + (search->data[iface_len] == '=')) { + return(search); + } + search = search->link; + } + return(NULL); +} + +extern int ifupdown_main(int argc, char **argv) +{ + int (*cmds) (struct interface_defn_t *) = NULL; + struct interfaces_file_t *defn; + FILE *state_fp = NULL; + llist_t *state_list = NULL; + llist_t *target_list = NULL; + const char *interfaces = "/etc/network/interfaces"; + const char *statefile = "/var/run/ifstate"; + +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + int run_mappings = 1; +#endif + int do_all = 0; + int force = 0; + int any_failures = 0; + int i; + + if (bb_applet_name[2] == 'u') { + /* ifup command */ + cmds = iface_up; + } else { + /* ifdown command */ + cmds = iface_down; + } + +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + while ((i = getopt(argc, argv, "i:hvnamf")) != -1) +#else + while ((i = getopt(argc, argv, "i:hvnaf")) != -1) +#endif + { + switch (i) { + case 'i': /* interfaces */ + interfaces = optarg; + break; + case 'v': /* verbose */ + verbose = 1; + break; + case 'a': /* all */ + do_all = 1; + break; + case 'n': /* no-act */ + no_act = 1; + break; +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + case 'm': /* no-mappings */ + run_mappings = 0; + break; +#endif + case 'f': /* force */ + force = 1; + break; + default: + bb_show_usage(); + break; + } + } + + if (argc - optind > 0) { + if (do_all) { + bb_show_usage(); + } + } else { + if (!do_all) { + bb_show_usage(); + } + } + + debug_noise("reading %s file:\n", interfaces); + defn = read_interfaces(interfaces); + debug_noise("\ndone reading %s\n\n", interfaces); + + if (!defn) { + exit(EXIT_FAILURE); + } + + if (no_act) { + state_fp = fopen(statefile, "r"); + } + + /* Create a list of interfaces to work on */ + if (do_all) { + if (cmds == iface_up) { + target_list = defn->autointerfaces; + } else { +#if 0 + /* iface_down */ + llist_t *new_item; + const llist_t *list = state_list; + while (list) { + new_item = xmalloc(sizeof(llist_t)); + new_item->data = strdup(list->data); + new_item->link = NULL; + list = target_list; + if (list == NULL) + target_list = new_item; + else { + while (list->link) { + list = list->link; + } + list = new_item; + } + list = list->link; + } + target_list = defn->autointerfaces; +#else + + /* iface_down */ + const llist_t *list = state_list; + while (list) { + target_list = llist_add_to_end(target_list, strdup(list->data)); + list = list->link; + } + target_list = defn->autointerfaces; +#endif + } + } else { + target_list = llist_add_to_end(target_list, argv[optind]); + } + + + /* Update the interfaces */ + while (target_list) { + llist_t *iface_list; + struct interface_defn_t *currif; + char *iface; + char *liface; + char *pch; + int okay = 0; + int cmds_ret; + + iface = strdup(target_list->data); + target_list = target_list->link; + + pch = strchr(iface, '='); + if (pch) { + *pch = '\0'; + liface = strdup(pch + 1); + } else { + liface = strdup(iface); + } + + if (!force) { + const llist_t *iface_state = find_iface_state(state_list, iface); + + if (cmds == iface_up) { + /* ifup */ + if (iface_state) { + bb_error_msg("interface %s already configured", iface); + continue; + } + } else { + /* ifdown */ + if (iface_state) { + bb_error_msg("interface %s not configured", iface); + continue; + } + } + } + +#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING + if ((cmds == iface_up) && run_mappings) { + struct mapping_defn_t *currmap; + + for (currmap = defn->mappings; currmap; currmap = currmap->next) { + + for (i = 0; i < currmap->n_matches; i++) { + if (fnmatch(currmap->match[i], liface, 0) != 0) + continue; + if (verbose) { + printf("Running mapping script %s on %s\n", currmap->script, liface); + } + liface = run_mapping(iface, currmap); + break; + } + } + } +#endif + + + iface_list = defn->ifaces; + while (iface_list) { + currif = (struct interface_defn_t *) iface_list->data; + if (strcmp(liface, currif->iface) == 0) { + char *oldiface = currif->iface; + + okay = 1; + currif->iface = iface; + + debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name); + + /* Call the cmds function pointer, does either iface_up() or iface_down() */ + cmds_ret = cmds(currif); + if (cmds_ret == -1) { + bb_error_msg("Don't seem to have all the variables for %s/%s.", + liface, currif->address_family->name); + any_failures += 1; + } else if (cmds_ret == 0) { + any_failures += 1; + } + + currif->iface = oldiface; + } + iface_list = iface_list->link; + } + if (verbose) { + printf("\n"); + } + + if (!okay && !force) { + bb_error_msg("Ignoring unknown interface %s", liface); + any_failures += 1; + } else { + llist_t *iface_state = find_iface_state(state_list, iface); + + if (cmds == iface_up) { + char *newiface = xmalloc(bb_strlen(iface) + 1 + bb_strlen(liface) + 1); + sprintf(newiface, "%s=%s", iface, liface); + if (iface_state == NULL) { + state_list = llist_add_to_end(state_list, newiface); + } else { + free(iface_state->data); + iface_state->data = newiface; + } + } else if (cmds == iface_down) { + /* Remove an interface from the linked list */ + if (iface_state) { + /* This needs to be done better */ + free(iface_state->data); + free(iface_state->link); + if (iface_state->link) { + iface_state->data = iface_state->link->data; + iface_state->link = iface_state->link->link; + } else { + iface_state->data = NULL; + iface_state->link = NULL; + } + } + } + } + } + + /* Actually write the new state */ + if (!no_act) { + + if (state_fp) + fclose(state_fp); + state_fp = bb_xfopen(statefile, "a+"); + + if (ftruncate(fileno(state_fp), 0) < 0) { + bb_error_msg_and_die("failed to truncate statefile %s: %s", statefile, strerror(errno)); + } + + rewind(state_fp); + + while (state_list) { + if (state_list->data) { + fputs(state_list->data, state_fp); + fputc('\n', state_fp); + } + state_list = state_list->link; + } + fflush(state_fp); + } + + /* Cleanup */ + if (state_fp != NULL) { + fclose(state_fp); + state_fp = NULL; + } + + if (any_failures) + return 1; + return 0; +} diff --git a/busybox/networking/inetd.c b/busybox/networking/inetd.c new file mode 100644 index 000000000..169cc8716 --- /dev/null +++ b/busybox/networking/inetd.c @@ -0,0 +1,1221 @@ +/* + * Copyright (c) 1983,1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David A. Holland. + * + * Busybox port by Vladimir Oleynik (C) 2001-2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Inetd - Internet super-server + * + * This program invokes all internet services as needed. + * connection-oriented services are invoked each time a + * connection is made, by creating a process. This process + * is passed the connection as file descriptor 0 and is + * expected to do a getpeername to find out the source host + * and port. + * + * Datagram oriented services are invoked when a datagram + * arrives; a process is created and passed a pending message + * on file descriptor 0. Datagram servers may either connect + * to their peer, freeing up the original socket for inetd + * to receive further messages on, or ``take over the socket'', + * processing all arriving datagrams and, eventually, timing + * out. The first type of server is said to be ``multi-threaded''; + * the second type of server ``single-threaded''. + * + * Inetd uses a configuration file which is read at startup + * and, possibly, at some later time in response to a hangup signal. + * The configuration file is ``free format'' with fields given in the + * order shown below. Continuation lines for an entry must being with + * a space or tab. All fields must be present in each entry. + * + * service name must be in /etc/services + * socket type stream/dgram/raw/rdm/seqpacket + * protocol must be in /etc/protocols + * wait/nowait[.max] single-threaded/multi-threaded, max # + * user[.group] user/group to run daemon as + * server program full path name + * server program arguments maximum of MAXARGS (20) + * + * RPC services unsupported + * + * Comment lines are indicated by a `#' in column 1. + */ + +/* + * Here's the scoop concerning the user.group feature: + * + * 1) No group listed. + * + * a) for root: NO setuid() or setgid() is done + * + * b) nonroot: setuid() + * setgid(primary group as found in passwd) + * initgroups(name, primary group) + * + * 2) set-group-option on. + * + * a) for root: NO setuid() + * setgid(specified group) + * setgroups(1, specified group) + * + * b) nonroot: setuid() + * setgid(specified group) + * initgroups(name, specified group) + * + * All supplementary groups are discarded at startup in case inetd was + * run manually. + */ + +#define __USE_BSD_SIGNAL + +#include "busybox.h" + + +#ifndef __linux__ +#ifndef RLIMIT_NOFILE +#define RLIMIT_NOFILE RLIMIT_OFILE +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef OPEN_MAX +#define OPEN_MAX 64 +#endif + +#define _PATH_INETDCONF "/etc/inetd.conf" +#define _PATH_INETDPID "/var/run/inetd.pid" + +#define TOOMANY 40 /* don't start more than TOOMANY */ +#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ +#define RETRYTIME (60*10) /* retry after bind or server fail */ +#define MAXARGV 20 + +#define se_ctrladdr se_un.se_un_ctrladdr +#define se_ctrladdr_in se_un.se_un_ctrladdr_in +#define se_ctrladdr_un se_un.se_un_ctrladdr_un + +/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ +#define FD_MARGIN (8) + +/* Check unsupporting builtin */ +#if defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN +# define INETD_FEATURE_ENABLED +#endif + +typedef struct servtab_s { + char *se_service; /* name of service */ + int se_socktype; /* type of socket to use */ + int se_family; /* address family */ + char *se_proto; /* protocol used */ + short se_wait; /* single threaded server */ + short se_checked; /* looked at during merge */ + char *se_user; /* user name to run as */ + char *se_group; /* group name to run as */ +#ifdef INETD_FEATURE_ENABLED + const struct biltin *se_bi; /* if built-in, description */ +#endif + char *se_server; /* server program */ + char *se_argv[MAXARGV+1]; /* program arguments */ + int se_fd; /* open descriptor */ + union { + struct sockaddr se_un_ctrladdr; + struct sockaddr_in se_un_ctrladdr_in; + struct sockaddr_un se_un_ctrladdr_un; + } se_un; /* bound address */ + int se_ctrladdr_size; + int se_max; /* max # of instances of this service */ + int se_count; /* number started since se_time */ + struct timeval se_time; /* start of se_count */ + struct servtab_s *se_next; +} servtab_t; + +static servtab_t *servtab; + +#ifdef INETD_FEATURE_ENABLED +struct biltin { + const char *bi_service; /* internally provided service name */ + int bi_socktype; /* type of socket supported */ + short bi_fork; /* 1 if should fork before call */ + short bi_wait; /* 1 if should wait for child */ + void (*bi_fn)(int, servtab_t *); /* fn which performs it */ +}; + + /* Echo received data */ +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO +static void echo_stream(int, servtab_t *); +static void echo_dg(int, servtab_t *); +#endif + /* Internet /dev/null */ +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD +static void discard_stream(int, servtab_t *); +static void discard_dg(int, servtab_t *); +#endif + /* Return 32 bit time since 1900 */ +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME +static void machtime_stream(int, servtab_t *); +static void machtime_dg(int, servtab_t *); +#endif + /* Return human-readable time */ +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME +static void daytime_stream(int, servtab_t *); +static void daytime_dg(int, servtab_t *); +#endif + /* Familiar character generator */ +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN +static void chargen_stream(int, servtab_t *); +static void chargen_dg(int, servtab_t *); +#endif + +static const struct biltin biltins[] = { +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO + /* Echo received data */ + { "echo", SOCK_STREAM, 1, 0, echo_stream, }, + { "echo", SOCK_DGRAM, 0, 0, echo_dg, }, +#endif +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD + /* Internet /dev/null */ + { "discard", SOCK_STREAM, 1, 0, discard_stream, }, + { "discard", SOCK_DGRAM, 0, 0, discard_dg, }, +#endif +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME + /* Return 32 bit time since 1900 */ + { "time", SOCK_STREAM, 0, 0, machtime_stream, }, + { "time", SOCK_DGRAM, 0, 0, machtime_dg, }, +#endif +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME + /* Return human-readable time */ + { "daytime", SOCK_STREAM, 0, 0, daytime_stream, }, + { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, }, +#endif +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN + /* Familiar character generator */ + { "chargen", SOCK_STREAM, 1, 0, chargen_stream, }, + { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, }, +#endif + { NULL, 0, 0, 0, NULL } +}; +#endif /* INETD_FEATURE_ENABLED */ + +#ifdef RLIMIT_NOFILE +static struct rlimit rlim_ofile; +#endif + +/* Length of socket listen queue. Should be per-service probably. */ +static int global_queuelen = 128; + +static FILE *fconfig; +static sigset_t blockmask; +static sigset_t emptymask; +static fd_set allsock; +static int nsock; +static int maxsock; +static int timingout; +static int rlim_ofile_cur = OPEN_MAX; +static const char *CONFIG = _PATH_INETDCONF; + +static void +syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) + __attribute__ ((noreturn, format (printf, 2, 3))); + +static void +syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) +{ + char buf[50]; + va_list p; + + va_start(p, msg); + vsyslog(LOG_ERR, msg, p); + if (se_socktype != SOCK_STREAM) + recv(0, buf, sizeof (buf), 0); + _exit(1); +} + +static char * inetd_strdup(const char *s) +{ + char *ms = strdup(s); + + if(ms == NULL) + syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m"); + return ms; +} + + +static servtab_t *getconfigent(void) +{ + static servtab_t serv; + servtab_t *sep = &serv; + int argc; + char *cp = NULL; + char *cp_ptr; + char *cp_ptr_ptr = NULL; + +more: + free(cp); + cp = bb_get_chomped_line_from_file(fconfig); + if (feof(fconfig)) { + free(cp); + return (NULL); + } + if ((cp == NULL) || (*cp == '#')) { + goto more; + } + /* make bind 0.0.0.0 and other zero default */ + memset((char *)sep, 0, sizeof *sep); + + cp_ptr = strtok_r(cp, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* Error */ + goto more; + } + sep->se_service = inetd_strdup(cp_ptr); + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* Error */ + goto more; + } + if (strcmp(cp_ptr, "stream") == 0) + sep->se_socktype = SOCK_STREAM; + else if (strcmp(cp_ptr, "dgram") == 0) + sep->se_socktype = SOCK_DGRAM; + else if (strcmp(cp_ptr, "rdm") == 0) + sep->se_socktype = SOCK_RDM; + else if (strcmp(cp_ptr, "seqpacket") == 0) + sep->se_socktype = SOCK_SEQPACKET; + else if (strcmp(cp_ptr, "raw") == 0) + sep->se_socktype = SOCK_RAW; + else + sep->se_socktype = -1; + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* error */ + goto more; + } + if (strcmp(cp_ptr, "unix") == 0) { + sep->se_family = AF_UNIX; + } else { + if (strncmp(cp_ptr, "rpc/", 4) == 0) { + syslog(LOG_ERR, "%s: rpc services not supported", + sep->se_service); + goto more; + } + sep->se_family = AF_INET; + } + sep->se_proto = inetd_strdup(cp_ptr); + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* error */ + goto more; + } + { + char *s = strchr(cp_ptr, '.'); + if (s) { + *s++ = '\0'; + sep->se_max = atoi(s); + } else + sep->se_max = TOOMANY; + } + sep->se_wait = strcmp(cp_ptr, "wait") == 0; + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* error */ + goto more; + } + + sep->se_user = inetd_strdup(cp_ptr); + { + char *cp_ptr2 = strchr(sep->se_user, '.'); + + if (cp_ptr2) { + *cp_ptr2++ = '\0'; + } + sep->se_group = cp_ptr2; + } + + cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); + if (cp_ptr == NULL) { + /* error */ + goto more; + } + if (strcmp(cp_ptr, "internal") == 0) { +#ifdef INETD_FEATURE_ENABLED + const struct biltin *bi; + + for (bi = biltins; bi->bi_service; bi++) { + if ((bi->bi_socktype == sep->se_socktype) && + (strcmp(bi->bi_service, sep->se_service) == 0)) { + break; + } + } + if (bi->bi_service == 0) { + syslog(LOG_ERR, "internal service %s unknown", sep->se_service); + goto more; + } + sep->se_bi = bi; + sep->se_wait = bi->bi_wait; +#else + syslog(LOG_ERR, "internal service %s unknown", cp_ptr); + goto more; +#endif + } +#ifdef INETD_FEATURE_ENABLED + else { + sep->se_bi = NULL; + } +#endif + sep->se_server = inetd_strdup(cp_ptr); + + argc = 0; + while ((cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr)) != NULL) { + if (argc < MAXARGV) { + sep->se_argv[argc++] = inetd_strdup(cp_ptr); + } + } + free(cp); + + return (sep); +} + +static void freeconfig(servtab_t *cp) +{ + int i; + + free(cp->se_service); + free(cp->se_proto); + free(cp->se_user); + /* Note: se_group is part of the newstr'ed se_user */ + free(cp->se_server); + for (i = 0; i < MAXARGV; i++) + free(cp->se_argv[i]); +} + +#ifdef INETD_FEATURE_ENABLED +static char **Argv; +static char *LastArg; + +static void setproctitle(char *a, int s) +{ + size_t size; + char *cp; + struct sockaddr_in sn; + char buf[80]; + + cp = Argv[0]; + size = sizeof(sn); + if (getpeername(s, (struct sockaddr *)&sn, &size) == 0) + (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr)); + else + (void) sprintf(buf, "-%s", a); + strncpy(cp, buf, LastArg - cp); + cp += strlen(cp); + while (cp < LastArg) + *cp++ = ' '; +} +#endif /* INETD_FEATURE_ENABLED */ + + +static void setup(servtab_t *sep) +{ + int on = 1; + + if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { + syslog(LOG_ERR, "%s/%s: socket: %m", + sep->se_service, sep->se_proto); + return; + } + if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, + sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); + if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) { + syslog(LOG_ERR, "%s/%s: bind: %m", + sep->se_service, sep->se_proto); + (void) close(sep->se_fd); + sep->se_fd = -1; + if (!timingout) { + timingout = 1; + alarm(RETRYTIME); + } + return; + } + if (sep->se_socktype == SOCK_STREAM) + listen(sep->se_fd, global_queuelen); + + FD_SET(sep->se_fd, &allsock); + nsock++; + if (sep->se_fd > maxsock) { + maxsock = sep->se_fd; + if (maxsock > rlim_ofile_cur - FD_MARGIN) { +#ifdef RLIMIT_NOFILE +# define FD_CHUNK 32 + struct rlimit rl; + + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { + syslog(LOG_ERR, "getrlimit: %m"); + return; + } + rl.rlim_cur = rl.rlim_max < (rl.rlim_cur + FD_CHUNK) ? rl.rlim_max : (rl.rlim_cur + FD_CHUNK); + if (rl.rlim_cur <= rlim_ofile_cur) { + syslog(LOG_ERR, +# if _FILE_OFFSET_BITS == 64 + "bump_nofile: cannot extend file limit, max = %lld", +# else + "bump_nofile: cannot extend file limit, max = %ld", +# endif + rl.rlim_cur); + return; + } + + if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { + syslog(LOG_ERR, "setrlimit: %m"); + return; + } + + rlim_ofile_cur = rl.rlim_cur; + return; +#else + syslog(LOG_ERR, "bump_nofile: cannot extend file limit"); + return; +#endif /* RLIMIT_NOFILE */ + } + } +} + +static void config(int signum) +{ + servtab_t *sep, *cp, **sepp; + sigset_t oldmask; + unsigned n; + + (void)signum; + + if (fconfig != NULL) { + fseek(fconfig, 0L, L_SET); + } else { + fconfig = fopen(CONFIG, "r"); + if (fconfig == NULL) { + syslog(LOG_ERR, "%s: %m", CONFIG); + return; + } + } + + for (sep = servtab; sep; sep = sep->se_next) + sep->se_checked = 0; + while ((cp = getconfigent()) != NULL) { + for (sep = servtab; sep; sep = sep->se_next) + if (strcmp(sep->se_service, cp->se_service) == 0 && + strcmp(sep->se_proto, cp->se_proto) == 0) + break; + if (sep != 0) { + int i; + +#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;} + + sigprocmask(SIG_BLOCK, &emptymask, &oldmask); + /* + * sep->se_wait may be holding the pid of a daemon + * that we're waiting for. If so, don't overwrite + * it unless the config file explicitly says don't + * wait. + */ + if ( +#ifdef INETD_FEATURE_ENABLED + cp->se_bi == 0 && +#endif + (sep->se_wait == 1 || cp->se_wait == 0)) + sep->se_wait = cp->se_wait; + if (cp->se_max != sep->se_max) + SWAP(int, cp->se_max, sep->se_max); + if (cp->se_user) + SWAP(char *, sep->se_user, cp->se_user); + if (cp->se_group) + SWAP(char *, sep->se_group, cp->se_group); + if (cp->se_server) + SWAP(char *, sep->se_server, cp->se_server); + for (i = 0; i < MAXARGV; i++) + SWAP(char *, sep->se_argv[i], cp->se_argv[i]); +#undef SWAP + sigprocmask(SIG_SETMASK, &oldmask, NULL); + // This freeconfig() is probably a bug, since it will try and free() + // each of the argv[] values, which are really just pointers + // into the middle of a single line buffer for the config file. + //freeconfig(cp); // BUG? + } else { + sep = (servtab_t *)xmalloc(sizeof (*sep)); + *sep = *cp; + sep->se_fd = -1; + sigprocmask(SIG_BLOCK, &blockmask, &oldmask); + sep->se_next = servtab; + servtab = sep; + sigprocmask(SIG_SETMASK, &oldmask, NULL); + } + sep->se_checked = 1; + + switch (sep->se_family) { + case AF_UNIX: + if (sep->se_fd != -1) + break; + (void)unlink(sep->se_service); + n = strlen(sep->se_service); + if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1) + n = sizeof(sep->se_ctrladdr_un.sun_path) - 1; + strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n); + sep->se_ctrladdr_un.sun_family = AF_UNIX; + sep->se_ctrladdr_size = n + + sizeof sep->se_ctrladdr_un.sun_family; + setup(sep); + break; + case AF_INET: + sep->se_ctrladdr_in.sin_family = AF_INET; + sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; + { + u_short port = bb_lookup_port(sep->se_service, sep->se_proto, 0); + + if (port == 0) { + syslog(LOG_ERR, + "%s/%s: unknown service", + sep->se_service, sep->se_proto); + continue; + } + if (port != sep->se_ctrladdr_in.sin_port) { + sep->se_ctrladdr_in.sin_port = port; + if (sep->se_fd != -1) { + FD_CLR(sep->se_fd, &allsock); + nsock--; + (void) close(sep->se_fd); + } + sep->se_fd = -1; + } + if (sep->se_fd == -1) + setup(sep); + } + } + } + if (fconfig) { + (void) fclose(fconfig); + fconfig = NULL; + } + /* + * Purge anything not looked at above. + */ + sigprocmask(SIG_SETMASK, &blockmask, &oldmask); + sepp = &servtab; + while ((sep = *sepp) != NULL) { + if (sep->se_checked) { + sepp = &sep->se_next; + continue; + } + *sepp = sep->se_next; + if (sep->se_fd != -1) { + FD_CLR(sep->se_fd, &allsock); + nsock--; + (void) close(sep->se_fd); + } + if (sep->se_family == AF_UNIX) + (void)unlink(sep->se_service); + freeconfig(sep); + free((char *)sep); + } + sigprocmask(SIG_SETMASK, &oldmask, NULL); +} + + + +static void reapchild(int signum) +{ + int status; + int pid; + servtab_t *sep; + + (void)signum; + for (;;) { + pid = wait3(&status, WNOHANG, (struct rusage *)0); + if (pid <= 0) + break; + for (sep = servtab; sep; sep = sep->se_next) + if (sep->se_wait == pid) { + if (WIFEXITED(status) && WEXITSTATUS(status)) + syslog(LOG_WARNING, + "%s: exit status 0x%x", + sep->se_server, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + syslog(LOG_WARNING, + "%s: exit signal 0x%x", + sep->se_server, WTERMSIG(status)); + sep->se_wait = 1; + FD_SET(sep->se_fd, &allsock); + nsock++; + } + } +} + +static void retry(int signum) +{ + servtab_t *sep; + + (void)signum; + timingout = 0; + for (sep = servtab; sep; sep = sep->se_next) { + if (sep->se_fd == -1) { + switch (sep->se_family) { + case AF_UNIX: + case AF_INET: + setup(sep); + break; + } + } + } +} + +static void goaway(int signum) +{ + servtab_t *sep; + + (void)signum; + for (sep = servtab; sep; sep = sep->se_next) + if (sep->se_fd != -1 && sep->se_family == AF_UNIX) + (void)unlink(sep->se_service); + (void)unlink(_PATH_INETDPID); + exit(0); +} + + + +extern int inetd_main(int argc, char *argv[]) +{ + servtab_t *sep; + struct group *grp = NULL; + struct sigaction sa; + int pid; + unsigned long opt; + char *sq; + gid_t gid; + +#ifdef INETD_FEATURE_ENABLED + extern char **environ; +#endif + + gid = getgid(); + setgroups(1, &gid); + +#ifdef INETD_FEATURE_ENABLED + Argv = argv; + if (environ == 0 || *environ == 0) + environ = argv; + while (*environ) + environ++; + LastArg = environ[-1] + strlen(environ[-1]); +#endif + +#if defined(__uClinux__) + opt = bb_getopt_ulflags(argc, argv, "q:f", &sq); + if (!(opt & 2)) { + daemon(0, 0); + /* reexec for vfork() do continue parent */ + vfork_daemon_rexec(argc, argv, "-f"); + } +#else + opt = bb_getopt_ulflags(argc, argv, "q:", &sq); + daemon(0, 0); +#endif /* uClinux */ + + if(opt & 1) { + global_queuelen = atoi(sq); + if (global_queuelen < 8) global_queuelen=8; + } + argc -= optind; + argv += optind; + + if (argc > 0) + CONFIG = argv[0]; + + openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + { + FILE *fp; + + if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) { + fprintf(fp, "%u\n", getpid()); + (void)fclose(fp); + } + } + +#ifdef RLIMIT_NOFILE + if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { + syslog(LOG_ERR, "getrlimit: %m"); + } else { + rlim_ofile_cur = rlim_ofile.rlim_cur; + if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ + rlim_ofile_cur = OPEN_MAX; + } +#endif + + config(0); + + sigemptyset(&emptymask); + sigemptyset(&blockmask); + sigaddset(&blockmask, SIGCHLD); + sigaddset(&blockmask, SIGHUP); + sigaddset(&blockmask, SIGALRM); + + memset(&sa, 0, sizeof(sa)); + sa.sa_mask = blockmask; + sa.sa_handler = retry; + sigaction(SIGALRM, &sa, NULL); + sa.sa_handler = config; + sigaction(SIGHUP, &sa, NULL); + sa.sa_handler = reapchild; + sigaction(SIGCHLD, &sa, NULL); + sa.sa_handler = goaway; + sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = goaway; + sigaction(SIGINT, &sa, NULL); + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); + + { + /* space for daemons to overwrite environment for ps */ +#define DUMMYSIZE 100 + char dummy[DUMMYSIZE]; + + (void)memset(dummy, 'x', DUMMYSIZE - 1); + dummy[DUMMYSIZE - 1] = '\0'; + + (void)setenv("inetd_dummy", dummy, 1); + } + + for (;;) { + fd_set readable; + int ctrl; + int n; + + if (nsock == 0) { + sigprocmask(SIG_BLOCK, &blockmask, NULL); + while (nsock == 0) { + sigsuspend(&emptymask); + } + sigprocmask(SIG_SETMASK, &emptymask, NULL); + } + readable = allsock; + n = select(maxsock + 1, &readable, (fd_set *)0, (fd_set *)0, (struct timeval *)0); + if (n <= 0) { + if (n < 0 && errno != EINTR) { + syslog(LOG_WARNING, "select: %m"); + } + sleep(1); + continue; + } + for (sep = servtab; n && sep; sep = sep->se_next) { + if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { + n--; + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { + /* Fixed AGC */ + fcntl(sep->se_fd, F_SETFL, O_NDELAY); + /* --------- */ + ctrl = accept(sep->se_fd, NULL, NULL); + fcntl(sep->se_fd, F_SETFL, 0); + if (ctrl < 0) { + if (errno == EINTR || errno == EWOULDBLOCK) { + continue; + } + syslog(LOG_WARNING, "accept (for %s): %m", + sep->se_service); + continue; + } + } else { + ctrl = sep->se_fd; + } + sigprocmask(SIG_BLOCK, &blockmask, NULL); + pid = 0; +#ifdef INETD_FEATURE_ENABLED + if (sep->se_bi == 0 || sep->se_bi->bi_fork) +#endif + { + if (sep->se_count++ == 0) { + gettimeofday(&sep->se_time, (struct timezone *)0); + } + else if (sep->se_count >= sep->se_max) { + struct timeval now; + + gettimeofday(&now, (struct timezone *)0); + if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { + sep->se_time = now; + sep->se_count = 1; + } else { + syslog(LOG_ERR, + "%s/%s server failing (looping), service terminated", + sep->se_service, sep->se_proto); + FD_CLR(sep->se_fd, &allsock); + close(sep->se_fd); + sep->se_fd = -1; + sep->se_count = 0; + nsock--; + sigprocmask(SIG_SETMASK, &emptymask, NULL); + if (!timingout) { + timingout = 1; + alarm(RETRYTIME); + } + continue; + } + } + pid = fork(); + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + if (sep->se_socktype == SOCK_STREAM) { + close(ctrl); + } + sigprocmask(SIG_SETMASK, &emptymask, NULL); + sleep(1); + continue; + } + if (pid && sep->se_wait) { + sep->se_wait = pid; + FD_CLR(sep->se_fd, &allsock); + nsock--; + } + } + sigprocmask(SIG_SETMASK, &emptymask, NULL); + if (pid == 0) { +#ifdef INETD_FEATURE_ENABLED + if (sep->se_bi) { + (*sep->se_bi->bi_fn)(ctrl, sep); + } else +#endif + { + struct passwd *pwd = getpwnam(sep->se_user); + if (pwd == NULL) { + syslog_err_and_discard_dg( + sep->se_socktype, + "getpwnam: %s: No such user", + sep->se_user); + } + if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) { + syslog_err_and_discard_dg(sep->se_socktype, + "getgrnam: %s: No such group", sep->se_group); + } + /* + * Ok. There are four cases here: + * 1. nonroot user, no group specified + * 2. nonroot user, some group specified + * 3. root user, no group specified + * 4. root user, some group specified + * In cases 2 and 4 we setgid to the specified + * group. In cases 1 and 2 we run initgroups + * to run with the groups of the given user. + * In case 4 we do setgroups to run with the + * given group. In case 3 we do nothing. + */ + if (pwd->pw_uid) { + if (sep->se_group) { + pwd->pw_gid = grp->gr_gid; + } + setgid((gid_t)pwd->pw_gid); + initgroups(pwd->pw_name, pwd->pw_gid); + setuid((uid_t)pwd->pw_uid); + } else if (sep->se_group) { + setgid((gid_t)grp->gr_gid); + setgroups(1, &grp->gr_gid); + } + dup2(ctrl, 0); + close(ctrl); + dup2(0, 1); + dup2(0, 2); +#ifdef RLIMIT_NOFILE + if (rlim_ofile.rlim_cur != rlim_ofile_cur) { + if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { + syslog(LOG_ERR,"setrlimit: %m"); + } + } +#endif + for (ctrl = rlim_ofile_cur-1; --ctrl > 2; ) { + (void)close(ctrl); + } + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGPIPE, &sa, NULL); + + execv(sep->se_server, sep->se_argv); + syslog_err_and_discard_dg(sep->se_socktype, "execv %s: %m", sep->se_server); + } + } + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { + close(ctrl); + } + } + } + } +} + + +/* + * Internet services provided internally by inetd: + */ +#define BUFSIZE 4096 + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO +/* Echo service -- echo data back */ +static void echo_stream(int s, servtab_t *sep) +{ + char buffer[BUFSIZE]; + int i; + + setproctitle(sep->se_service, s); + while ((i = read(s, buffer, sizeof(buffer))) > 0 && + write(s, buffer, i) > 0) + ; + exit(0); +} + +/* Echo service -- echo data back */ +static void echo_dg(int s, servtab_t *sep) +{ + char buffer[BUFSIZE]; + int i; + size_t size; + struct sockaddr sa; + + (void)sep; + + size = sizeof(sa); + if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0) + return; + (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */ + + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD +/* Discard service -- ignore data */ +static void discard_stream(int s, servtab_t *sep) +{ + char buffer[BUFSIZE]; + + setproctitle(sep->se_service, s); + while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) || + errno == EINTR) + ; + exit(0); +} + +/* Discard service -- ignore data */ +static void discard_dg(int s, servtab_t *sep) +{ + char buffer[BUFSIZE]; + (void)sep; + read(s, buffer, sizeof(buffer)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */ + + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN +#include +#define LINESIZ 72 +static char ring[128]; +static char *endring; + +static void initring(void) +{ + int i; + + endring = ring; + + for (i = 0; i <= 128; ++i) + if (isprint(i)) + *endring++ = i; +} + +/* Character generator */ +static void chargen_stream(int s, servtab_t *sep) +{ + char *rs; + int len; + char text[LINESIZ+2]; + + setproctitle(sep->se_service, s); + + if (!endring) { + initring(); + rs = ring; + } + + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + for (rs = ring;;) { + if ((len = endring - rs) >= LINESIZ) + memcpy(rs, text, LINESIZ); + else { + memcpy(rs, text, len); + memcpy(ring, text + len, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + if (write(s, text, sizeof(text)) != sizeof(text)) + break; + } + exit(0); +} + +/* Character generator */ +static void chargen_dg(int s, servtab_t *sep) +{ + struct sockaddr sa; + static char *rs; + size_t len, size; + char text[LINESIZ+2]; + + (void)sep; + + if (endring == 0) { + initring(); + rs = ring; + } + + size = sizeof(sa); + if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) + return; + + if ((len = endring - rs) >= LINESIZ) + memcpy(rs, text, LINESIZ); + else { + memcpy(rs, text, len); + memcpy(ring, text + len, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */ + + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME +/* + * Return a machine readable date and time, in the form of the + * number of seconds since midnight, Jan 1, 1900. Since gettimeofday + * returns the number of seconds since midnight, Jan 1, 1970, + * we must add 2208988800 seconds to this figure to make up for + * some seventy years Bell Labs was asleep. + */ + +static long machtime(void) +{ + struct timeval tv; + + if (gettimeofday(&tv, (struct timezone *)0) < 0) { + fprintf(stderr, "Unable to get time of day\n"); + return (0L); + } + return (htonl((long)tv.tv_sec + 2208988800UL)); +} + +static void machtime_stream(int s, servtab_t *sep) +{ + long result; + (void)sep; + + result = machtime(); + write(s, (char *) &result, sizeof(result)); +} + +static void machtime_dg(int s, servtab_t *sep) +{ + long result; + struct sockaddr sa; + size_t size; + (void)sep; + + size = sizeof(sa); + if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0) + return; + result = machtime(); + (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */ + + +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME +/* Return human-readable time of day */ +static int human_readable_time_sprintf(char *buffer) +{ + time_t clocc = time(NULL); + + return sprintf(buffer, "%.24s\r\n", ctime(&clocc)); +} + +static void daytime_stream(int s, servtab_t *sep) +{ + char buffer[256]; + size_t st = human_readable_time_sprintf(buffer); + + (void)sep; + + write(s, buffer, st); +} + +/* Return human-readable time of day */ +static void daytime_dg(int s, servtab_t *sep) +{ + char buffer[256]; + struct sockaddr sa; + size_t size; + + (void)sep; + + size = sizeof(sa); + if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) + return; + size = human_readable_time_sprintf(buffer); + sendto(s, buffer, size, 0, &sa, sizeof(sa)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */ diff --git a/busybox/networking/ip.c b/busybox/networking/ip.c new file mode 100644 index 000000000..cfdbf8b96 --- /dev/null +++ b/busybox/networking/ip.c @@ -0,0 +1,115 @@ +/* + * ip.c "ip" utility frontend. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "./libiproute/utils.h" +#include "./libiproute/ip_common.h" + +#include "busybox.h" + +#if 0 +int preferred_family = AF_UNSPEC; +int oneline = 0; +char * _SL_ = NULL; + +void ip_parse_common_args(int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (argc > 1) { + char *opt = argv[1]; + + if (strcmp(opt,"--") == 0) { + argc--; argv++; + break; + } + + if (opt[0] != '-') + break; + + if (opt[1] == '-') + opt++; + + if (matches(opt, "-family") == 0) { + argc--; + argv++; + if (strcmp(argv[1], "inet") == 0) + preferred_family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + preferred_family = AF_INET6; + else if (strcmp(argv[1], "link") == 0) + preferred_family = AF_PACKET; + else + invarg(argv[1], "invalid protocol family"); + } else if (strcmp(opt, "-4") == 0) { + preferred_family = AF_INET; + } else if (strcmp(opt, "-6") == 0) { + preferred_family = AF_INET6; + } else if (strcmp(opt, "-0") == 0) { + preferred_family = AF_PACKET; + } else if (matches(opt, "-oneline") == 0) { + ++oneline; + } else { + bb_show_usage(); + } + argc--; argv++; + } + _SL_ = oneline ? "\\" : "\n" ; +} +#endif + +int ip_main(int argc, char **argv) +{ + int ret = EXIT_FAILURE; + + ip_parse_common_args(&argc, &argv); + + if (argc > 1) { +#ifdef CONFIG_FEATURE_IP_ADDRESS + if (matches(argv[1], "address") == 0) { + ret = do_ipaddr(argc-2, argv+2); + } +#endif +#ifdef CONFIG_FEATURE_IP_ROUTE + if (matches(argv[1], "route") == 0) { + ret = do_iproute(argc-2, argv+2); + } +#endif +#ifdef CONFIG_FEATURE_IP_LINK + if (matches(argv[1], "link") == 0) { + ret = do_iplink(argc-2, argv+2); + } +#endif +#ifdef CONFIG_FEATURE_IP_TUNNEL + if (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0) { + ret = do_iptunnel(argc-2, argv+2); + } +#endif + } + if (ret) { + bb_show_usage(); + } + return(EXIT_SUCCESS); +} diff --git a/busybox/networking/ipaddr.c b/busybox/networking/ipaddr.c new file mode 100644 index 000000000..7826194c9 --- /dev/null +++ b/busybox/networking/ipaddr.c @@ -0,0 +1,27 @@ +/* + * ip.c "ip" utility frontend. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include "./libiproute/utils.h" +#include "./libiproute/ip_common.h" + +#include "busybox.h" + +int ipaddr_main(int argc, char **argv) +{ + ip_parse_common_args(&argc, &argv); + + return do_ipaddr(argc-1, argv+1); +} diff --git a/busybox/networking/ipcalc.c b/busybox/networking/ipcalc.c new file mode 100644 index 000000000..bcd272b85 --- /dev/null +++ b/busybox/networking/ipcalc.c @@ -0,0 +1,224 @@ +/* vi: set sw=4 ts=4 ai: */ +/* + * Mini ipcalc implementation for busybox + * + * By Jordan Crouse + * Stephan Linz + * + * This is a complete reimplementation of the ipcalc program + * from Red Hat. I didn't look at their source code, but there + * is no denying that this is a loving reimplementation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +#define IPCALC_MSG(CMD,ALTCMD) if (mode & SILENT) {ALTCMD;} else {CMD;} + +#define CLASS_A_NETMASK ntohl(0xFF000000) +#define CLASS_B_NETMASK ntohl(0xFFFF0000) +#define CLASS_C_NETMASK ntohl(0xFFFFFF00) + +static unsigned long get_netmask(unsigned long ipaddr) +{ + ipaddr = htonl(ipaddr); + + if ((ipaddr & 0xC0000000) == 0xC0000000) + return CLASS_C_NETMASK; + else if ((ipaddr & 0x80000000) == 0x80000000) + return CLASS_B_NETMASK; + else if ((ipaddr & 0x80000000) == 0) + return CLASS_A_NETMASK; + else + return 0; +} + +#ifdef CONFIG_FEATURE_IPCALC_FANCY +static int get_prefix(unsigned long netmask) +{ + unsigned long msk = 0x80000000; + int ret = 0; + + netmask = htonl(netmask); + while(msk) { + if (netmask & msk) + ret++; + msk >>= 1; + } + return ret; +} +#endif + +#define NETMASK 0x01 +#define BROADCAST 0x02 +#define NETWORK 0x04 +#define NETPREFIX 0x08 +#define HOSTNAME 0x10 +#define SILENT 0x20 + + +int ipcalc_main(int argc, char **argv) +{ + unsigned long mode; + + in_addr_t netmask; + in_addr_t broadcast; + in_addr_t network; + in_addr_t ipaddr; + struct in_addr a; + +#ifdef CONFIG_FEATURE_IPCALC_FANCY + unsigned long netprefix = 0; + int have_netmask = 0; + char *ipstr, *prefixstr; +#endif + + static const struct option long_options[] = { + {"netmask", no_argument, NULL, 'm'}, + {"broadcast", no_argument, NULL, 'b'}, + {"network", no_argument, NULL, 'n'}, +#ifdef CONFIG_FEATURE_IPCALC_FANCY + {"prefix", no_argument, NULL, 'p'}, + {"hostname", no_argument, NULL, 'h'}, + {"silent", no_argument, NULL, 's'}, +#endif + {NULL, 0, NULL, 0} + }; + + bb_applet_long_options = long_options; + mode = bb_getopt_ulflags(argc, argv, +#ifdef CONFIG_FEATURE_IPCALC_FANCY + "mbnphs" +#else + "mbn" +#endif + ); + if (mode & (BROADCAST | NETWORK | NETPREFIX)) { + if (argc - optind > 2) { + bb_show_usage(); + } + } else { + if (argc - optind != 1) { + bb_show_usage(); + } + } + +#ifdef CONFIG_FEATURE_IPCALC_FANCY + prefixstr = ipstr = argv[optind]; + + while(*prefixstr) { + if (*prefixstr == '/') { + *prefixstr = (char)0; + prefixstr++; + if (*prefixstr) { + unsigned int msk; + + if (safe_strtoul(prefixstr, &netprefix) || netprefix > 32) { + IPCALC_MSG(bb_error_msg_and_die("bad IP prefix: %s\n", prefixstr), + exit(EXIT_FAILURE)); + } + netmask = 0; + msk = 0x80000000; + while (netprefix > 0) { + netmask |= msk; + msk >>= 1; + netprefix--; + } + netmask = htonl(netmask); + /* Even if it was 0, we will signify that we have a netmask. This allows */ + /* for specification of default routes, etc which have a 0 netmask/prefix */ + have_netmask = 1; + } + break; + } + prefixstr++; + } + ipaddr = inet_aton(ipstr, &a); +#else + ipaddr = inet_aton(argv[optind], &a); +#endif + + if (ipaddr == 0) { + IPCALC_MSG(bb_error_msg_and_die("bad IP address: %s", argv[optind]), + exit(EXIT_FAILURE)); + } + ipaddr = a.s_addr; + + if (argc - optind == 2) { +#ifdef CONFIG_FEATURE_IPCALC_FANCY + if (have_netmask) { + IPCALC_MSG(bb_error_msg_and_die("Both prefix and netmask were specified, use one or the other.\n"), + exit(EXIT_FAILURE)); + } + +#endif + netmask = inet_aton(argv[optind + 1], &a); + if (netmask == 0) { + IPCALC_MSG(bb_error_msg_and_die("bad netmask: %s", argv[optind + 1]), + exit(EXIT_FAILURE)); + } + netmask = a.s_addr; + } else { +#ifdef CONFIG_FEATURE_IPCALC_FANCY + + if (!have_netmask) +#endif + /* JHC - If the netmask wasn't provided then calculate it */ + netmask = get_netmask(ipaddr); + } + + if (mode & NETMASK) { + printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask))); + } + + if (mode & BROADCAST) { + broadcast = (ipaddr & netmask) | ~netmask; + printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast))); + } + + if (mode & NETWORK) { + network = ipaddr & netmask; + printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network))); + } + +#ifdef CONFIG_FEATURE_IPCALC_FANCY + if (mode & NETPREFIX) { + printf("PREFIX=%i\n", get_prefix(netmask)); + } + + if (mode & HOSTNAME) { + struct hostent *hostinfo; + int x; + + hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET); + if (!hostinfo) { + IPCALC_MSG(bb_herror_msg_and_die( + "cannot find hostname for %s", argv[optind]),); + exit(EXIT_FAILURE); + } + for (x = 0; hostinfo->h_name[x]; x++) { + hostinfo->h_name[x] = tolower(hostinfo->h_name[x]); + } + + printf("HOSTNAME=%s\n", hostinfo->h_name); + } +#endif + + return EXIT_SUCCESS; +} + +/* END CODE */ +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/networking/iplink.c b/busybox/networking/iplink.c new file mode 100644 index 000000000..7207e176c --- /dev/null +++ b/busybox/networking/iplink.c @@ -0,0 +1,27 @@ +/* + * ip.c "ip" utility frontend. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include "./libiproute/utils.h" +#include "./libiproute/ip_common.h" + +#include "busybox.h" + +int iplink_main(int argc, char **argv) +{ + ip_parse_common_args(&argc, &argv); + + return do_iplink(argc-1, argv+1); +} diff --git a/busybox/networking/iproute.c b/busybox/networking/iproute.c new file mode 100644 index 000000000..d049a87c4 --- /dev/null +++ b/busybox/networking/iproute.c @@ -0,0 +1,27 @@ +/* + * ip.c "ip" utility frontend. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include "./libiproute/utils.h" +#include "./libiproute/ip_common.h" + +#include "busybox.h" + +int iproute_main(int argc, char **argv) +{ + ip_parse_common_args(&argc, &argv); + + return do_iproute(argc-1, argv+1); +} diff --git a/busybox/networking/iptunnel.c b/busybox/networking/iptunnel.c new file mode 100644 index 000000000..f2b2e8a77 --- /dev/null +++ b/busybox/networking/iptunnel.c @@ -0,0 +1,27 @@ +/* + * ip.c "ip" utility frontend. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include "./libiproute/utils.h" +#include "./libiproute/ip_common.h" + +#include "busybox.h" + +int iptunnel_main(int argc, char **argv) +{ + ip_parse_common_args(&argc, &argv); + + return do_iptunnel(argc-1, argv+1); +} diff --git a/busybox/networking/libiproute/Makefile b/busybox/networking/libiproute/Makefile new file mode 100644 index 000000000..d3aefaaf4 --- /dev/null +++ b/busybox/networking/libiproute/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=../.. +top_builddir=../.. +srcdir=$(top_srcdir)/networking/libiproute +LIBIPROUTE_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/networking/libiproute/Makefile.in b/busybox/networking/libiproute/Makefile.in new file mode 100644 index 000000000..fcc7f48ce --- /dev/null +++ b/busybox/networking/libiproute/Makefile.in @@ -0,0 +1,84 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +LIBIPROUTE_AR:=libiproute.a +ifndef $(LIBIPROUTE_DIR) +LIBIPROUTE_DIR:=$(top_builddir)/networking/libiproute/ +endif +srcdir=$(top_srcdir)/networking/libiproute + +LIBIPROUTE-$(CONFIG_IP) += \ + ip_parse_common_args.o \ + ipaddress.o \ + iplink.o \ + iproute.o \ + iptunnel.o \ + libnetlink.o \ + ll_addr.o \ + ll_map.o \ + ll_proto.o \ + ll_types.o \ + rt_names.o \ + rtm_map.o \ + utils.o + +LIBIPROUTE-$(CONFIG_IPADDR) += \ + ip_parse_common_args.o \ + ipaddress.o \ + libnetlink.o \ + ll_addr.o \ + ll_map.o \ + ll_types.o \ + rt_names.o \ + utils.o + +LIBIPROUTE-$(CONFIG_IPLINK) += \ + ip_parse_common_args.o \ + ipaddress.o \ + iplink.o \ + libnetlink.o \ + ll_addr.o \ + ll_map.o \ + ll_types.o \ + rt_names.o \ + utils.o + +LIBIPROUTE-$(CONFIG_IPROUTE) += \ + ip_parse_common_args.o \ + iproute.o \ + libnetlink.o \ + ll_map.o \ + rt_names.o \ + rtm_map.o \ + utils.o + +LIBIPROUTE-$(CONFIG_IPTUNNEL) += \ + ip_parse_common_args.o \ + iptunnel.o \ + rt_names.o \ + utils.o + +libraries-y+=$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR) + +$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR): $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y)) + $(AR) -ro $@ $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y)) + +$(LIBIPROUTE_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/networking/libiproute/ip_common.h b/busybox/networking/libiproute/ip_common.h new file mode 100644 index 000000000..25e9c6c81 --- /dev/null +++ b/busybox/networking/libiproute/ip_common.h @@ -0,0 +1,18 @@ +extern int preferred_family; +extern char * _SL_; + +extern void ip_parse_common_args(int *argcp, char ***argvp); +extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); +extern int ipaddr_list_or_flush(int argc, char **argv, int flush); +extern int iproute_monitor(int argc, char **argv); +extern void iplink_usage(void) __attribute__((noreturn)); +extern void ipneigh_reset_filter(void); +extern int do_ipaddr(int argc, char **argv); +extern int do_iproute(int argc, char **argv); +extern int do_iprule(int argc, char **argv); +extern int do_ipneigh(int argc, char **argv); +extern int do_iptunnel(int argc, char **argv); +extern int do_iplink(int argc, char **argv); +extern int do_ipmonitor(int argc, char **argv); +extern int do_multiaddr(int argc, char **argv); +extern int do_multiroute(int argc, char **argv); diff --git a/busybox/networking/libiproute/ip_parse_common_args.c b/busybox/networking/libiproute/ip_parse_common_args.c new file mode 100644 index 000000000..a76df48e0 --- /dev/null +++ b/busybox/networking/libiproute/ip_parse_common_args.c @@ -0,0 +1,76 @@ +/* + * ip.c "ip" utility frontend. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include + +#include "utils.h" +#include "ip_common.h" + +#include "busybox.h" + +int preferred_family = AF_UNSPEC; +int oneline = 0; +char * _SL_ = NULL; + +void ip_parse_common_args(int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (argc > 1) { + char *opt = argv[1]; + + if (strcmp(opt,"--") == 0) { + argc--; argv++; + break; + } + + if (opt[0] != '-') + break; + + if (opt[1] == '-') + opt++; + + if (matches(opt, "-family") == 0) { + argc--; + argv++; + if (! argv[1]) + bb_show_usage(); + if (strcmp(argv[1], "inet") == 0) + preferred_family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + preferred_family = AF_INET6; + else if (strcmp(argv[1], "link") == 0) + preferred_family = AF_PACKET; + else + invarg(argv[1], "invalid protocol family"); + } else if (strcmp(opt, "-4") == 0) { + preferred_family = AF_INET; + } else if (strcmp(opt, "-6") == 0) { + preferred_family = AF_INET6; + } else if (strcmp(opt, "-0") == 0) { + preferred_family = AF_PACKET; + } else if (matches(opt, "-oneline") == 0) { + ++oneline; + } else { + bb_show_usage(); + } + argc--; argv++; + } + _SL_ = oneline ? "\\" : "\n" ; + *argcp = argc; + *argvp = argv; +} diff --git a/busybox/networking/libiproute/ipaddress.c b/busybox/networking/libiproute/ipaddress.c new file mode 100644 index 000000000..7e0c75785 --- /dev/null +++ b/busybox/networking/libiproute/ipaddress.c @@ -0,0 +1,825 @@ +/* + * ipaddress.c "ip address". + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * Changes: + * Laszlo Valko 990223: address label must be zero terminated + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include "rt_names.h" +#include "utils.h" + +#include "libbb.h" + +static struct +{ + int ifindex; + int family; + int oneline; + int showqueue; + inet_prefix pfx; + int scope, scopemask; + int flags, flagmask; + int up; + char *label; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; +} filter; + +void print_link_flags(FILE *fp, unsigned flags, unsigned mdown) +{ + fprintf(fp, "<"); + flags &= ~IFF_RUNNING; +#define _PF(f) if (flags&IFF_##f) { \ + flags &= ~IFF_##f ; \ + fprintf(fp, #f "%s", flags ? "," : ""); } + _PF(LOOPBACK); + _PF(BROADCAST); + _PF(POINTOPOINT); + _PF(MULTICAST); + _PF(NOARP); +#if 0 + _PF(ALLMULTI); + _PF(PROMISC); + _PF(MASTER); + _PF(SLAVE); + _PF(DEBUG); + _PF(DYNAMIC); + _PF(AUTOMEDIA); + _PF(PORTSEL); + _PF(NOTRAILERS); +#endif + _PF(UP); +#undef _PF + if (flags) + fprintf(fp, "%x", flags); + if (mdown) + fprintf(fp, ",M-DOWN"); + fprintf(fp, "> "); +} + +static void print_queuelen(char *name) +{ + struct ifreq ifr; + int s; + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, name); + if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { + perror("SIOCGIFXQLEN"); + close(s); + return; + } + close(s); + + if (ifr.ifr_qlen) + printf("qlen %d", ifr.ifr_qlen); +} + +static int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr * tb[IFLA_MAX+1]; + int len = n->nlmsg_len; + unsigned m_flag = 0; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return -1; + + if (filter.ifindex && ifi->ifi_index != filter.ifindex) + return 0; + if (filter.up && !(ifi->ifi_flags&IFF_UP)) + return 0; + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + if (tb[IFLA_IFNAME] == NULL) { + bb_error_msg("nil ifname"); + return -1; + } + if (filter.label && + (!filter.family || filter.family == AF_PACKET) && + fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) + return 0; + + if (n->nlmsg_type == RTM_DELLINK) + fprintf(fp, "Deleted "); + + fprintf(fp, "%d: %s", ifi->ifi_index, + tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : ""); + + if (tb[IFLA_LINK]) { + SPRINT_BUF(b1); + int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); + if (iflink == 0) + fprintf(fp, "@NONE: "); + else { + fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); + m_flag = ll_index_to_flags(iflink); + m_flag = !(m_flag & IFF_UP); + } + } else { + fprintf(fp, ": "); + } + print_link_flags(fp, ifi->ifi_flags, m_flag); + + if (tb[IFLA_MTU]) + fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); + if (tb[IFLA_QDISC]) + fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC])); +#ifdef IFLA_MASTER + if (tb[IFLA_MASTER]) { + SPRINT_BUF(b1); + fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); + } +#endif + if (filter.showqueue) + print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME])); + + if (!filter.family || filter.family == AF_PACKET) { + SPRINT_BUF(b1); + fprintf(fp, "%s", _SL_); + fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1))); + + if (tb[IFLA_ADDRESS]) { + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), + RTA_PAYLOAD(tb[IFLA_ADDRESS]), + ifi->ifi_type, + b1, sizeof(b1))); + } + if (tb[IFLA_BROADCAST]) { + if (ifi->ifi_flags&IFF_POINTOPOINT) + fprintf(fp, " peer "); + else + fprintf(fp, " brd "); + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), + RTA_PAYLOAD(tb[IFLA_BROADCAST]), + ifi->ifi_type, + b1, sizeof(b1))); + } + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * rta_tb[IFA_MAX+1]; + char abuf[256]; + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) + return 0; + len -= NLMSG_LENGTH(sizeof(*ifa)); + if (len < 0) { + bb_error_msg("wrong nlmsg len %d", len); + return -1; + } + + if (filter.flushb && n->nlmsg_type != RTM_NEWADDR) + return 0; + + memset(rta_tb, 0, sizeof(rta_tb)); + parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); + + if (!rta_tb[IFA_LOCAL]) + rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; + if (!rta_tb[IFA_ADDRESS]) + rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL]; + + if (filter.ifindex && filter.ifindex != ifa->ifa_index) + return 0; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + return 0; + if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + return 0; + if (filter.label) { + const char *label; + if (rta_tb[IFA_LABEL]) + label = RTA_DATA(rta_tb[IFA_LABEL]); + else + label = ll_idx_n2a(ifa->ifa_index, b1); + if (fnmatch(filter.label, label, 0) != 0) + return 0; + } + if (filter.pfx.family) { + if (rta_tb[IFA_LOCAL]) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = ifa->ifa_family; + memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + return 0; + } + } + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELADDR; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + return 0; + } + + if (n->nlmsg_type == RTM_DELADDR) + fprintf(fp, "Deleted "); + + if (filter.oneline) + fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index)); + if (ifa->ifa_family == AF_INET) + fprintf(fp, " inet "); + else if (ifa->ifa_family == AF_INET6) + fprintf(fp, " inet6 "); + else + fprintf(fp, " family %d ", ifa->ifa_family); + + if (rta_tb[IFA_LOCAL]) { + fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_LOCAL]), + RTA_DATA(rta_tb[IFA_LOCAL]), + abuf, sizeof(abuf))); + + if (rta_tb[IFA_ADDRESS] == NULL || + memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) { + fprintf(fp, "/%d ", ifa->ifa_prefixlen); + } else { + fprintf(fp, " peer %s/%d ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_ADDRESS]), + RTA_DATA(rta_tb[IFA_ADDRESS]), + abuf, sizeof(abuf)), + ifa->ifa_prefixlen); + } + } + + if (rta_tb[IFA_BROADCAST]) { + fprintf(fp, "brd %s ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_BROADCAST]), + RTA_DATA(rta_tb[IFA_BROADCAST]), + abuf, sizeof(abuf))); + } + if (rta_tb[IFA_ANYCAST]) { + fprintf(fp, "any %s ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_ANYCAST]), + RTA_DATA(rta_tb[IFA_ANYCAST]), + abuf, sizeof(abuf))); + } + fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); + if (ifa->ifa_flags&IFA_F_SECONDARY) { + ifa->ifa_flags &= ~IFA_F_SECONDARY; + fprintf(fp, "secondary "); + } + if (ifa->ifa_flags&IFA_F_TENTATIVE) { + ifa->ifa_flags &= ~IFA_F_TENTATIVE; + fprintf(fp, "tentative "); + } + if (ifa->ifa_flags&IFA_F_DEPRECATED) { + ifa->ifa_flags &= ~IFA_F_DEPRECATED; + fprintf(fp, "deprecated "); + } + if (!(ifa->ifa_flags&IFA_F_PERMANENT)) { + fprintf(fp, "dynamic "); + } else + ifa->ifa_flags &= ~IFA_F_PERMANENT; + if (ifa->ifa_flags) + fprintf(fp, "flags %02x ", ifa->ifa_flags); + if (rta_tb[IFA_LABEL]) + fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL])); + if (rta_tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); + char buf[128]; + fprintf(fp, "%s", _SL_); + if (ci->ifa_valid == 0xFFFFFFFFU) + sprintf(buf, "valid_lft forever"); + else + sprintf(buf, "valid_lft %dsec", ci->ifa_valid); + if (ci->ifa_prefered == 0xFFFFFFFFU) + sprintf(buf+strlen(buf), " preferred_lft forever"); + else + sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered); + fprintf(fp, " %s", buf); + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + + +struct nlmsg_list +{ + struct nlmsg_list *next; + struct nlmsghdr h; +}; + +static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp) +{ + for ( ;ainfo ; ainfo = ainfo->next) { + struct nlmsghdr *n = &ainfo->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (n->nlmsg_type != RTM_NEWADDR) + continue; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa))) + return -1; + + if (ifa->ifa_index != ifindex || + (filter.family && filter.family != ifa->ifa_family)) + continue; + + print_addrinfo(NULL, n, fp); + } + return 0; +} + + +static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + struct nlmsg_list **linfo = (struct nlmsg_list**)arg; + struct nlmsg_list *h; + struct nlmsg_list **lp; + + h = malloc(n->nlmsg_len+sizeof(void*)); + if (h == NULL) + return -1; + + memcpy(&h->h, n, n->nlmsg_len); + h->next = NULL; + + for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */; + *lp = h; + + ll_remember_index(who, n, NULL); + return 0; +} + +static void ipaddr_reset_filter(int _oneline) +{ + memset(&filter, 0, sizeof(filter)); + filter.oneline = _oneline; +} + +extern int ipaddr_list_or_flush(int argc, char **argv, int flush) +{ + const char *option[] = { "to", "scope", "up", "label", "dev", 0 }; + struct nlmsg_list *linfo = NULL; + struct nlmsg_list *ainfo = NULL; + struct nlmsg_list *l; + struct rtnl_handle rth; + char *filter_dev = NULL; + int no_link = 0; + + ipaddr_reset_filter(oneline); + filter.showqueue = 1; + + if (filter.family == AF_UNSPEC) + filter.family = preferred_family; + + if (flush) { + if (argc <= 0) { + fprintf(stderr, "Flush requires arguments.\n"); + return -1; + } + if (filter.family == AF_PACKET) { + fprintf(stderr, "Cannot flush link addresses.\n"); + return -1; + } + } + + while (argc > 0) { + const unsigned short option_num = compare_string_array(option, *argv); + switch (option_num) { + case 0: /* to */ + NEXT_ARG(); + get_prefix(&filter.pfx, *argv, filter.family); + if (filter.family == AF_UNSPEC) { + filter.family = filter.pfx.family; + } + break; + case 1: /* scope */ + { + int scope = 0; + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) { + invarg("invalid \"scope\"\n", *argv); + } + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + break; + } + case 2: /* up */ + filter.up = 1; + break; + case 3: /* label */ + NEXT_ARG(); + filter.label = *argv; + break; + case 4: /* dev */ + NEXT_ARG(); + default: + if (filter_dev) { + duparg2("dev", *argv); + } + filter_dev = *argv; + } + argv++; + argc--; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) { + bb_perror_msg_and_die("Cannot send dump request"); + } + + if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) { + bb_error_msg_and_die("Dump terminated"); + } + + if (filter_dev) { + filter.ifindex = ll_name_to_index(filter_dev); + if (filter.ifindex <= 0) { + bb_error_msg("Device \"%s\" does not exist.", filter_dev); + return -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { +#if 0 + if (round == 0) + fprintf(stderr, "Nothing to flush.\n"); +#endif + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + } + } + + if (filter.family != AF_PACKET) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { + bb_perror_msg_and_die("Cannot send dump request"); + } + + if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) { + bb_error_msg_and_die("Dump terminated"); + } + } + + + if (filter.family && filter.family != AF_PACKET) { + struct nlmsg_list **lp; + lp=&linfo; + + if (filter.oneline) + no_link = 1; + + while ((l=*lp)!=NULL) { + int ok = 0; + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + struct nlmsg_list *a; + + for (a=ainfo; a; a=a->next) { + struct nlmsghdr *n = &a->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (ifa->ifa_index != ifi->ifi_index || + (filter.family && filter.family != ifa->ifa_family)) + continue; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + continue; + if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + continue; + if (filter.pfx.family || filter.label) { + struct rtattr *tb[IFA_MAX+1]; + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); + if (!tb[IFA_LOCAL]) + tb[IFA_LOCAL] = tb[IFA_ADDRESS]; + + if (filter.pfx.family && tb[IFA_LOCAL]) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = ifa->ifa_family; + memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + continue; + } + if (filter.label) { + SPRINT_BUF(b1); + const char *label; + if (tb[IFA_LABEL]) + label = RTA_DATA(tb[IFA_LABEL]); + else + label = ll_idx_n2a(ifa->ifa_index, b1); + if (fnmatch(filter.label, label, 0) != 0) + continue; + } + } + + ok = 1; + break; + } + if (!ok) + *lp = l->next; + else + lp = &l->next; + } + } + + for (l=linfo; l; l = l->next) { + if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) { + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + if (filter.family != AF_PACKET) + print_selected_addrinfo(ifi->ifi_index, ainfo, stdout); + } + fflush(stdout); + } + + exit(0); +} + +static int default_scope(inet_prefix *lcl) +{ + if (lcl->family == AF_INET) { + if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127) + return RT_SCOPE_HOST; + } + return 0; +} + +static int ipaddr_modify(int cmd, int argc, char **argv) +{ + const char *option[] = { "peer", "remote", "broadcast", "brd", + "anycast", "scope", "dev", "label", "local", 0 }; + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct ifaddrmsg ifa; + char buf[256]; + } req; + char *d = NULL; + char *l = NULL; + inet_prefix lcl; + inet_prefix peer; + int local_len = 0; + int peer_len = 0; + int brd_len = 0; + int any_len = 0; + int scoped = 0; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.ifa.ifa_family = preferred_family; + + while (argc > 0) { + const unsigned short option_num = compare_string_array(option, *argv); + switch (option_num) { + case 0: /* peer */ + case 1: /* remote */ + NEXT_ARG(); + + if (peer_len) { + duparg("peer", *argv); + } + get_prefix(&peer, *argv, req.ifa.ifa_family); + peer_len = peer.bytelen; + if (req.ifa.ifa_family == AF_UNSPEC) { + req.ifa.ifa_family = peer.family; + } + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen); + req.ifa.ifa_prefixlen = peer.bitlen; + break; + case 2: /* broadcast */ + case 3: /* brd */ + { + inet_prefix addr; + NEXT_ARG(); + if (brd_len) { + duparg("broadcast", *argv); + } + if (strcmp(*argv, "+") == 0) { + brd_len = -1; + } + else if (strcmp(*argv, "-") == 0) { + brd_len = -2; + } else { + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = addr.family; + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen); + brd_len = addr.bytelen; + } + break; + } + case 4: /* anycast */ + { + inet_prefix addr; + NEXT_ARG(); + if (any_len) { + duparg("anycast", *argv); + } + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) { + req.ifa.ifa_family = addr.family; + } + addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); + any_len = addr.bytelen; + break; + } + case 5: /* scope */ + { + int scope = 0; + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) { + invarg(*argv, "invalid scope value."); + } + req.ifa.ifa_scope = scope; + scoped = 1; + break; + } + case 6: /* dev */ + NEXT_ARG(); + d = *argv; + break; + case 7: /* label */ + NEXT_ARG(); + l = *argv; + addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1); + break; + case 8: /* local */ + NEXT_ARG(); + default: + if (local_len) { + duparg2("local", *argv); + } + get_prefix(&lcl, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) { + req.ifa.ifa_family = lcl.family; + } + addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen); + local_len = lcl.bytelen; + } + argc--; + argv++; + } + + if (d == NULL) { + bb_error_msg("Not enough information: \"dev\" argument is required."); + return -1; + } + if (l && matches(d, l) != 0) { + bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s).", d, l); + } + + if (peer_len == 0 && local_len && cmd != RTM_DELADDR) { + peer = lcl; + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen); + } + if (req.ifa.ifa_prefixlen == 0) + req.ifa.ifa_prefixlen = lcl.bitlen; + + if (brd_len < 0 && cmd != RTM_DELADDR) { + inet_prefix brd; + int i; + if (req.ifa.ifa_family != AF_INET) { + bb_error_msg("Broadcast can be set only for IPv4 addresses"); + return -1; + } + brd = peer; + if (brd.bitlen <= 30) { + for (i=31; i>=brd.bitlen; i--) { + if (brd_len == -1) + brd.data[0] |= htonl(1<<(31-i)); + else + brd.data[0] &= ~htonl(1<<(31-i)); + } + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen); + brd_len = brd.bytelen; + } + } + if (!scoped && cmd != RTM_DELADDR) + req.ifa.ifa_scope = default_scope(&lcl); + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) { + bb_error_msg("Cannot find device \"%s\"", d); + return -1; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + exit(0); +} + +extern int do_ipaddr(int argc, char **argv) +{ + const char *commands[] = { "add", "delete", "list", "show", "lst", "flush", 0 }; + unsigned short command_num = 2; + + if (*argv) { + command_num = compare_string_array(commands, *argv); + } + switch (command_num) { + case 0: /* add */ + return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1); + case 1: /* delete */ + return ipaddr_modify(RTM_DELADDR, argc-1, argv+1); + case 2: /* list */ + case 3: /* show */ + case 4: /* lst */ + return ipaddr_list_or_flush(argc-1, argv+1, 0); + case 5: /* flush */ + return ipaddr_list_or_flush(argc-1, argv+1, 1); + } + bb_error_msg_and_die("Unknown command %s", *argv); +} diff --git a/busybox/networking/libiproute/iplink.c b/busybox/networking/libiproute/iplink.c new file mode 100644 index 000000000..2550c196e --- /dev/null +++ b/busybox/networking/libiproute/iplink.c @@ -0,0 +1,367 @@ +/* + * iplink.c "ip link". + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#else +#include +#endif + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#include "libbb.h" + + +/* take from linux/sockios.h */ +#define SIOCSIFNAME 0x8923 /* set interface name */ + +static int do_link; + +static int on_off(char *msg) +{ + bb_error_msg("Error: argument of \"%s\" must be \"on\" or \"off\"", msg); + return -1; +} + +static int get_ctl_fd(void) +{ + int s_errno; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + s_errno = errno; + fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + fd = socket(PF_INET6, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + errno = s_errno; + perror("Cannot create control socket"); + return -1; +} + +static int do_chflags(char *dev, __u32 flags, __u32 mask) +{ + struct ifreq ifr; + int fd; + int err; + + strcpy(ifr.ifr_name, dev); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCGIFFLAGS, &ifr); + if (err) { + perror("SIOCGIFFLAGS"); + close(fd); + return -1; + } + if ((ifr.ifr_flags^flags)&mask) { + ifr.ifr_flags &= ~mask; + ifr.ifr_flags |= mask&flags; + err = ioctl(fd, SIOCSIFFLAGS, &ifr); + if (err) + perror("SIOCSIFFLAGS"); + } + close(fd); + return err; +} + +static int do_changename(char *dev, char *newdev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) + struct ifreq ifr; + int fd; + int err; + + strcpy(ifr.ifr_name, dev); + strcpy(ifr.ifr_newname, newdev); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCSIFNAME, &ifr); + if (err) { + perror("SIOCSIFNAME"); + close(fd); + return -1; + } + close(fd); + return err; +#endif + return 0; +} + +static int set_qlen(char *dev, int qlen) +{ + struct ifreq ifr; + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, dev); + ifr.ifr_qlen = qlen; + if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { + perror("SIOCSIFXQLEN"); + close(s); + return -1; + } + close(s); + + return 0; +} + +static int set_mtu(char *dev, int mtu) +{ + struct ifreq ifr; + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, dev); + ifr.ifr_mtu = mtu; + if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { + perror("SIOCSIFMTU"); + close(s); + return -1; + } + close(s); + + return 0; +} + +static int get_address(char *dev, int *htype) +{ + struct ifreq ifr; + struct sockaddr_ll me; + int alen; + int s; + + s = socket(PF_PACKET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_PACKET)"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, dev); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror("SIOCGIFINDEX"); + close(s); + return -1; + } + + memset(&me, 0, sizeof(me)); + me.sll_family = AF_PACKET; + me.sll_ifindex = ifr.ifr_ifindex; + me.sll_protocol = htons(ETH_P_LOOP); + if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { + perror("bind"); + close(s); + return -1; + } + + alen = sizeof(me); + if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { + perror("getsockname"); + close(s); + return -1; + } + close(s); + *htype = me.sll_hatype; + return me.sll_halen; +} + +static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr) +{ + int alen; + + memset(ifr, 0, sizeof(*ifr)); + strcpy(ifr->ifr_name, dev); + ifr->ifr_hwaddr.sa_family = hatype; + alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); + if (alen < 0) + return -1; + if (alen != halen) { + bb_error_msg("Wrong address (%s) length: expected %d bytes", lla, halen); + return -1; + } + return 0; +} + +static int set_address(struct ifreq *ifr, int brd) +{ + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { + perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int do_set(int argc, char **argv) +{ + char *dev = NULL; + __u32 mask = 0; + __u32 flags = 0; + int qlen = -1; + int mtu = -1; + char *newaddr = NULL; + char *newbrd = NULL; + struct ifreq ifr0, ifr1; + char *newname = NULL; + int htype, halen; + + while (argc > 0) { + if (strcmp(*argv, "up") == 0) { + mask |= IFF_UP; + flags |= IFF_UP; + } else if (strcmp(*argv, "down") == 0) { + mask |= IFF_UP; + flags &= ~IFF_UP; + } else if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + newname = *argv; + } else if (strcmp(*argv, "mtu") == 0) { + NEXT_ARG(); + if (mtu != -1) + duparg("mtu", *argv); + if (get_integer(&mtu, *argv, 0)) + invarg("Invalid \"mtu\" value\n", *argv); + } else if (strcmp(*argv, "multicast") == 0) { + NEXT_ARG(); + mask |= IFF_MULTICAST; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_MULTICAST; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_MULTICAST; + } else + return on_off("multicast"); + } else if (strcmp(*argv, "arp") == 0) { + NEXT_ARG(); + mask |= IFF_NOARP; + if (strcmp(*argv, "on") == 0) { + flags &= ~IFF_NOARP; + } else if (strcmp(*argv, "off") == 0) { + flags |= IFF_NOARP; + } else + return on_off("noarp"); + } else { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } + if (dev) + duparg2("dev", *argv); + dev = *argv; + } + argc--; argv++; + } + + if (!dev) { + bb_error_msg("Not enough of information: \"dev\" argument is required."); + exit(-1); + } + + if (newaddr || newbrd) { + halen = get_address(dev, &htype); + if (halen < 0) + return -1; + if (newaddr) { + if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) + return -1; + } + if (newbrd) { + if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) + return -1; + } + } + + if (newname && strcmp(dev, newname)) { + if (do_changename(dev, newname) < 0) + return -1; + dev = newname; + } + if (qlen != -1) { + if (set_qlen(dev, qlen) < 0) + return -1; + } + if (mtu != -1) { + if (set_mtu(dev, mtu) < 0) + return -1; + } + if (newaddr || newbrd) { + if (newbrd) { + if (set_address(&ifr1, 1) < 0) + return -1; + } + if (newaddr) { + if (set_address(&ifr0, 0) < 0) + return -1; + } + } + if (mask) + return do_chflags(dev, flags, mask); + return 0; +} + +static int ipaddr_list_link(int argc, char **argv) +{ + preferred_family = AF_PACKET; + do_link = 1; + return ipaddr_list_or_flush(argc, argv, 0); +} + +int do_iplink(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "set") == 0) + return do_set(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return ipaddr_list_link(argc-1, argv+1); + } else + return ipaddr_list_link(0, NULL); + + bb_error_msg("Command \"%s\" is unknown.", *argv); + exit(-1); +} diff --git a/busybox/networking/libiproute/iproute.c b/busybox/networking/libiproute/iproute.c new file mode 100644 index 000000000..9c57140a5 --- /dev/null +++ b/busybox/networking/libiproute/iproute.c @@ -0,0 +1,852 @@ +/* + * iproute.c "ip route". + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + * Kunihiro Ishiguro 001102: rtnh_ifindex was not initialized + */ + +#include + +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" + +#include "libbb.h" + +#ifndef RTAX_RTTVAR +#define RTAX_RTTVAR RTAX_HOPS +#endif + + +static struct +{ + int tb; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; + int protocol, protocolmask; + int scope, scopemask; + int type, typemask; + int tos, tosmask; + int iif, iifmask; + int oif, oifmask; + int realm, realmmask; + inet_prefix rprefsrc; + inet_prefix rvia; + inet_prefix rdst; + inet_prefix mdst; + inet_prefix rsrc; + inet_prefix msrc; +} filter; + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + inet_prefix dst; + inet_prefix src; + int host_len = -1; + SPRINT_BUF(b1); + + + if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { + fprintf(stderr, "Not a route: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) + return 0; + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + bb_error_msg("wrong nlmsg len %d", len); + return -1; + } + + if (r->rtm_family == AF_INET6) + host_len = 128; + else if (r->rtm_family == AF_INET) + host_len = 32; + + if (r->rtm_family == AF_INET6) { + if (filter.tb) { + if (filter.tb < 0) { + if (!(r->rtm_flags&RTM_F_CLONED)) { + return 0; + } + } else { + if (r->rtm_flags&RTM_F_CLONED) { + return 0; + } + if (filter.tb == RT_TABLE_LOCAL) { + if (r->rtm_type != RTN_LOCAL) { + return 0; + } + } else if (filter.tb == RT_TABLE_MAIN) { + if (r->rtm_type == RTN_LOCAL) { + return 0; + } + } else { + return 0; + } + } + } + } else { + if (filter.tb > 0 && filter.tb != r->rtm_table) { + return 0; + } + } + if (filter.rdst.family && + (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) { + return 0; + } + if (filter.mdst.family && + (r->rtm_family != filter.mdst.family || + (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) { + return 0; + } + if (filter.rsrc.family && + (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) { + return 0; + } + if (filter.msrc.family && + (r->rtm_family != filter.msrc.family || + (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) { + return 0; + } + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) + return 0; + if (filter.mdst.family && filter.mdst.bitlen >= 0 && + inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) + return 0; + + if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) + return 0; + if (filter.msrc.family && filter.msrc.bitlen >= 0 && + inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) + return 0; + + if (filter.flushb && + r->rtm_family == AF_INET6 && + r->rtm_dst_len == 0 && + r->rtm_type == RTN_UNREACHABLE && + tb[RTA_PRIORITY] && + *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELROUTE; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + return 0; + } + + if (n->nlmsg_type == RTM_DELROUTE) { + fprintf(fp, "Deleted "); + } + if (r->rtm_type != RTN_UNICAST && !filter.type) { + fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + } + + if (tb[RTA_DST]) { + if (r->rtm_dst_len != host_len) { + fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)), + r->rtm_dst_len + ); + } else { + fprintf(fp, "%s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_dst_len) { + fprintf(fp, "0/%d ", r->rtm_dst_len); + } else { + fprintf(fp, "default "); + } + if (tb[RTA_SRC]) { + if (r->rtm_src_len != host_len) { + fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)), + r->rtm_src_len + ); + } else { + fprintf(fp, "from %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_src_len) { + fprintf(fp, "from 0/%u ", r->rtm_src_len); + } + if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { + fprintf(fp, "via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + if (tb[RTA_OIF] && filter.oifmask != -1) { + fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); + } + + if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { + /* Do not use format_host(). It is our local addr + and symbolic name will not be useful. + */ + fprintf(fp, " src %s ", + rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_PREFSRC]), + RTA_DATA(tb[RTA_PREFSRC]), + abuf, sizeof(abuf))); + } + if (tb[RTA_PRIORITY]) { + fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY])); + } + if (r->rtm_family == AF_INET6) { + struct rta_cacheinfo *ci = NULL; + if (tb[RTA_CACHEINFO]) { + ci = RTA_DATA(tb[RTA_CACHEINFO]); + } + if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { + static int hz; + if (!hz) { + hz = get_hz(); + } + if (r->rtm_flags & RTM_F_CLONED) { + fprintf(fp, "%s cache ", _SL_); + } + if (ci->rta_expires) { + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + } + if (ci->rta_error != 0) { + fprintf(fp, " error %d", ci->rta_error); + } + } else if (ci) { + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + } + } + if (tb[RTA_IIF] && filter.iifmask != -1) { + fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char mxbuf[256]; + struct rtattr * mxrta = (void*)mxbuf; + unsigned mxlock = 0; + char *d = NULL; + int gw_ok = 0; + int dst_ok = 0; + int proto_ok = 0; + int type_ok = 0; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.r.rtm_family = preferred_family; + req.r.rtm_table = RT_TABLE_MAIN; + req.r.rtm_scope = RT_SCOPE_NOWHERE; + + if (cmd != RTM_DELROUTE) { + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + } + + mxrta->rta_type = RTA_METRICS; + mxrta->rta_len = RTA_LENGTH(0); + + while (argc > 0) { + if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = addr.family; + } + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + gw_ok = 1; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = addr.family; + } + addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "mtu") == 0) { + unsigned mtu; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1< '9') && + rtnl_rtntype_a2n(&type, *argv) == 0) { + NEXT_ARG(); + req.r.rtm_type = type; + type_ok = 1; + } + + if (dst_ok) { + duparg2("to", *argv); + } + get_prefix(&dst, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = dst.family; + } + req.r.rtm_dst_len = dst.bitlen; + dst_ok = 1; + if (dst.bytelen) { + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); + } + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) { + exit(1); + } + + if (d) { + int idx; + + ll_init_map(&rth); + + if (d) { + if ((idx = ll_name_to_index(d)) == 0) { + bb_error_msg("Cannot find device \"%s\"", d); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (mxrta->rta_len > RTA_LENGTH(0)) { + if (mxlock) { + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); + } + addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); + } + + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = AF_INET; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + exit(2); + } + + return 0; +} + +static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + memset(&req, 0, sizeof(req)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = RTM_GETROUTE; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.rtm.rtm_family = family; + req.rtm.rtm_flags |= RTM_F_CLONED; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +static int iproute_flush_cache(void) +{ +#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" + + int len; + int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); + char *buffer = "-1"; + + if (flush_fd < 0) { + fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH); + return -1; + } + + len = strlen (buffer); + + if ((write (flush_fd, (void *)buffer, len)) < len) { + fprintf (stderr, "Cannot flush routing cache\n"); + return -1; + } + close(flush_fd); + return 0; +} + +static void iproute_reset_filter(void) +{ + memset(&filter, 0, sizeof(filter)); + filter.mdst.bitlen = -1; + filter.msrc.bitlen = -1; +} + +static int iproute_list_or_flush(int argc, char **argv, int flush) +{ + int do_ipv6 = preferred_family; + struct rtnl_handle rth; + char *id = NULL; + char *od = NULL; + + iproute_reset_filter(); + filter.tb = RT_TABLE_MAIN; + + if (flush && argc <= 0) { + fprintf(stderr, "\"ip route flush\" requires arguments.\n"); + return -1; + } + + while (argc > 0) { + if (matches(*argv, "protocol") == 0) { + int prot = 0; + NEXT_ARG(); + filter.protocolmask = -1; + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all") != 0) { + invarg("invalid \"protocol\"\n", *argv); + } + prot = 0; + filter.protocolmask = 0; + } + filter.protocol = prot; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + od = *argv; + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + id = *argv; + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rsrc, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.msrc, *argv, do_ipv6); + filter.rsrc = filter.msrc; + } + } else { + if (matches(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rdst, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.mdst, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.mdst, *argv, do_ipv6); + filter.rdst = filter.mdst; + } + } + argc--; argv++; + } + + if (do_ipv6 == AF_UNSPEC && filter.tb) { + do_ipv6 = AF_INET; + } + + if (rtnl_open(&rth, 0) < 0) { + exit(1); + } + + ll_init_map(&rth); + + if (id || od) { + int idx; + + if (id) { + if ((idx = ll_name_to_index(id)) == 0) { + bb_error_msg("Cannot find device \"%s\"", id); + return -1; + } + filter.iif = idx; + filter.iifmask = -1; + } + if (od) { + if ((idx = ll_name_to_index(od)) == 0) { + bb_error_msg("Cannot find device \"%s\"", od); + } + filter.oif = idx; + filter.oifmask = -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + if (filter.tb == -1) { + if (do_ipv6 != AF_INET6) + iproute_flush_cache(); + if (do_ipv6 == AF_INET) + return 0; + } + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + return -1; + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + bb_error_msg("Flush terminated\n"); + return -1; + } + if (filter.flushed == 0) { + if (round == 0) { + if (filter.tb != -1 || do_ipv6 == AF_INET6) + fprintf(stderr, "Nothing to flush.\n"); + } + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + } + } + + if (filter.tb != -1) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + bb_perror_msg_and_die("Cannot send dump request"); + } + } else { + if (rtnl_rtcache_request(&rth, do_ipv6) < 0) { + bb_perror_msg_and_die("Cannot send dump request"); + } + } + + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + bb_error_msg_and_die("Dump terminated"); + } + + exit(0); +} + + +static int iproute_get(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char *idev = NULL; + char *odev = NULL; + int connected = 0; + int from_ok = 0; + const char *options[] = { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 }; + + memset(&req, 0, sizeof(req)); + + iproute_reset_filter(); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + req.r.rtm_family = preferred_family; + req.r.rtm_table = 0; + req.r.rtm_protocol = 0; + req.r.rtm_scope = 0; + req.r.rtm_type = 0; + req.r.rtm_src_len = 0; + req.r.rtm_dst_len = 0; + req.r.rtm_tos = 0; + + while (argc > 0) { + switch (compare_string_array(options, *argv)) { + case 0: /* from */ + { + inet_prefix addr; + NEXT_ARG(); + from_ok = 1; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = addr.family; + } + if (addr.bytelen) { + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + } + req.r.rtm_src_len = addr.bitlen; + break; + } + case 1: /* iif */ + NEXT_ARG(); + idev = *argv; + break; + case 2: /* oif */ + case 3: /* dev */ + NEXT_ARG(); + odev = *argv; + break; + case 4: /* notify */ + req.r.rtm_flags |= RTM_F_NOTIFY; + break; + case 5: /* connected */ + connected = 1; + break; + case 6: /* to */ + NEXT_ARG(); + default: + { + inet_prefix addr; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = addr.family; + } + if (addr.bytelen) { + addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); + } + req.r.rtm_dst_len = addr.bitlen; + } + argc--; argv++; + } + } + + if (req.r.rtm_dst_len == 0) { + bb_error_msg_and_die("need at least destination address"); + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (idev || odev) { + int idx; + + if (idev) { + if ((idx = ll_name_to_index(idev)) == 0) { + bb_error_msg("Cannot find device \"%s\"", idev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_IIF, idx); + } + if (odev) { + if ((idx = ll_name_to_index(odev)) == 0) { + bb_error_msg("Cannot find device \"%s\"", odev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (req.r.rtm_family == AF_UNSPEC) { + req.r.rtm_family = AF_INET; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { + exit(2); + } + + if (connected && !from_ok) { + struct rtmsg *r = NLMSG_DATA(&req.n); + int len = req.n.nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + bb_error_msg_and_die("An error :-)"); + } + + if (req.n.nlmsg_type != RTM_NEWROUTE) { + bb_error_msg("Not a route?"); + return -1; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + bb_error_msg("Wrong len %d", len); + return -1; + } + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PREFSRC]) { + tb[RTA_PREFSRC]->rta_type = RTA_SRC; + r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]); + } else if (!tb[RTA_SRC]) { + bb_error_msg("Failed to connect the route"); + return -1; + } + if (!odev && tb[RTA_OIF]) { + tb[RTA_OIF]->rta_type = 0; + } + if (tb[RTA_GATEWAY]) { + tb[RTA_GATEWAY]->rta_type = 0; + } + if (!idev && tb[RTA_IIF]) { + tb[RTA_IIF]->rta_type = 0; + } + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { + exit(2); + } + } + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + bb_error_msg_and_die("An error :-)"); + } + + exit(0); +} + +int do_iproute(int argc, char **argv) +{ + const char *ip_route_commands[] = { "add", "append", "change", "chg", + "delete", "del", "get", "list", "show", "prepend", "replace", "test", "flush", 0 }; + unsigned short command_num = 7; + unsigned int flags = 0; + int cmd = RTM_NEWROUTE; + + if (*argv) { + command_num = compare_string_array(ip_route_commands, *argv); + } + switch(command_num) { + case 0: /* add*/ + flags = NLM_F_CREATE|NLM_F_EXCL; + break; + case 1: /* append */ + flags = NLM_F_CREATE|NLM_F_APPEND; + break; + case 2: /* change */ + case 3: /* chg */ + flags = NLM_F_REPLACE; + break; + case 4: /* delete */ + case 5: /* del */ + cmd = RTM_DELROUTE; + break; + case 6: /* get */ + return iproute_get(argc-1, argv+1); + case 7: /* list */ + case 8: /* show */ + return iproute_list_or_flush(argc-1, argv+1, 0); + case 9: /* prepend */ + flags = NLM_F_CREATE; + case 10: /* replace */ + flags = NLM_F_CREATE|NLM_F_REPLACE; + case 11: /* test */ + flags = NLM_F_EXCL; + case 12: /* flush */ + return iproute_list_or_flush(argc-1, argv+1, 1); + default: + bb_error_msg_and_die("Unknown command %s", *argv); + } + + return iproute_modify(cmd, flags, argc-1, argv+1); +} diff --git a/busybox/networking/libiproute/iptunnel.c b/busybox/networking/libiproute/iptunnel.c new file mode 100644 index 000000000..f8713e08b --- /dev/null +++ b/busybox/networking/libiproute/iptunnel.c @@ -0,0 +1,548 @@ +/* + * iptunnel.c "ip tunnel" + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + * Rani Assaf 980930: do not allow key for ipip/sit + * Phil Karn 990408: "pmtudisc" flag + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#define __constant_htons htons +#include + +#include "rt_names.h" +#include "utils.h" + +#include "libbb.h" + +static int do_ioctl_get_ifindex(char *dev) +{ + struct ifreq ifr; + int fd; + + strcpy(ifr.ifr_name, dev); + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ioctl(fd, SIOCGIFINDEX, &ifr)) { + bb_perror_msg("ioctl"); + return 0; + } + close(fd); + return ifr.ifr_ifindex; +} + +static int do_ioctl_get_iftype(char *dev) +{ + struct ifreq ifr; + int fd; + + strcpy(ifr.ifr_name, dev); + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ioctl(fd, SIOCGIFHWADDR, &ifr)) { + bb_perror_msg("ioctl"); + return -1; + } + close(fd); + return ifr.ifr_addr.sa_family; +} + + +static char *do_ioctl_get_ifname(int idx) +{ + static struct ifreq ifr; + int fd; + + ifr.ifr_ifindex = idx; + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ioctl(fd, SIOCGIFNAME, &ifr)) { + bb_perror_msg("ioctl"); + return NULL; + } + close(fd); + return ifr.ifr_name; +} + + + +static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + strcpy(ifr.ifr_name, basedev); + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGETTUNNEL, &ifr); + if (err) { + bb_perror_msg("ioctl"); + } + close(fd); + return err; +} + +static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (cmd == SIOCCHGTUNNEL && p->name[0]) { + strcpy(ifr.ifr_name, p->name); + } else { + strcpy(ifr.ifr_name, basedev); + } + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, cmd, &ifr); + if (err) { + bb_perror_msg("ioctl"); + } + close(fd); + return err; +} + +static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (p->name[0]) { + strcpy(ifr.ifr_name, p->name); + } else { + strcpy(ifr.ifr_name, basedev); + } + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCDELTUNNEL, &ifr); + if (err) { + bb_perror_msg("ioctl"); + } + close(fd); + return err; +} + +static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) +{ + int count = 0; + char medium[IFNAMSIZ]; + memset(p, 0, sizeof(*p)); + memset(&medium, 0, sizeof(medium)); + + p->iph.version = 4; + p->iph.ihl = 5; +#ifndef IP_DF +#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ +#endif + p->iph.frag_off = htons(IP_DF); + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "ipip") == 0 || + strcmp(*argv, "ip/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { + bb_error_msg("You managed to ask for more than one tunnel mode."); + exit(-1); + } + p->iph.protocol = IPPROTO_IPIP; + } else if (strcmp(*argv, "gre") == 0 || + strcmp(*argv, "gre/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { + bb_error_msg("You managed to ask for more than one tunnel mode."); + exit(-1); + } + p->iph.protocol = IPPROTO_GRE; + } else if (strcmp(*argv, "sit") == 0 || + strcmp(*argv, "ipv6/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { + bb_error_msg("You managed to ask for more than one tunnel mode."); + exit(-1); + } + p->iph.protocol = IPPROTO_IPV6; + } else { + bb_error_msg("Cannot guess tunnel mode."); + exit(-1); + } + } else if (strcmp(*argv, "key") == 0) { + unsigned uval; + NEXT_ARG(); + p->i_flags |= GRE_KEY; + p->o_flags |= GRE_KEY; + if (strchr(*argv, '.')) + p->i_key = p->o_key = get_addr32(*argv); + else { + if (get_unsigned(&uval, *argv, 0)<0) { + bb_error_msg("invalid value of \"key\""); + exit(-1); + } + p->i_key = p->o_key = htonl(uval); + } + } else if (strcmp(*argv, "ikey") == 0) { + unsigned uval; + NEXT_ARG(); + p->i_flags |= GRE_KEY; + if (strchr(*argv, '.')) + p->o_key = get_addr32(*argv); + else { + if (get_unsigned(&uval, *argv, 0)<0) { + bb_error_msg("invalid value of \"ikey\""); + exit(-1); + } + p->i_key = htonl(uval); + } + } else if (strcmp(*argv, "okey") == 0) { + unsigned uval; + NEXT_ARG(); + p->o_flags |= GRE_KEY; + if (strchr(*argv, '.')) + p->o_key = get_addr32(*argv); + else { + if (get_unsigned(&uval, *argv, 0)<0) { + bb_error_msg("invalid value of \"okey\""); + exit(-1); + } + p->o_key = htonl(uval); + } + } else if (strcmp(*argv, "seq") == 0) { + p->i_flags |= GRE_SEQ; + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "iseq") == 0) { + p->i_flags |= GRE_SEQ; + } else if (strcmp(*argv, "oseq") == 0) { + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "csum") == 0) { + p->i_flags |= GRE_CSUM; + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "icsum") == 0) { + p->i_flags |= GRE_CSUM; + } else if (strcmp(*argv, "ocsum") == 0) { + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "nopmtudisc") == 0) { + p->iph.frag_off = 0; + } else if (strcmp(*argv, "pmtudisc") == 0) { + p->iph.frag_off = htons(IP_DF); + } else if (strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "any")) + p->iph.daddr = get_addr32(*argv); + } else if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "any")) + p->iph.saddr = get_addr32(*argv); + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + strncpy(medium, *argv, IFNAMSIZ-1); + } else if (strcmp(*argv, "ttl") == 0) { + unsigned uval; + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_unsigned(&uval, *argv, 0)) + invarg("invalid TTL\n", *argv); + if (uval > 255) + invarg("TTL must be <=255\n", *argv); + p->iph.ttl = uval; + } + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 uval; + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (rtnl_dsfield_a2n(&uval, *argv)) + invarg("bad TOS value", *argv); + p->iph.tos = uval; + } else + p->iph.tos = 1; + } else { + if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + } + if (p->name[0]) + duparg2("name", *argv); + strncpy(p->name, *argv, IFNAMSIZ); + if (cmd == SIOCCHGTUNNEL && count == 0) { + struct ip_tunnel_parm old_p; + memset(&old_p, 0, sizeof(old_p)); + if (do_get_ioctl(*argv, &old_p)) + return -1; + *p = old_p; + } + } + count++; + argc--; argv++; + } + + + if (p->iph.protocol == 0) { + if (memcmp(p->name, "gre", 3) == 0) + p->iph.protocol = IPPROTO_GRE; + else if (memcmp(p->name, "ipip", 4) == 0) + p->iph.protocol = IPPROTO_IPIP; + else if (memcmp(p->name, "sit", 3) == 0) + p->iph.protocol = IPPROTO_IPV6; + } + + if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) { + if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { + bb_error_msg("Keys are not allowed with ipip and sit."); + return -1; + } + } + + if (medium[0]) { + p->link = do_ioctl_get_ifindex(medium); + if (p->link == 0) + return -1; + } + + if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->i_key = p->iph.daddr; + p->i_flags |= GRE_KEY; + } + if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->o_key = p->iph.daddr; + p->o_flags |= GRE_KEY; + } + if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { + bb_error_msg("Broadcast tunnel requires a source address."); + return -1; + } + return 0; +} + + +static int do_add(int cmd, int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, cmd, &p) < 0) + return -1; + + if (p.iph.ttl && p.iph.frag_off == 0) { + bb_error_msg("ttl != 0 and noptmudisc are incompatible"); + return -1; + } + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + return do_add_ioctl(cmd, "tunl0", &p); + case IPPROTO_GRE: + return do_add_ioctl(cmd, "gre0", &p); + case IPPROTO_IPV6: + return do_add_ioctl(cmd, "sit0", &p); + default: + bb_error_msg("cannot determine tunnel mode (ipip, gre or sit)"); + return -1; + } + return -1; +} + +int do_del(int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) + return -1; + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + return do_del_ioctl("tunl0", &p); + case IPPROTO_GRE: + return do_del_ioctl("gre0", &p); + case IPPROTO_IPV6: + return do_del_ioctl("sit0", &p); + default: + return do_del_ioctl(p.name, &p); + } + return -1; +} + +void print_tunnel(struct ip_tunnel_parm *p) +{ + char s1[256]; + char s2[256]; + char s3[64]; + char s4[64]; + + format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)); + format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)); + inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3)); + inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4)); + + printf("%s: %s/ip remote %s local %s ", + p->name, + p->iph.protocol == IPPROTO_IPIP ? "ip" : + (p->iph.protocol == IPPROTO_GRE ? "gre" : + (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")), + p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any"); + if (p->link) { + char *n = do_ioctl_get_ifname(p->link); + if (n) + printf(" dev %s ", n); + } + if (p->iph.ttl) + printf(" ttl %d ", p->iph.ttl); + else + printf(" ttl inherit "); + if (p->iph.tos) { + SPRINT_BUF(b1); + printf(" tos"); + if (p->iph.tos&1) + printf(" inherit"); + if (p->iph.tos&~1) + printf("%c%s ", p->iph.tos&1 ? '/' : ' ', + rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1))); + } + if (!(p->iph.frag_off&htons(IP_DF))) + printf(" nopmtudisc"); + + if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key) + printf(" key %s", s3); + else if ((p->i_flags|p->o_flags)&GRE_KEY) { + if (p->i_flags&GRE_KEY) + printf(" ikey %s ", s3); + if (p->o_flags&GRE_KEY) + printf(" okey %s ", s4); + } + + if (p->i_flags&GRE_SEQ) + printf("%s Drop packets out of sequence.\n", _SL_); + if (p->i_flags&GRE_CSUM) + printf("%s Checksum in received packet is required.", _SL_); + if (p->o_flags&GRE_SEQ) + printf("%s Sequence packets on output.", _SL_); + if (p->o_flags&GRE_CSUM) + printf("%s Checksum output packets.", _SL_); +} + +static int do_tunnels_list(struct ip_tunnel_parm *p) +{ + char name[IFNAMSIZ]; + unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, + rx_fifo, rx_frame, + tx_bytes, tx_packets, tx_errs, tx_drops, + tx_fifo, tx_colls, tx_carrier, rx_multi; + int type; + struct ip_tunnel_parm p1; + + char buf[512]; + FILE *fp = fopen("/proc/net/dev", "r"); + if (fp == NULL) { + perror("fopen"); + return -1; + } + + fgets(buf, sizeof(buf), fp); + fgets(buf, sizeof(buf), fp); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *ptr; + buf[sizeof(buf) - 1] = 0; + if ((ptr = strchr(buf, ':')) == NULL || + (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { + bb_error_msg("Wrong format of /proc/net/dev. Sorry."); + return -1; + } + if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld", + &rx_bytes, &rx_packets, &rx_errs, &rx_drops, + &rx_fifo, &rx_frame, &rx_multi, + &tx_bytes, &tx_packets, &tx_errs, &tx_drops, + &tx_fifo, &tx_colls, &tx_carrier) != 14) + continue; + if (p->name[0] && strcmp(p->name, name)) + continue; + type = do_ioctl_get_iftype(name); + if (type == -1) { + bb_error_msg("Failed to get type of [%s]", name); + continue; + } + if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) + continue; + memset(&p1, 0, sizeof(p1)); + if (do_get_ioctl(name, &p1)) + continue; + if ((p->link && p1.link != p->link) || + (p->name[0] && strcmp(p1.name, p->name)) || + (p->iph.daddr && p1.iph.daddr != p->iph.daddr) || + (p->iph.saddr && p1.iph.saddr != p->iph.saddr) || + (p->i_key && p1.i_key != p->i_key)) + continue; + print_tunnel(&p1); + printf("\n"); + } + return 0; +} + +static int do_show(int argc, char **argv) +{ + int err; + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) + return -1; + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p); + break; + case IPPROTO_GRE: + err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p); + break; + case IPPROTO_IPV6: + err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p); + break; + default: + do_tunnels_list(&p); + return 0; + } + if (err) + return -1; + + print_tunnel(&p); + printf("\n"); + return 0; +} + +int do_iptunnel(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return do_add(SIOCADDTUNNEL, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return do_add(SIOCCHGTUNNEL, argc-1, argv+1); + if (matches(*argv, "del") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc-1, argv+1); + } else + return do_show(0, NULL); + + bb_error_msg("Command \"%s\" is unknown.", *argv); + exit(-1); +} diff --git a/busybox/networking/libiproute/libnetlink.c b/busybox/networking/libiproute/libnetlink.c new file mode 100644 index 000000000..5545be8fe --- /dev/null +++ b/busybox/networking/libiproute/libnetlink.c @@ -0,0 +1,524 @@ +/* + * libnetlink.c RTnetlink service routines. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "libnetlink.h" +#include "libbb.h" + +void rtnl_close(struct rtnl_handle *rth) +{ + close(rth->fd); +} + +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +{ + int addr_len; + + memset(rth, 0, sizeof(rth)); + + rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (rth->fd < 0) { + bb_perror_msg("Cannot open netlink socket"); + return -1; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = subscriptions; + + if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + bb_perror_msg("Cannot bind netlink socket"); + return -1; + } + addr_len = sizeof(rth->local); + if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + bb_perror_msg("Cannot getsockname"); + return -1; + } + if (addr_len != sizeof(rth->local)) { + bb_error_msg("Wrong address length %d", addr_len); + return -1; + } + if (rth->local.nl_family != AF_NETLINK) { + bb_error_msg("Wrong address family %d", rth->local.nl_family); + return -1; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.g.rtgen_family = family; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +int rtnl_send(struct rtnl_handle *rth, char *buf, int len) +{ + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh; + struct sockaddr_nl nladdr; + struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + iov, 2, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + nlh.nlmsg_len = NLMSG_LENGTH(len); + nlh.nlmsg_type = type; + nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + nlh.nlmsg_pid = 0; + nlh.nlmsg_seq = rth->dump = ++rth->seq; + + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_filter(struct rtnl_handle *rth, + int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), + void *arg1, + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *arg2) +{ + char buf[8192]; + struct sockaddr_nl nladdr; + struct iovec iov = { buf, sizeof(buf) }; + + while (1) { + int status; + struct nlmsghdr *h; + + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + status = recvmsg(rth->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("OVERRUN"); + continue; + } + if (status == 0) { + bb_error_msg("EOF on netlink"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + bb_error_msg_and_die("sender address length == %d", msg.msg_namelen); + } + + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + int err; + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rth->local.nl_pid || + h->nlmsg_seq != rth->dump) { + if (junk) { + err = junk(&nladdr, h, arg2); + if (err < 0) { + return err; + } + } + goto skip_it; + } + + if (h->nlmsg_type == NLMSG_DONE) { + return 0; + } + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + bb_error_msg("ERROR truncated"); + } else { + errno = -l_err->error; + bb_perror_msg("RTNETLINK answers"); + } + return -1; + } + err = filter(&nladdr, h, arg1); + if (err < 0) { + return err; + } + +skip_it: + h = NLMSG_NEXT(h, status); + } + if (msg.msg_flags & MSG_TRUNC) { + bb_error_msg("Message truncated"); + continue; + } + if (status) { + bb_error_msg_and_die("!!!Remnant of size %d", status); + } + } +} + +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg) +{ + int status; + unsigned seq; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov = { (void*)n, n->nlmsg_len }; + char buf[8192]; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + n->nlmsg_seq = seq = ++rtnl->seq; + if (answer == NULL) { + n->nlmsg_flags |= NLM_F_ACK; + } + status = sendmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + bb_perror_msg("Cannot talk to rtnetlink"); + return -1; + } + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) { + continue; + } + bb_perror_msg("OVERRUN"); + continue; + } + if (status == 0) { + bb_error_msg("EOF on netlink"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + bb_error_msg_and_die("sender address length == %d", msg.msg_namelen); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int l_err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + bb_error_msg("Truncated message"); + return -1; + } + bb_error_msg_and_die("!!!malformed message: len=%d", len); + } + + if (nladdr.nl_pid != peer || + h->nlmsg_pid != rtnl->local.nl_pid || + h->nlmsg_seq != seq) { + if (junk) { + l_err = junk(&nladdr, h, jarg); + if (l_err < 0) { + return l_err; + } + } + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (l < sizeof(struct nlmsgerr)) { + bb_error_msg("ERROR truncated"); + } else { + errno = -err->error; + if (errno == 0) { + if (answer) { + memcpy(answer, h, h->nlmsg_len); + } + return 0; + } + bb_perror_msg("RTNETLINK answers"); + } + return -1; + } + if (answer) { + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + bb_error_msg("Unexpected reply!!!"); + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + bb_error_msg("Message truncated"); + continue; + } + if (status) { + bb_error_msg_and_die("!!!Remnant of size %d", status); + } + } +} + +int rtnl_listen(struct rtnl_handle *rtnl, + int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov; + char buf[8192]; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("OVERRUN"); + continue; + } + if (status == 0) { + bb_error_msg("EOF on netlink"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + bb_error_msg_and_die("Sender address length == %d", msg.msg_namelen); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + bb_error_msg("Truncated message"); + return -1; + } + bb_error_msg_and_die("!!!malformed message: len=%d", len); + } + + err = handler(&nladdr, h, jarg); + if (err < 0) { + return err; + } + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + bb_error_msg("Message truncated"); + continue; + } + if (status) { + bb_error_msg_and_die("!!!Remnant of size %d", status); + } + } +} + +int rtnl_from_file(FILE *rtnl, + int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg) +{ + int status; + struct sockaddr_nl nladdr; + char buf[8192]; + struct nlmsghdr *h = (void*)buf; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + while (1) { + int err, len, type; + int l; + + status = fread(&buf, 1, sizeof(*h), rtnl); + + if (status < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("rtnl_from_file: fread"); + return -1; + } + if (status == 0) + return 0; + + len = h->nlmsg_len; + type= h->nlmsg_type; + l = len - sizeof(*h); + + if (l<0 || len>sizeof(buf)) { + bb_error_msg("!!!malformed message: len=%d @%lu", + len, ftell(rtnl)); + return -1; + } + + status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); + + if (status < 0) { + bb_perror_msg("rtnl_from_file: fread"); + return -1; + } + if (status < l) { + bb_error_msg("rtnl-from_file: truncated message"); + return -1; + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + } +} + +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *rta; + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) + return -1; + rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), &data, 4); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) +{ + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) + return -1; + rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *subrta; + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, 4); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) +{ + struct rtattr *subrta; + int len = RTA_LENGTH(alen); + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), data, alen); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max) { + tb[rta->rta_type] = rta; + } + rta = RTA_NEXT(rta,len); + } + if (len) { + bb_error_msg("!!!Deficit %d, rta_len=%d", len, rta->rta_len); + } + return 0; +} diff --git a/busybox/networking/libiproute/libnetlink.h b/busybox/networking/libiproute/libnetlink.h new file mode 100644 index 000000000..45d3ad2bc --- /dev/null +++ b/busybox/networking/libiproute/libnetlink.h @@ -0,0 +1,46 @@ +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#include +#include +#include + +struct rtnl_handle +{ + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 dump; +}; + +extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); +extern void rtnl_close(struct rtnl_handle *rth); +extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); +extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); +extern int rtnl_dump_filter(struct rtnl_handle *rth, + int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), + void *arg1, + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *arg2); +extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg); +extern int rtnl_send(struct rtnl_handle *rth, char *buf, int); + + +extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); +extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen); +extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); +extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen); + +extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); + +extern int rtnl_listen(struct rtnl_handle *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg); +extern int rtnl_from_file(FILE *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg); + +#endif /* __LIBNETLINK_H__ */ + diff --git a/busybox/networking/libiproute/linux/pkt_sched.h b/busybox/networking/libiproute/linux/pkt_sched.h new file mode 100644 index 000000000..70cbabc26 --- /dev/null +++ b/busybox/networking/libiproute/linux/pkt_sched.h @@ -0,0 +1,413 @@ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +/* Logical priority bands not depending on specific packet scheduler. + Every scheduler will map them to real traffic classes, if it has + no more precise mechanism to classify packets. + + These numbers have no special meaning, though their coincidence + with obsolete IPv6 values is not occasional :-). New IPv6 drafts + preferred full anarchy inspired by diffserv group. + + Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy + class, actually, as rule it will be handled with more care than + filler or even bulk. + */ + +#include + +#define TC_PRIO_BESTEFFORT 0 +#define TC_PRIO_FILLER 1 +#define TC_PRIO_BULK 2 +#define TC_PRIO_INTERACTIVE_BULK 4 +#define TC_PRIO_INTERACTIVE 6 +#define TC_PRIO_CONTROL 7 + +#define TC_PRIO_MAX 15 + +/* Generic queue statistics, available for all the elements. + Particular schedulers may have also their private records. + */ + +struct tc_stats +{ + __u64 bytes; /* NUmber of enqueues bytes */ + __u32 packets; /* Number of enqueued packets */ + __u32 drops; /* Packets dropped because of lack of resources */ + __u32 overlimits; /* Number of throttle events when this + * flow goes out of allocated bandwidth */ + __u32 bps; /* Current flow byte rate */ + __u32 pps; /* Current flow packet rate */ + __u32 qlen; + __u32 backlog; +#ifdef __KERNEL__ + spinlock_t *lock; +#endif +}; + +struct tc_estimator +{ + char interval; + unsigned char ewma_log; +}; + +/* "Handles" + --------- + + All the traffic control objects have 32bit identifiers, or "handles". + + They can be considered as opaque numbers from user API viewpoint, + but actually they always consist of two fields: major and + minor numbers, which are interpreted by kernel specially, + that may be used by applications, though not recommended. + + F.e. qdisc handles always have minor number equal to zero, + classes (or flows) have major equal to parent qdisc major, and + minor uniquely identifying class inside qdisc. + + Macros to manipulate handles: + */ + +#define TC_H_MAJ_MASK (0xFFFF0000U) +#define TC_H_MIN_MASK (0x0000FFFFU) +#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) +#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) +#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) + +#define TC_H_UNSPEC (0U) +#define TC_H_ROOT (0xFFFFFFFFU) +#define TC_H_INGRESS (0xFFFFFFF1U) + +struct tc_ratespec +{ + unsigned char cell_log; + unsigned char __reserved; + unsigned short feature; + short addend; + unsigned short mpu; + __u32 rate; +}; + +/* FIFO section */ + +struct tc_fifo_qopt +{ + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ +}; + +/* PRIO section */ + +#define TCQ_PRIO_BANDS 16 + +struct tc_prio_qopt +{ + int bands; /* Number of bands */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ +}; + +/* CSZ section */ + +struct tc_csz_qopt +{ + int flows; /* Maximal number of guaranteed flows */ + unsigned char R_log; /* Fixed point position for round number */ + unsigned char delta_log; /* Log of maximal managed time interval */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */ +}; + +struct tc_csz_copt +{ + struct tc_ratespec slice; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum +{ + TCA_CSZ_UNSPEC, + TCA_CSZ_PARMS, + TCA_CSZ_RTAB, + TCA_CSZ_PTAB, +}; + +/* TBF section */ + +struct tc_tbf_qopt +{ + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum +{ + TCA_TBF_UNSPEC, + TCA_TBF_PARMS, + TCA_TBF_RTAB, + TCA_TBF_PTAB, +}; + + +/* TEQL section */ + +/* TEQL does not require any parameters */ + +/* SFQ section */ + +struct tc_sfq_qopt +{ + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ +}; + +/* + * NOTE: limit, divisor and flows are hardwired to code at the moment. + * + * limit=flows=128, divisor=1024; + * + * The only reason for this is efficiency, it is possible + * to change these parameters in compile time. + */ + +/* RED section */ + +enum +{ + TCA_RED_UNSPEC, + TCA_RED_PARMS, + TCA_RED_STAB, +}; + +struct tc_red_qopt +{ + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; +#define TC_RED_ECN 1 +}; + +struct tc_red_xstats +{ + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ +}; + +/* GRED section */ + +#define MAX_DPs 16 + +enum +{ + TCA_GRED_UNSPEC, + TCA_GRED_PARMS, + TCA_GRED_STAB, + TCA_GRED_DPS, +}; + +#define TCA_SET_OFF TCA_GRED_PARMS +struct tc_gred_qopt +{ + __u32 limit; /* HARD maximal queue length (bytes) +*/ + __u32 qth_min; /* Min average length threshold (bytes) +*/ + __u32 qth_max; /* Max average length threshold (bytes) +*/ + __u32 DP; /* upto 2^32 DPs */ + __u32 backlog; + __u32 qave; + __u32 forced; + __u32 early; + __u32 other; + __u32 pdrop; + + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + __u8 prio; /* prio of this VQ */ + __u32 packets; + __u32 bytesin; +}; +/* gred setup */ +struct tc_gred_sopt +{ + __u32 DPs; + __u32 def_DP; + __u8 grio; +}; + +/* HTB section */ +#define TC_HTB_NUMPRIO 4 +#define TC_HTB_MAXDEPTH 4 + +struct tc_htb_opt +{ + struct tc_ratespec rate; + struct tc_ratespec ceil; + __u32 buffer; + __u32 cbuffer; + __u32 quantum; /* out only */ + __u32 level; /* out only */ + __u8 prio; + __u8 injectd; /* inject class distance */ + __u8 pad[2]; +}; +struct tc_htb_glob +{ + __u32 rate2quantum; /* bps->quantum divisor */ + __u32 defcls; /* default class number */ + __u32 use_dcache; /* use dequeue cache ? */ + __u32 debug; /* debug flags */ + + + /* stats */ + __u32 deq_rate; /* dequeue rate */ + __u32 utilz; /* dequeue utilization */ + __u32 trials; /* deq_prio trials per dequeue */ + __u32 dcache_hits; + __u32 direct_pkts; /* count of non shapped packets */ +}; +enum +{ + TCA_HTB_UNSPEC, + TCA_HTB_PARMS, + TCA_HTB_INIT, + TCA_HTB_CTAB, + TCA_HTB_RTAB, +}; +struct tc_htb_xstats +{ + __u32 lends; + __u32 borrows; + __u32 giants; /* too big packets (rate will not be accurate) */ + __u32 injects; /* how many times leaf used injected bw */ + __u32 tokens; + __u32 ctokens; +}; + +/* CBQ section */ + +#define TC_CBQ_MAXPRIO 8 +#define TC_CBQ_MAXLEVEL 8 +#define TC_CBQ_DEF_EWMA 5 + +struct tc_cbq_lssopt +{ + unsigned char change; + unsigned char flags; +#define TCF_CBQ_LSS_BOUNDED 1 +#define TCF_CBQ_LSS_ISOLATED 2 + unsigned char ewma_log; + unsigned char level; +#define TCF_CBQ_LSS_FLAGS 1 +#define TCF_CBQ_LSS_EWMA 2 +#define TCF_CBQ_LSS_MAXIDLE 4 +#define TCF_CBQ_LSS_MINIDLE 8 +#define TCF_CBQ_LSS_OFFTIME 0x10 +#define TCF_CBQ_LSS_AVPKT 0x20 + __u32 maxidle; + __u32 minidle; + __u32 offtime; + __u32 avpkt; +}; + +struct tc_cbq_wrropt +{ + unsigned char flags; + unsigned char priority; + unsigned char cpriority; + unsigned char __reserved; + __u32 allot; + __u32 weight; +}; + +struct tc_cbq_ovl +{ + unsigned char strategy; +#define TC_CBQ_OVL_CLASSIC 0 +#define TC_CBQ_OVL_DELAY 1 +#define TC_CBQ_OVL_LOWPRIO 2 +#define TC_CBQ_OVL_DROP 3 +#define TC_CBQ_OVL_RCLASSIC 4 + unsigned char priority2; + __u32 penalty; +}; + +struct tc_cbq_police +{ + unsigned char police; + unsigned char __res1; + unsigned short __res2; +}; + +struct tc_cbq_fopt +{ + __u32 split; + __u32 defmap; + __u32 defchange; +}; + +struct tc_cbq_xstats +{ + __u32 borrows; + __u32 overactions; + __s32 avgidle; + __s32 undertime; +}; + +enum +{ + TCA_CBQ_UNSPEC, + TCA_CBQ_LSSOPT, + TCA_CBQ_WRROPT, + TCA_CBQ_FOPT, + TCA_CBQ_OVL_STRATEGY, + TCA_CBQ_RATE, + TCA_CBQ_RTAB, + TCA_CBQ_POLICE, +}; + +#define TCA_CBQ_MAX TCA_CBQ_POLICE + +/* dsmark section */ + +enum { + TCA_DSMARK_UNSPEC, + TCA_DSMARK_INDICES, + TCA_DSMARK_DEFAULT_INDEX, + TCA_DSMARK_SET_TC_INDEX, + TCA_DSMARK_MASK, + TCA_DSMARK_VALUE +}; + +#define TCA_DSMARK_MAX TCA_DSMARK_VALUE + +/* ATM section */ + +enum { + TCA_ATM_UNSPEC, + TCA_ATM_FD, /* file/socket descriptor */ + TCA_ATM_PTR, /* pointer to descriptor - later */ + TCA_ATM_HDR, /* LL header */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ + TCA_ATM_ADDR, /* PVC address (for output only) */ + TCA_ATM_STATE /* VC state (ATM_VS_*; for output only) */ +}; + +#define TCA_ATM_MAX TCA_ATM_STATE + +#endif diff --git a/busybox/networking/libiproute/ll_addr.c b/busybox/networking/libiproute/ll_addr.c new file mode 100644 index 000000000..ada685f4e --- /dev/null +++ b/busybox/networking/libiproute/ll_addr.c @@ -0,0 +1,81 @@ +/* + * ll_addr.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + */ + +#include +#include +#include +#include "utils.h" +#include "libbb.h" + +const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen) +{ + int i; + int l; + + if (alen == 4 && + (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) { + return inet_ntop(AF_INET, addr, buf, blen); + } + l = 0; + for (i=0; i 255) { + bb_error_msg("\"%s\" is invalid lladdr.", arg); + return -1; + } + lladdr[i] = temp; + if (!cp) { + break; + } + arg = cp; + } + return i+1; + } +} diff --git a/busybox/networking/libiproute/ll_map.c b/busybox/networking/libiproute/ll_map.c new file mode 100644 index 000000000..b7a828421 --- /dev/null +++ b/busybox/networking/libiproute/ll_map.c @@ -0,0 +1,164 @@ +/* + * ll_map.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include + +#include "libnetlink.h" + +struct idxmap +{ + struct idxmap * next; + int index; + int type; + int alen; + unsigned flags; + unsigned char addr[8]; + char name[16]; +}; + +static struct idxmap *idxmap[16]; + +int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + int h; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct idxmap *im, **imp; + struct rtattr *tb[IFLA_MAX+1]; + + if (n->nlmsg_type != RTM_NEWLINK) + return 0; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi))) + return -1; + + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + if (tb[IFLA_IFNAME] == NULL) + return 0; + + h = ifi->ifi_index&0xF; + + for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next) + if (im->index == ifi->ifi_index) + break; + + if (im == NULL) { + im = malloc(sizeof(*im)); + if (im == NULL) + return 0; + im->next = *imp; + im->index = ifi->ifi_index; + *imp = im; + } + + im->type = ifi->ifi_type; + im->flags = ifi->ifi_flags; + if (tb[IFLA_ADDRESS]) { + int alen; + im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]); + if (alen > sizeof(im->addr)) + alen = sizeof(im->addr); + memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen); + } else { + im->alen = 0; + memset(im->addr, 0, sizeof(im->addr)); + } + strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME])); + return 0; +} + +const char *ll_idx_n2a(int idx, char *buf) +{ + struct idxmap *im; + + if (idx == 0) + return "*"; + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->name; + snprintf(buf, 16, "if%d", idx); + return buf; +} + + +const char *ll_index_to_name(int idx) +{ + static char nbuf[16]; + + return ll_idx_n2a(idx, nbuf); +} + +int ll_index_to_type(int idx) +{ + struct idxmap *im; + + if (idx == 0) + return -1; + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->type; + return -1; +} + +unsigned ll_index_to_flags(int idx) +{ + struct idxmap *im; + + if (idx == 0) + return 0; + + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->flags; + return 0; +} + +int ll_name_to_index(char *name) +{ + static char ncache[16]; + static int icache; + struct idxmap *im; + int i; + + if (name == NULL) + return 0; + if (icache && strcmp(name, ncache) == 0) + return icache; + for (i=0; i<16; i++) { + for (im = idxmap[i]; im; im = im->next) { + if (strcmp(im->name, name) == 0) { + icache = im->index; + strcpy(ncache, name); + return im->index; + } + } + } + return 0; +} + +int ll_init_map(struct rtnl_handle *rth) +{ + if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + return 0; +} diff --git a/busybox/networking/libiproute/ll_map.h b/busybox/networking/libiproute/ll_map.h new file mode 100644 index 000000000..739f157e7 --- /dev/null +++ b/busybox/networking/libiproute/ll_map.h @@ -0,0 +1,12 @@ +#ifndef __LL_MAP_H__ +#define __LL_MAP_H__ 1 + +extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); +extern int ll_init_map(struct rtnl_handle *rth); +extern int ll_name_to_index(char *name); +extern const char *ll_index_to_name(int idx); +extern const char *ll_idx_n2a(int idx, char *buf); +extern int ll_index_to_type(int idx); +extern unsigned ll_index_to_flags(int idx); + +#endif /* __LL_MAP_H__ */ diff --git a/busybox/networking/libiproute/ll_proto.c b/busybox/networking/libiproute/ll_proto.c new file mode 100644 index 000000000..9b5260b32 --- /dev/null +++ b/busybox/networking/libiproute/ll_proto.c @@ -0,0 +1,120 @@ +/* + * ll_proto.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + */ + +#include +#include +#include +#include "utils.h" + +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#else +#include +#endif + +#define __PF(f,n) { ETH_P_##f, #n }, +static struct { + int id; + char *name; +} llproto_names[] = { +__PF(LOOP,loop) +__PF(PUP,pup) +#ifdef ETH_P_PUPAT +__PF(PUPAT,pupat) +#endif +__PF(IP,ip) +__PF(X25,x25) +__PF(ARP,arp) +__PF(BPQ,bpq) +#ifdef ETH_P_IEEEPUP +__PF(IEEEPUP,ieeepup) +#endif +#ifdef ETH_P_IEEEPUPAT +__PF(IEEEPUPAT,ieeepupat) +#endif +__PF(DEC,dec) +__PF(DNA_DL,dna_dl) +__PF(DNA_RC,dna_rc) +__PF(DNA_RT,dna_rt) +__PF(LAT,lat) +__PF(DIAG,diag) +__PF(CUST,cust) +__PF(SCA,sca) +__PF(RARP,rarp) +__PF(ATALK,atalk) +__PF(AARP,aarp) +__PF(IPX,ipx) +__PF(IPV6,ipv6) +#ifdef ETH_P_PPP_DISC +__PF(PPP_DISC,ppp_disc) +#endif +#ifdef ETH_P_PPP_SES +__PF(PPP_SES,ppp_ses) +#endif +#ifdef ETH_P_ATMMPOA +__PF(ATMMPOA,atmmpoa) +#endif +#ifdef ETH_P_ATMFATE +__PF(ATMFATE,atmfate) +#endif + +__PF(802_3,802_3) +__PF(AX25,ax25) +__PF(ALL,all) +__PF(802_2,802_2) +__PF(SNAP,snap) +__PF(DDCMP,ddcmp) +__PF(WAN_PPP,wan_ppp) +__PF(PPP_MP,ppp_mp) +__PF(LOCALTALK,localtalk) +__PF(PPPTALK,ppptalk) +__PF(TR_802_2,tr_802_2) +__PF(MOBITEX,mobitex) +__PF(CONTROL,control) +__PF(IRDA,irda) +#ifdef ETH_P_ECONET +__PF(ECONET,econet) +#endif + +{ 0x8100, "802.1Q" }, +{ ETH_P_IP, "ipv4" }, +}; +#undef __PF + + +char * ll_proto_n2a(unsigned short id, char *buf, int len) +{ + int i; + + id = ntohs(id); + + for (i=0; i + */ +#include +#include + +#include + +char * ll_type_n2a(int type, char *buf, int len) +{ +#define __PF(f,n) { ARPHRD_##f, #n }, +static struct { + int type; + char *name; +} arphrd_names[] = { +{ 0, "generic" }, +__PF(ETHER,ether) +__PF(EETHER,eether) +__PF(AX25,ax25) +__PF(PRONET,pronet) +__PF(CHAOS,chaos) +#ifdef ARPHRD_IEEE802_TR +__PF(IEEE802,ieee802) +#else +__PF(IEEE802,tr) +#endif +__PF(ARCNET,arcnet) +__PF(APPLETLK,atalk) +__PF(DLCI,dlci) +#ifdef ARPHRD_ATM +__PF(ATM,atm) +#endif +__PF(METRICOM,metricom) +#ifdef ARPHRD_IEEE1394 +__PF(IEEE1394,ieee1394) +#endif + +__PF(SLIP,slip) +__PF(CSLIP,cslip) +__PF(SLIP6,slip6) +__PF(CSLIP6,cslip6) +__PF(RSRVD,rsrvd) +__PF(ADAPT,adapt) +__PF(ROSE,rose) +__PF(X25,x25) +#ifdef ARPHRD_HWX25 +__PF(HWX25,hwx25) +#endif +__PF(PPP,ppp) +__PF(HDLC,hdlc) +__PF(LAPB,lapb) +#ifdef ARPHRD_DDCMP +__PF(DDCMP,ddcmp) +__PF(RAWHDLC,rawhdlc) +#endif + +__PF(TUNNEL,ipip) +__PF(TUNNEL6,tunnel6) +__PF(FRAD,frad) +__PF(SKIP,skip) +__PF(LOOPBACK,loopback) +__PF(LOCALTLK,ltalk) +__PF(FDDI,fddi) +__PF(BIF,bif) +__PF(SIT,sit) +__PF(IPDDP,ip/ddp) +__PF(IPGRE,gre) +__PF(PIMREG,pimreg) +__PF(HIPPI,hippi) +__PF(ASH,ash) +__PF(ECONET,econet) +__PF(IRDA,irda) +__PF(FCPP,fcpp) +__PF(FCAL,fcal) +__PF(FCPL,fcpl) +__PF(FCFABRIC,fcfb0) +__PF(FCFABRIC+1,fcfb1) +__PF(FCFABRIC+2,fcfb2) +__PF(FCFABRIC+3,fcfb3) +__PF(FCFABRIC+4,fcfb4) +__PF(FCFABRIC+5,fcfb5) +__PF(FCFABRIC+6,fcfb6) +__PF(FCFABRIC+7,fcfb7) +__PF(FCFABRIC+8,fcfb8) +__PF(FCFABRIC+9,fcfb9) +__PF(FCFABRIC+10,fcfb10) +__PF(FCFABRIC+11,fcfb11) +__PF(FCFABRIC+12,fcfb12) +#ifdef ARPHRD_IEEE802_TR +__PF(IEEE802_TR,tr) +#endif +#ifdef ARPHRD_IEEE80211 +__PF(IEEE80211,ieee802.11) +#endif +#ifdef ARPHRD_VOID +__PF(VOID,void) +#endif +}; +#undef __PF + + int i; + for (i=0; i + */ +#include +#include +#include + +#include + +static void rtnl_tab_initialize(char *file, char **tab, int size) +{ + char buf[512]; + FILE *fp; + + fp = fopen(file, "r"); + if (!fp) + return; + while (fgets(buf, sizeof(buf), fp)) { + char *p = buf; + int id; + char namebuf[512]; + + while (*p == ' ' || *p == '\t') + p++; + if (*p == '#' || *p == '\n' || *p == 0) + continue; + if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 && + sscanf(p, "0x%x %s #", &id, namebuf) != 2 && + sscanf(p, "%d %s\n", &id, namebuf) != 2 && + sscanf(p, "%d %s #", &id, namebuf) != 2) { + fprintf(stderr, "Database %s is corrupted at %s\n", + file, p); + return; + } + + if (id<0 || id>size) + continue; + + tab[id] = strdup(namebuf); + } + fclose(fp); +} + + +static char * rtnl_rtprot_tab[256] = { + "none", + "redirect", + "kernel", + "boot", + "static", + NULL, + NULL, + NULL, + "gated", + "ra", + "mrt", + "zebra", + "bird", +}; + + + +static int rtnl_rtprot_init; + +static void rtnl_rtprot_initialize(void) +{ + rtnl_rtprot_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_protos", + rtnl_rtprot_tab, 256); +} + +char * rtnl_rtprot_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtprot_tab[id]) { + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + } + if (rtnl_rtprot_tab[id]) + return rtnl_rtprot_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtprot_a2n(uint32_t *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtprot_tab[i] && + strcmp(rtnl_rtprot_tab[i], arg) == 0) { + cache = rtnl_rtprot_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rtscope_tab[256] = { + "global", +}; + +static int rtnl_rtscope_init; + +static void rtnl_rtscope_initialize(void) +{ + rtnl_rtscope_init = 1; + rtnl_rtscope_tab[255] = "nowhere"; + rtnl_rtscope_tab[254] = "host"; + rtnl_rtscope_tab[253] = "link"; + rtnl_rtscope_tab[200] = "site"; + rtnl_tab_initialize("/etc/iproute2/rt_scopes", + rtnl_rtscope_tab, 256); +} + +char * rtnl_rtscope_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtscope_tab[id]) { + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + } + if (rtnl_rtscope_tab[id]) + return rtnl_rtscope_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtscope_a2n(uint32_t *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtscope_tab[i] && + strcmp(rtnl_rtscope_tab[i], arg) == 0) { + cache = rtnl_rtscope_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rtrealm_tab[256] = { + "unknown", +}; + +static int rtnl_rtrealm_init; + +static void rtnl_rtrealm_initialize(void) +{ + rtnl_rtrealm_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_realms", + rtnl_rtrealm_tab, 256); +} + +char * rtnl_rtrealm_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtrealm_tab[id]) { + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + } + if (rtnl_rtrealm_tab[id]) + return rtnl_rtrealm_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + + +int rtnl_rtrealm_a2n(uint32_t *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtrealm_tab[i] && + strcmp(rtnl_rtrealm_tab[i], arg) == 0) { + cache = rtnl_rtrealm_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rttable_tab[256] = { + "unspec", +}; + +static int rtnl_rttable_init; + +static void rtnl_rttable_initialize(void) +{ + rtnl_rttable_init = 1; + rtnl_rttable_tab[255] = "local"; + rtnl_rttable_tab[254] = "main"; + rtnl_tab_initialize("/etc/iproute2/rt_tables", + rtnl_rttable_tab, 256); +} + +char * rtnl_rttable_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rttable_tab[id]) { + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + } + if (rtnl_rttable_tab[id]) + return rtnl_rttable_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rttable_a2n(uint32_t *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rttable_tab[i] && + strcmp(rtnl_rttable_tab[i], arg) == 0) { + cache = rtnl_rttable_tab[i]; + res = i; + *id = res; + return 0; + } + } + + i = strtoul(arg, &end, 0); + if (!end || end == arg || *end || i > 255) + return -1; + *id = i; + return 0; +} + + +static char * rtnl_rtdsfield_tab[256] = { + "0", +}; + +static int rtnl_rtdsfield_init; + +static void rtnl_rtdsfield_initialize(void) +{ + rtnl_rtdsfield_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_dsfield", + rtnl_rtdsfield_tab, 256); +} + +char * rtnl_dsfield_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtdsfield_tab[id]) { + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + } + if (rtnl_rtdsfield_tab[id]) + return rtnl_rtdsfield_tab[id]; + snprintf(buf, len, "0x%02x", id); + return buf; +} + + +int rtnl_dsfield_a2n(uint32_t *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtdsfield_tab[i] && + strcmp(rtnl_rtdsfield_tab[i], arg) == 0) { + cache = rtnl_rtdsfield_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 16); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + diff --git a/busybox/networking/libiproute/rt_names.h b/busybox/networking/libiproute/rt_names.h new file mode 100644 index 000000000..97bc6169f --- /dev/null +++ b/busybox/networking/libiproute/rt_names.h @@ -0,0 +1,30 @@ +#ifndef RT_NAMES_H_ +#define RT_NAMES_H_ 1 + +#include + +const char* rtnl_rtprot_n2a(int id, char *buf, int len); +const char* rtnl_rtscope_n2a(int id, char *buf, int len); +const char* rtnl_rttable_n2a(int id, char *buf, int len); +const char* rtnl_rtrealm_n2a(int id, char *buf, int len); +const char* rtnl_dsfield_n2a(int id, char *buf, int len); +int rtnl_rtprot_a2n(int *id, char *arg); +int rtnl_rtscope_a2n(int *id, char *arg); +int rtnl_rttable_a2n(int *id, char *arg); +int rtnl_rtrealm_a2n(uint32_t *id, char *arg); +int rtnl_dsfield_a2n(uint32_t *id, char *arg); + +const char *inet_proto_n2a(int proto, char *buf, int len); +int inet_proto_a2n(char *buf); + + +const char * ll_type_n2a(int type, char *buf, int len); + +const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen); +int ll_addr_a2n(unsigned char *lladdr, int len, char *arg); + +const char * ll_proto_n2a(unsigned short id, char *buf, int len); +int ll_proto_a2n(unsigned short *id, char *buf); + + +#endif diff --git a/busybox/networking/libiproute/rtm_map.c b/busybox/networking/libiproute/rtm_map.c new file mode 100644 index 000000000..5f6a9e69c --- /dev/null +++ b/busybox/networking/libiproute/rtm_map.c @@ -0,0 +1,110 @@ +/* + * rtm_map.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include + +#include "rt_names.h" +#include "utils.h" + +char *rtnl_rtntype_n2a(int id, char *buf, int len) +{ + switch (id) { + case RTN_UNSPEC: + return "none"; + case RTN_UNICAST: + return "unicast"; + case RTN_LOCAL: + return "local"; + case RTN_BROADCAST: + return "broadcast"; + case RTN_ANYCAST: + return "anycast"; + case RTN_MULTICAST: + return "multicast"; + case RTN_BLACKHOLE: + return "blackhole"; + case RTN_UNREACHABLE: + return "unreachable"; + case RTN_PROHIBIT: + return "prohibit"; + case RTN_THROW: + return "throw"; + case RTN_NAT: + return "nat"; + case RTN_XRESOLVE: + return "xresolve"; + default: + snprintf(buf, len, "%d", id); + return buf; + } +} + + +int rtnl_rtntype_a2n(int *id, char *arg) +{ + char *end; + unsigned long res; + + if (strcmp(arg, "local") == 0) + res = RTN_LOCAL; + else if (strcmp(arg, "nat") == 0) + res = RTN_NAT; + else if (matches(arg, "broadcast") == 0 || + strcmp(arg, "brd") == 0) + res = RTN_BROADCAST; + else if (matches(arg, "anycast") == 0) + res = RTN_ANYCAST; + else if (matches(arg, "multicast") == 0) + res = RTN_MULTICAST; + else if (matches(arg, "prohibit") == 0) + res = RTN_PROHIBIT; + else if (matches(arg, "unreachable") == 0) + res = RTN_UNREACHABLE; + else if (matches(arg, "blackhole") == 0) + res = RTN_BLACKHOLE; + else if (matches(arg, "xresolve") == 0) + res = RTN_XRESOLVE; + else if (matches(arg, "unicast") == 0) + res = RTN_UNICAST; + else if (strcmp(arg, "throw") == 0) + res = RTN_THROW; + else { + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + } + *id = res; + return 0; +} + +int get_rt_realms(__u32 *realms, char *arg) +{ + __u32 realm = 0; + char *p = strchr(arg, '/'); + + *realms = 0; + if (p) { + *p = 0; + if (rtnl_rtrealm_a2n(realms, arg)) { + *p = '/'; + return -1; + } + *realms <<= 16; + *p = '/'; + arg = p+1; + } + if (*arg && rtnl_rtrealm_a2n(&realm, arg)) + return -1; + *realms |= realm; + return 0; +} diff --git a/busybox/networking/libiproute/rtm_map.h b/busybox/networking/libiproute/rtm_map.h new file mode 100644 index 000000000..70bda7d05 --- /dev/null +++ b/busybox/networking/libiproute/rtm_map.h @@ -0,0 +1,10 @@ +#ifndef __RTM_MAP_H__ +#define __RTM_MAP_H__ 1 + +char *rtnl_rtntype_n2a(int id, char *buf, int len); +int rtnl_rtntype_a2n(int *id, char *arg); + +int get_rt_realms(__u32 *realms, char *arg); + + +#endif /* __RTM_MAP_H__ */ diff --git a/busybox/networking/libiproute/utils.c b/busybox/networking/libiproute/utils.c new file mode 100644 index 000000000..fa1548609 --- /dev/null +++ b/busybox/networking/libiproute/utils.c @@ -0,0 +1,359 @@ +/* + * utils.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, + * + * + * Changes: + * + * Rani Assaf 980929: resolve addresses + */ + +#include +#include +#include +#include + +#include "utils.h" +#include "libbb.h" + +int get_integer(int *val, char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN) + return -1; + *val = res; + return 0; +} + +int get_unsigned(unsigned *val, char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > UINT_MAX) + return -1; + *val = res; + return 0; +} + +int get_u32(__u32 * val, char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) + return -1; + *val = res; + return 0; +} + +int get_u16(__u16 * val, char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFFFF) + return -1; + *val = res; + return 0; +} + +int get_u8(__u8 * val, char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFF) + return -1; + *val = res; + return 0; +} + +int get_s16(__s16 * val, char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000) + return -1; + *val = res; + return 0; +} + +int get_s8(__s8 * val, char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80) + return -1; + *val = res; + return 0; +} + +int get_addr_1(inet_prefix * addr, char *name, int family) +{ + char *cp; + unsigned char *ap = (unsigned char *) addr->data; + int i; + + memset(addr, 0, sizeof(*addr)); + + if (strcmp(name, "default") == 0 || + strcmp(name, "all") == 0 || strcmp(name, "any") == 0) { + addr->family = family; + addr->bytelen = (family == AF_INET6 ? 16 : 4); + addr->bitlen = -1; + return 0; + } + + if (strchr(name, ':')) { + addr->family = AF_INET6; + if (family != AF_UNSPEC && family != AF_INET6) + return -1; + if (inet_pton(AF_INET6, name, addr->data) <= 0) + return -1; + addr->bytelen = 16; + addr->bitlen = -1; + return 0; + } + + addr->family = AF_INET; + if (family != AF_UNSPEC && family != AF_INET) + return -1; + addr->bytelen = 4; + addr->bitlen = -1; + for (cp = name, i = 0; *cp; cp++) { + if (*cp <= '9' && *cp >= '0') { + ap[i] = 10 * ap[i] + (*cp - '0'); + continue; + } + if (*cp == '.' && ++i <= 3) + continue; + return -1; + } + return 0; +} + +int get_prefix_1(inet_prefix * dst, char *arg, int family) +{ + int err; + unsigned plen; + char *slash; + + memset(dst, 0, sizeof(*dst)); + + if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) { + dst->family = family; + dst->bytelen = 0; + dst->bitlen = 0; + return 0; + } + + slash = strchr(arg, '/'); + if (slash) + *slash = 0; + err = get_addr_1(dst, arg, family); + if (err == 0) { + switch (dst->family) { + case AF_INET6: + dst->bitlen = 128; + break; + default: + case AF_INET: + dst->bitlen = 32; + } + if (slash) { + if (get_integer(&plen, slash + 1, 0) || plen > dst->bitlen) { + err = -1; + goto done; + } + dst->bitlen = plen; + } + } + done: + if (slash) + *slash = '/'; + return err; +} + +int get_addr(inet_prefix * dst, char *arg, int family) +{ + if (family == AF_PACKET) { + bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg); + } + if (get_addr_1(dst, arg, family)) { + bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg); + } + return 0; +} + +int get_prefix(inet_prefix * dst, char *arg, int family) +{ + if (family == AF_PACKET) { + bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg); + } + if (get_prefix_1(dst, arg, family)) { + bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg); + } + return 0; +} + +__u32 get_addr32(char *name) +{ + inet_prefix addr; + + if (get_addr_1(&addr, name, AF_INET)) { + bb_error_msg_and_die("an IP address is expected rather than \"%s\"", name); + } + return addr.data[0]; +} + +void incomplete_command() +{ + bb_error_msg("Command line is not complete. Try option \"help\""); + exit(-1); +} + +void invarg(char *msg, char *arg) +{ + bb_error_msg("argument \"%s\" is wrong: %s", arg, msg); + exit(-1); +} + +void duparg(char *key, char *arg) +{ + bb_error_msg("duplicate \"%s\": \"%s\" is the second value.", key, arg); + exit(-1); +} + +void duparg2(char *key, char *arg) +{ + bb_error_msg("either \"%s\" is duplicate, or \"%s\" is a garbage.", key, arg); + exit(-1); +} + +int matches(char *cmd, char *pattern) +{ + int len = strlen(cmd); + + if (len > strlen(pattern)) { + return -1; + } + return memcmp(pattern, cmd, len); +} + +int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits) +{ + __u32 *a1 = a->data; + __u32 *a2 = b->data; + int words = bits >> 0x05; + + bits &= 0x1f; + + if (words) + if (memcmp(a1, a2, words << 2)) + return -1; + + if (bits) { + __u32 w1, w2; + __u32 mask; + + w1 = a1[words]; + w2 = a2[words]; + + mask = htonl((0xffffffff) << (0x20 - bits)); + + if ((w1 ^ w2) & mask) + return 1; + } + + return 0; +} + +int __iproute2_hz_internal; + +int __get_hz(void) +{ + int hz = 0; + FILE *fp = fopen("/proc/net/psched", "r"); + + if (fp) { + unsigned nom, denom; + + if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) + if (nom == 1000000) + hz = denom; + fclose(fp); + } + if (hz) + return hz; + return sysconf(_SC_CLK_TCK); +} + +const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen) +{ + switch (af) { + case AF_INET: + case AF_INET6: + return inet_ntop(af, addr, buf, buflen); + default: + return "???"; + } +} + + +const char *format_host(int af, int len, void *addr, char *buf, int buflen) +{ +#ifdef RESOLVE_HOSTNAMES + if (resolve_hosts) { + struct hostent *h_ent; + + if (len <= 0) { + switch (af) { + case AF_INET: + len = 4; + break; + case AF_INET6: + len = 16; + break; + default:; + } + } + if (len > 0 && (h_ent = gethostbyaddr(addr, len, af)) != NULL) { + snprintf(buf, buflen - 1, "%s", h_ent->h_name); + return buf; + } + } +#endif + return rt_addr_n2a(af, len, addr, buf, buflen); +} diff --git a/busybox/networking/libiproute/utils.h b/busybox/networking/libiproute/utils.h new file mode 100644 index 000000000..e79e177b9 --- /dev/null +++ b/busybox/networking/libiproute/utils.h @@ -0,0 +1,101 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ 1 + +#include +#include + +#include "libnetlink.h" +#include "ll_map.h" +#include "rtm_map.h" + +extern int preferred_family; +extern int show_stats; +extern int show_details; +extern int show_raw; +extern int resolve_hosts; +extern int oneline; +extern char * _SL_; + +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif + +#define SPRINT_BSIZE 64 +#define SPRINT_BUF(x) char x[SPRINT_BSIZE] + +extern void incomplete_command(void) __attribute__((noreturn)); + +#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0) + +typedef struct +{ + __u8 family; + __u8 bytelen; + __s16 bitlen; + __u32 data[4]; +} inet_prefix; + +#define DN_MAXADDL 20 +#ifndef AF_DECnet +#define AF_DECnet 12 +#endif + +struct dn_naddr +{ + unsigned short a_len; + unsigned char a_addr[DN_MAXADDL]; +}; + +#define IPX_NODE_LEN 6 + +struct ipx_addr { + uint32_t ipx_net; + uint8_t ipx_node[IPX_NODE_LEN]; +}; + +extern __u32 get_addr32(char *name); +extern int get_addr_1(inet_prefix *dst, char *arg, int family); +extern int get_prefix_1(inet_prefix *dst, char *arg, int family); +extern int get_addr(inet_prefix *dst, char *arg, int family); +extern int get_prefix(inet_prefix *dst, char *arg, int family); + +extern int get_integer(int *val, char *arg, int base); +extern int get_unsigned(unsigned *val, char *arg, int base); +#define get_byte get_u8 +#define get_ushort get_u16 +#define get_short get_s16 +extern int get_u32(__u32 *val, char *arg, int base); +extern int get_u16(__u16 *val, char *arg, int base); +extern int get_s16(__s16 *val, char *arg, int base); +extern int get_u8(__u8 *val, char *arg, int base); +extern int get_s8(__s8 *val, char *arg, int base); + +extern const char *format_host(int af, int len, void *addr, char *buf, int buflen); +extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen); + +void invarg(char *, char *) __attribute__((noreturn)); +void duparg(char *, char *) __attribute__((noreturn)); +void duparg2(char *, char *) __attribute__((noreturn)); +int matches(char *arg, char *pattern); +extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits); + +const char *dnet_ntop(int af, const void *addr, char *str, size_t len); +int dnet_pton(int af, const char *src, void *addr); + +const char *ipx_ntop(int af, const void *addr, char *str, size_t len); +int ipx_pton(int af, const char *src, void *addr); + +extern int __iproute2_hz_internal; +extern int __get_hz(void); + +static __inline__ int get_hz(void) +{ + if (__iproute2_hz_internal == 0) + __iproute2_hz_internal = __get_hz(); + return __iproute2_hz_internal; +} + +#endif /* __UTILS_H__ */ diff --git a/busybox/networking/nameif.c b/busybox/networking/nameif.c new file mode 100644 index 000000000..10f13b4bc --- /dev/null +++ b/busybox/networking/nameif.c @@ -0,0 +1,222 @@ +/* + * nameif.c - Naming Interfaces based on MAC address for busybox. + * + * Written 2000 by Andi Kleen. + * Busybox port 2002 by Nick Fedchik + * Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "busybox.h" + +/* Older versions of net/if.h do not appear to define IF_NAMESIZE. */ +#ifndef IF_NAMESIZE +# ifdef IFNAMSIZ +# define IF_NAMESIZE IFNAMSIZ +# else +# define IF_NAMESIZE 16 +# endif +#endif + +/* take from linux/sockios.h */ +#define SIOCSIFNAME 0x8923 /* set interface name */ + +/* Octets in one Ethernet addr, from */ +#define ETH_ALEN 6 + +#ifndef ifr_newname +#define ifr_newname ifr_ifru.ifru_slave +#endif + +typedef struct mactable_s { + struct mactable_s *next; + struct mactable_s *prev; + char *ifname; + struct ether_addr *mac; +} mactable_t; + +static unsigned char use_syslog; + +static void serror(const char *s, ...) __attribute__ ((noreturn)); + +static void serror(const char *s, ...) +{ + va_list ap; + + va_start(ap, s); + + if (use_syslog) { + openlog(bb_applet_name, 0, LOG_LOCAL0); + vsyslog(LOG_ERR, s, ap); + closelog(); + } else { + bb_verror_msg(s, ap); + putc('\n', stderr); + } + + va_end(ap); + + exit(EXIT_FAILURE); +} + +/* Check ascii str_macaddr, convert and copy to *mac */ +struct ether_addr *cc_macaddr(char *str_macaddr) +{ + struct ether_addr *lmac, *mac; + + lmac = ether_aton(str_macaddr); + if (lmac == NULL) + serror("cannot parse MAC %s", str_macaddr); + mac = xmalloc(ETH_ALEN); + memcpy(mac, lmac, ETH_ALEN); + + return mac; +} + +int nameif_main(int argc, char **argv) +{ + mactable_t *clist = NULL; + FILE *ifh; + const char *fname = "/etc/mactab"; + char *line; + int ctl_sk; + int opt; + int if_index = 1; + mactable_t *ch; + + + while ((opt = getopt(argc, argv, "c:s")) != -1) { + switch (opt) { + case 'c': + fname = optarg; + break; + case 's': + use_syslog = 1; + break; + default: + bb_show_usage(); + } + } + + if ((argc - optind) & 1) + bb_show_usage(); + + if (optind < argc) { + char **a = argv + optind; + + while (*a) { + + if (strlen(*a) > IF_NAMESIZE) + serror("interface name `%s' too long", *a); + ch = xcalloc(1, sizeof(mactable_t)); + ch->ifname = bb_xstrdup(*a++); + ch->mac = cc_macaddr(*a++); + if (clist) + clist->prev = ch; + ch->next = clist; + clist = ch; + } + } else { + ifh = bb_xfopen(fname, "r"); + + while ((line = bb_get_line_from_file(ifh)) != NULL) { + char *line_ptr; + size_t name_length; + + line_ptr = line + strspn(line, " \t"); + if ((line_ptr[0] == '#') || (line_ptr[0] == '\n')) + continue; + name_length = strcspn(line_ptr, " \t"); + ch = xcalloc(1, sizeof(mactable_t)); + ch->ifname = bb_xstrndup(line_ptr, name_length); + if (name_length > IF_NAMESIZE) + serror("interface name `%s' too long", ch->ifname); + line_ptr += name_length; + line_ptr += strspn(line_ptr, " \t"); + name_length = strspn(line_ptr, "0123456789ABCDEFabcdef:"); + line_ptr[name_length] = '\0'; + ch->mac = cc_macaddr(line_ptr); + if (clist) + clist->prev = ch; + ch->next = clist; + clist = ch; + free(line); + } + fclose(ifh); + } + + if ((ctl_sk = socket(PF_INET, SOCK_DGRAM, 0)) == -1) + serror("socket: %m"); + + while (clist) { + struct ifreq ifr; + + bzero(&ifr, sizeof(struct ifreq)); + if_index++; + ifr.ifr_ifindex = if_index; + + /* Get ifname by index or die */ + if (ioctl(ctl_sk, SIOCGIFNAME, &ifr)) + break; + + /* Has this device hwaddr? */ + if (ioctl(ctl_sk, SIOCGIFHWADDR, &ifr)) + continue; + + /* Search for mac like in ifr.ifr_hwaddr.sa_data */ + for (ch = clist; ch; ch = ch->next) + if (!memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN)) + break; + + /* Nothing found for current ifr.ifr_hwaddr.sa_data */ + if (ch == NULL) + continue; + + strcpy(ifr.ifr_newname, ch->ifname); + if (ioctl(ctl_sk, SIOCSIFNAME, &ifr) < 0) + serror("cannot change ifname %s to %s: %m", + ifr.ifr_name, ch->ifname); + + /* Remove list entry of renamed interface */ + if (ch->prev != NULL) { + (ch->prev)->next = ch->next; + } else { + clist = ch->next; + } + if (ch->next != NULL) + (ch->next)->prev = ch->prev; +#ifdef CONFIG_FEATURE_CLEAN_UP + free(ch->ifname); + free(ch->mac); + free(ch); +#endif + } + + return 0; +} diff --git a/busybox/networking/nc.c b/busybox/networking/nc.c new file mode 100644 index 000000000..3099763b1 --- /dev/null +++ b/busybox/networking/nc.c @@ -0,0 +1,177 @@ +/* vi: set sw=4 ts=4: */ +/* nc: mini-netcat - built from the ground up for LRP + Copyright (C) 1998 Charles P. Wright + + 0.0.1 6K It works. + 0.0.2 5K Smaller and you can also check the exit condition if you wish. + 0.0.3 Uses select() + + 19980918 Busy Boxed! Dave Cinege + 19990512 Uses Select. Charles P. Wright + 19990513 Fixes stdin stupidity and uses buffers. Charles P. Wright + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define GAPING_SECURITY_HOLE + +int nc_main(int argc, char **argv) +{ + int do_listen = 0, lport = 0, delay = 0, tmpfd, opt, sfd, x; + char buf[BUFSIZ]; +#ifdef GAPING_SECURITY_HOLE + char * pr00gie = NULL; +#endif + + struct sockaddr_in address; + struct hostent *hostinfo; + + fd_set readfds, testfds; + + while ((opt = getopt(argc, argv, "lp:i:e:")) > 0) { + switch (opt) { + case 'l': + do_listen++; + break; + case 'p': + lport = bb_lookup_port(optarg, "tcp", 0); + break; + case 'i': + delay = atoi(optarg); + break; +#ifdef GAPING_SECURITY_HOLE + case 'e': + pr00gie = optarg; + break; +#endif + default: + bb_show_usage(); + } + } + +#ifdef GAPING_SECURITY_HOLE + if (pr00gie) { + /* won't need stdin */ + close (STDIN_FILENO); + } +#endif /* GAPING_SECURITY_HOLE */ + + + if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc)) + bb_show_usage(); + + if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + bb_perror_msg_and_die("socket"); + x = 1; + if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1) + bb_perror_msg_and_die ("reuseaddr failed"); + address.sin_family = AF_INET; + + if (lport != 0) { + memset(&address.sin_addr, 0, sizeof(address.sin_addr)); + address.sin_port = lport; + + if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + bb_perror_msg_and_die("bind"); + } + + if (do_listen) { + socklen_t addrlen = sizeof(address); + + if (listen(sfd, 1) < 0) + bb_perror_msg_and_die("listen"); + + if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0) + bb_perror_msg_and_die("accept"); + + close(sfd); + sfd = tmpfd; + } else { + hostinfo = xgethostbyname(argv[optind]); + + address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; + address.sin_port = bb_lookup_port(argv[optind+1], "tcp", 0); + + if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + bb_perror_msg_and_die("connect"); + } + +#ifdef GAPING_SECURITY_HOLE + /* -e given? */ + if (pr00gie) { + dup2(sfd, 0); + close(sfd); + dup2 (0, 1); + dup2 (0, 2); + execl (pr00gie, pr00gie, NULL); + /* Don't print stuff or it will go over the wire.... */ + _exit(-1); + } +#endif /* GAPING_SECURITY_HOLE */ + + + FD_ZERO(&readfds); + FD_SET(sfd, &readfds); + FD_SET(STDIN_FILENO, &readfds); + + while (1) { + int fd; + int ofd; + int nread; + + testfds = readfds; + + if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0) + bb_perror_msg_and_die("select"); + + for (fd = 0; fd < FD_SETSIZE; fd++) { + if (FD_ISSET(fd, &testfds)) { + if ((nread = safe_read(fd, buf, sizeof(buf))) < 0) + bb_perror_msg_and_die("read"); + + if (fd == sfd) { + if (nread == 0) + exit(0); + ofd = STDOUT_FILENO; + } else { + if (nread == 0) + shutdown(sfd, 1); + ofd = sfd; + } + + if (bb_full_write(ofd, buf, nread) < 0) + bb_perror_msg_and_die("write"); + if (delay > 0) { + sleep(delay); + } + } + } + } +} diff --git a/busybox/networking/netstat.c b/busybox/networking/netstat.c new file mode 100644 index 000000000..bc1ed057b --- /dev/null +++ b/busybox/networking/netstat.c @@ -0,0 +1,661 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini netstat implementation(s) for busybox + * based in part on the netstat implementation from net-tools. + * + * Copyright (C) 2002 by Bart Visscher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 2002-04-20 + * IPV6 support added by Bart Visscher + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "inet_common.h" +#include "busybox.h" +#include "pwd_.h" + +#ifdef CONFIG_ROUTE +extern void displayroutes(int noresolve, int netstatfmt); +#endif + +#define NETSTAT_CONNECTED 0x01 +#define NETSTAT_LISTENING 0x02 +#define NETSTAT_NUMERIC 0x04 +#define NETSTAT_TCP 0x10 +#define NETSTAT_UDP 0x20 +#define NETSTAT_RAW 0x40 +#define NETSTAT_UNIX 0x80 + +static int flags = NETSTAT_CONNECTED | + NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX; + +#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH) +#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s) +#define PROGNAME_WIDTH2(s) #s + +#define PRG_HASH_SIZE 211 + +enum { + TCP_ESTABLISHED = 1, + TCP_SYN_SENT, + TCP_SYN_RECV, + TCP_FIN_WAIT1, + TCP_FIN_WAIT2, + TCP_TIME_WAIT, + TCP_CLOSE, + TCP_CLOSE_WAIT, + TCP_LAST_ACK, + TCP_LISTEN, + TCP_CLOSING /* now a valid state */ +}; + +static const char * const tcp_state[] = +{ + "", + "ESTABLISHED", + "SYN_SENT", + "SYN_RECV", + "FIN_WAIT1", + "FIN_WAIT2", + "TIME_WAIT", + "CLOSE", + "CLOSE_WAIT", + "LAST_ACK", + "LISTEN", + "CLOSING" +}; + +typedef enum { + SS_FREE = 0, /* not allocated */ + SS_UNCONNECTED, /* unconnected to any socket */ + SS_CONNECTING, /* in process of connecting */ + SS_CONNECTED, /* connected to socket */ + SS_DISCONNECTING /* in process of disconnecting */ +} socket_state; + +#define SO_ACCEPTCON (1<<16) /* performed a listen */ +#define SO_WAITDATA (1<<17) /* wait data to read */ +#define SO_NOSPACE (1<<18) /* no space to write */ + +static char *itoa(unsigned int i) +{ + /* 21 digits plus null terminator, good for 64-bit or smaller ints */ + static char local[22]; + char *p = &local[21]; + *p-- = '\0'; + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + return p + 1; +} + +static char *get_sname(int port, const char *proto, int num) +{ + char *str=itoa(ntohs(port)); + if (num) { + } else { + struct servent *se=getservbyport(port,proto); + if (se) + str=se->s_name; + } + if (!port) { + str="*"; + } + return str; +} + +static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric) +{ + char *port_name; + +#ifdef CONFIG_FEATURE_IPV6 + if (addr->sa_family == AF_INET6) { + INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr, + (numeric&NETSTAT_NUMERIC) ? 0x0fff : 0); + } else +#endif + { + INET_rresolve(ip_port, size, (struct sockaddr_in *)addr, + 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0), + 0xffffffff); + } + port_name=get_sname(htons(port), proto, numeric); + if ((strlen(ip_port) + strlen(port_name)) > 22) + ip_port[22 - strlen(port_name)] = '\0'; + ip_port+=strlen(ip_port); + strcat(ip_port, ":"); + strcat(ip_port, port_name); +} + +static void tcp_do_one(int lnr, const char *line) +{ + char local_addr[64], rem_addr[64]; + const char *state_str; + char more[512]; + int num, local_port, rem_port, d, state, timer_run, uid, timeout; +#ifdef CONFIG_FEATURE_IPV6 + struct sockaddr_in6 localaddr, remaddr; + char addr6[INET6_ADDRSTRLEN]; + struct in6_addr in6; +#else + struct sockaddr_in localaddr, remaddr; +#endif + unsigned long rxq, txq, time_len, retr, inode; + + if (lnr == 0) + return; + + more[0] = '\0'; + num = sscanf(line, + "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", + &d, local_addr, &local_port, + rem_addr, &rem_port, &state, + &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); + + if (strlen(local_addr) > 8) { +#ifdef CONFIG_FEATURE_IPV6 + sscanf(local_addr, "%08X%08X%08X%08X", + &in6.s6_addr32[0], &in6.s6_addr32[1], + &in6.s6_addr32[2], &in6.s6_addr32[3]); + inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); + inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr); + sscanf(rem_addr, "%08X%08X%08X%08X", + &in6.s6_addr32[0], &in6.s6_addr32[1], + &in6.s6_addr32[2], &in6.s6_addr32[3]); + inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); + inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr); + localaddr.sin6_family = AF_INET6; + remaddr.sin6_family = AF_INET6; +#endif + } else { + sscanf(local_addr, "%X", + &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); + sscanf(rem_addr, "%X", + &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); + ((struct sockaddr *) &localaddr)->sa_family = AF_INET; + ((struct sockaddr *) &remaddr)->sa_family = AF_INET; + } + + if (num < 10) { + bb_error_msg("warning, got bogus tcp line."); + return; + } + state_str = tcp_state[state]; + if ((rem_port && (flags&NETSTAT_CONNECTED)) || + (!rem_port && (flags&NETSTAT_LISTENING))) + { + snprint_ip_port(local_addr, sizeof(local_addr), + (struct sockaddr *) &localaddr, local_port, + "tcp", flags&NETSTAT_NUMERIC); + + snprint_ip_port(rem_addr, sizeof(rem_addr), + (struct sockaddr *) &remaddr, rem_port, + "tcp", flags&NETSTAT_NUMERIC); + + printf("tcp %6ld %6ld %-23s %-23s %-12s\n", + rxq, txq, local_addr, rem_addr, state_str); + + } +} + +static void udp_do_one(int lnr, const char *line) +{ + char local_addr[64], rem_addr[64]; + char *state_str, more[512]; + int num, local_port, rem_port, d, state, timer_run, uid, timeout; +#ifdef CONFIG_FEATURE_IPV6 + struct sockaddr_in6 localaddr, remaddr; + char addr6[INET6_ADDRSTRLEN]; + struct in6_addr in6; +#else + struct sockaddr_in localaddr, remaddr; +#endif + unsigned long rxq, txq, time_len, retr, inode; + + if (lnr == 0) + return; + + more[0] = '\0'; + num = sscanf(line, + "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", + &d, local_addr, &local_port, + rem_addr, &rem_port, &state, + &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); + + if (strlen(local_addr) > 8) { +#ifdef CONFIG_FEATURE_IPV6 + /* Demangle what the kernel gives us */ + sscanf(local_addr, "%08X%08X%08X%08X", + &in6.s6_addr32[0], &in6.s6_addr32[1], + &in6.s6_addr32[2], &in6.s6_addr32[3]); + inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); + inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr); + sscanf(rem_addr, "%08X%08X%08X%08X", + &in6.s6_addr32[0], &in6.s6_addr32[1], + &in6.s6_addr32[2], &in6.s6_addr32[3]); + inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); + inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr); + localaddr.sin6_family = AF_INET6; + remaddr.sin6_family = AF_INET6; +#endif + } else { + sscanf(local_addr, "%X", + &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); + sscanf(rem_addr, "%X", + &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); + ((struct sockaddr *) &localaddr)->sa_family = AF_INET; + ((struct sockaddr *) &remaddr)->sa_family = AF_INET; + } + + if (num < 10) { + bb_error_msg("warning, got bogus udp line."); + return; + } + switch (state) { + case TCP_ESTABLISHED: + state_str = "ESTABLISHED"; + break; + + case TCP_CLOSE: + state_str = ""; + break; + + default: + state_str = "UNKNOWN"; + break; + } + +#ifdef CONFIG_FEATURE_IPV6 +# define notnull(A) (((A.sin6_family == AF_INET6) && \ + ((A.sin6_addr.s6_addr32[0]) || \ + (A.sin6_addr.s6_addr32[1]) || \ + (A.sin6_addr.s6_addr32[2]) || \ + (A.sin6_addr.s6_addr32[3]))) || \ + ((A.sin6_family == AF_INET) && \ + ((struct sockaddr_in *) &A)->sin_addr.s_addr)) +#else +# define notnull(A) (A.sin_addr.s_addr) +#endif + if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) || + (!notnull(remaddr) && (flags&NETSTAT_LISTENING))) + { + snprint_ip_port(local_addr, sizeof(local_addr), + (struct sockaddr *) &localaddr, local_port, + "udp", flags&NETSTAT_NUMERIC); + + snprint_ip_port(rem_addr, sizeof(rem_addr), + (struct sockaddr *) &remaddr, rem_port, + "udp", flags&NETSTAT_NUMERIC); + + printf("udp %6ld %6ld %-23s %-23s %-12s\n", + rxq, txq, local_addr, rem_addr, state_str); + + } +} + +static void raw_do_one(int lnr, const char *line) +{ + char local_addr[64], rem_addr[64]; + char *state_str, more[512]; + int num, local_port, rem_port, d, state, timer_run, uid, timeout; +#ifdef CONFIG_FEATURE_IPV6 + struct sockaddr_in6 localaddr, remaddr; + char addr6[INET6_ADDRSTRLEN]; + struct in6_addr in6; +#else + struct sockaddr_in localaddr, remaddr; +#endif + unsigned long rxq, txq, time_len, retr, inode; + + if (lnr == 0) + return; + + more[0] = '\0'; + num = sscanf(line, + "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", + &d, local_addr, &local_port, + rem_addr, &rem_port, &state, + &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); + + if (strlen(local_addr) > 8) { +#ifdef CONFIG_FEATURE_IPV6 + sscanf(local_addr, "%08X%08X%08X%08X", + &in6.s6_addr32[0], &in6.s6_addr32[1], + &in6.s6_addr32[2], &in6.s6_addr32[3]); + inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); + inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr); + sscanf(rem_addr, "%08X%08X%08X%08X", + &in6.s6_addr32[0], &in6.s6_addr32[1], + &in6.s6_addr32[2], &in6.s6_addr32[3]); + inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6)); + inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr); + localaddr.sin6_family = AF_INET6; + remaddr.sin6_family = AF_INET6; +#endif + } else { + sscanf(local_addr, "%X", + &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); + sscanf(rem_addr, "%X", + &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); + ((struct sockaddr *) &localaddr)->sa_family = AF_INET; + ((struct sockaddr *) &remaddr)->sa_family = AF_INET; + } + + if (num < 10) { + bb_error_msg("warning, got bogus raw line."); + return; + } + state_str=itoa(state); + +#ifdef CONFIG_FEATURE_IPV6 +# define notnull(A) (((A.sin6_family == AF_INET6) && \ + ((A.sin6_addr.s6_addr32[0]) || \ + (A.sin6_addr.s6_addr32[1]) || \ + (A.sin6_addr.s6_addr32[2]) || \ + (A.sin6_addr.s6_addr32[3]))) || \ + ((A.sin6_family == AF_INET) && \ + ((struct sockaddr_in *) &A)->sin_addr.s_addr)) +#else +# define notnull(A) (A.sin_addr.s_addr) +#endif + if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) || + (!notnull(remaddr) && (flags&NETSTAT_LISTENING))) + { + snprint_ip_port(local_addr, sizeof(local_addr), + (struct sockaddr *) &localaddr, local_port, + "raw", flags&NETSTAT_NUMERIC); + + snprint_ip_port(rem_addr, sizeof(rem_addr), + (struct sockaddr *) &remaddr, rem_port, + "raw", flags&NETSTAT_NUMERIC); + + printf("raw %6ld %6ld %-23s %-23s %-12s\n", + rxq, txq, local_addr, rem_addr, state_str); + + } +} + +#define HAS_INODE 1 + +static void unix_do_one(int nr, const char *line) +{ + static int has = 0; + char path[PATH_MAX], ss_flags[32]; + char *ss_proto, *ss_state, *ss_type; + int num, state, type, inode; + void *d; + unsigned long refcnt, proto, unix_flags; + + if (nr == 0) { + if (strstr(line, "Inode")) + has |= HAS_INODE; + return; + } + path[0] = '\0'; + num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s", + &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path); + if (num < 6) { + bb_error_msg("warning, got bogus unix line."); + return; + } + if (!(has & HAS_INODE)) + snprintf(path,sizeof(path),"%d",inode); + + if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) { + if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) { + if (!(flags&NETSTAT_LISTENING)) + return; + } else { + if (!(flags&NETSTAT_CONNECTED)) + return; + } + } + + switch (proto) { + case 0: + ss_proto = "unix"; + break; + + default: + ss_proto = "??"; + } + + switch (type) { + case SOCK_STREAM: + ss_type = "STREAM"; + break; + + case SOCK_DGRAM: + ss_type = "DGRAM"; + break; + + case SOCK_RAW: + ss_type = "RAW"; + break; + + case SOCK_RDM: + ss_type = "RDM"; + break; + + case SOCK_SEQPACKET: + ss_type = "SEQPACKET"; + break; + + default: + ss_type = "UNKNOWN"; + } + + switch (state) { + case SS_FREE: + ss_state = "FREE"; + break; + + case SS_UNCONNECTED: + /* + * Unconnected sockets may be listening + * for something. + */ + if (unix_flags & SO_ACCEPTCON) { + ss_state = "LISTENING"; + } else { + ss_state = ""; + } + break; + + case SS_CONNECTING: + ss_state = "CONNECTING"; + break; + + case SS_CONNECTED: + ss_state = "CONNECTED"; + break; + + case SS_DISCONNECTING: + ss_state = "DISCONNECTING"; + break; + + default: + ss_state = "UNKNOWN"; + } + + strcpy(ss_flags, "[ "); + if (unix_flags & SO_ACCEPTCON) + strcat(ss_flags, "ACC "); + if (unix_flags & SO_WAITDATA) + strcat(ss_flags, "W "); + if (unix_flags & SO_NOSPACE) + strcat(ss_flags, "N "); + + strcat(ss_flags, "]"); + + printf("%-5s %-6ld %-11s %-10s %-13s ", + ss_proto, refcnt, ss_flags, ss_type, ss_state); + if (has & HAS_INODE) + printf("%-6d ",inode); + else + printf("- "); + puts(path); +} + +#define _PATH_PROCNET_UDP "/proc/net/udp" +#define _PATH_PROCNET_UDP6 "/proc/net/udp6" +#define _PATH_PROCNET_TCP "/proc/net/tcp" +#define _PATH_PROCNET_TCP6 "/proc/net/tcp6" +#define _PATH_PROCNET_RAW "/proc/net/raw" +#define _PATH_PROCNET_RAW6 "/proc/net/raw6" +#define _PATH_PROCNET_UNIX "/proc/net/unix" + +static void do_info(const char *file, const char *name, void (*proc)(int, const char *)) +{ + char buffer[8192]; + int lnr = 0; + FILE *procinfo; + + procinfo = fopen(file, "r"); + if (procinfo == NULL) { + if (errno != ENOENT) { + perror(file); + } else { + bb_error_msg("no support for `%s' on this system.", name); + } + } else { + do { + if (fgets(buffer, sizeof(buffer), procinfo)) + (proc)(lnr++, buffer); + } while (!feof(procinfo)); + fclose(procinfo); + } +} + +/* + * Our main function. + */ + +int netstat_main(int argc, char **argv) +{ + int opt; + int new_flags=0; + int showroute = 0, extended = 0; +#ifdef CONFIG_FEATURE_IPV6 + int inet=1; + int inet6=1; +#else +# define inet 1 +# define inet6 0 +#endif + while ((opt = getopt(argc, argv, "laenrtuwx")) != -1) + switch (opt) { + case 'l': + flags &= ~NETSTAT_CONNECTED; + flags |= NETSTAT_LISTENING; + break; + case 'a': + flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; + break; + case 'n': + flags |= NETSTAT_NUMERIC; + break; + case 'r': + showroute = 1; + break; + case 'e': + extended = 1; + break; + case 't': + new_flags |= NETSTAT_TCP; + break; + case 'u': + new_flags |= NETSTAT_UDP; + break; + case 'w': + new_flags |= NETSTAT_RAW; + break; + case 'x': + new_flags |= NETSTAT_UNIX; + break; + default: + bb_show_usage(); + } + if ( showroute ) { +#ifdef CONFIG_ROUTE + displayroutes ( flags & NETSTAT_NUMERIC, !extended ); + return 0; +#else + bb_error_msg_and_die( "-r (display routing table) is not compiled in." ); +#endif + } + + if (new_flags) { + flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX); + flags |= new_flags; + } + if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) { + printf("Active Internet connections "); /* xxx */ + + if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED)) + printf("(servers and established)"); + else { + if (flags&NETSTAT_LISTENING) + printf("(only servers)"); + else + printf("(w/o servers)"); + } + printf("\nProto Recv-Q Send-Q Local Address Foreign Address State \n"); + } + if (inet && flags&NETSTAT_TCP) + do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one); +#ifdef CONFIG_FEATURE_IPV6 + if (inet6 && flags&NETSTAT_TCP) + do_info(_PATH_PROCNET_TCP6,"AF INET6 (tcp)",tcp_do_one); +#endif + if (inet && flags&NETSTAT_UDP) + do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one); +#ifdef CONFIG_FEATURE_IPV6 + if (inet6 && flags&NETSTAT_UDP) + do_info(_PATH_PROCNET_UDP6,"AF INET6 (udp)",udp_do_one); +#endif + if (inet && flags&NETSTAT_RAW) + do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one); +#ifdef CONFIG_FEATURE_IPV6 + if (inet6 && flags&NETSTAT_RAW) + do_info(_PATH_PROCNET_RAW6,"AF INET6 (raw)",raw_do_one); +#endif + if (flags&NETSTAT_UNIX) { + printf("Active UNIX domain sockets "); + if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED)) + printf("(servers and established)"); + else { + if (flags&NETSTAT_LISTENING) + printf("(only servers)"); + else + printf("(w/o servers)"); + } + + printf("\nProto RefCnt Flags Type State I-Node Path\n"); + do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one); + } + return 0; +} diff --git a/busybox/networking/nslookup.c b/busybox/networking/nslookup.c new file mode 100644 index 000000000..936fa33cb --- /dev/null +++ b/busybox/networking/nslookup.c @@ -0,0 +1,206 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini nslookup implementation for busybox + * + * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * Correct default name server display and explicit name server option + * added by Ben Zeckel June 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#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. + */ + +/* only works for IPv4 */ +static int addr_fprint(char *addr) +{ + uint8_t split[4]; + uint32_t ip; + uint32_t *x = (uint32_t *) addr; + + ip = ntohl(*x); + split[0] = (ip & 0xff000000) >> 24; + split[1] = (ip & 0x00ff0000) >> 16; + split[2] = (ip & 0x0000ff00) >> 8; + split[3] = (ip & 0x000000ff); + printf("%d.%d.%d.%d", split[0], split[1], split[2], split[3]); + return 0; +} + +/* takes the NULL-terminated array h_addr_list, and + * prints its contents appropriately + */ +static int addr_list_fprint(char **h_addr_list) +{ + int i, j; + char *addr_string = (h_addr_list[1]) + ? "Addresses: " : "Address: "; + + printf("%s ", addr_string); + for (i = 0, j = 0; h_addr_list[i]; i++, j++) { + addr_fprint(h_addr_list[i]); + + /* real nslookup does this */ + if (j == 4) { + if (h_addr_list[i + 1]) { + printf("\n "); + } + j = 0; + } else { + if (h_addr_list[i + 1]) { + printf(", "); + } + } + + } + printf("\n"); + return 0; +} + +/* print the results as nslookup would */ +static struct hostent *hostent_fprint(struct hostent *host, const char *server_host) +{ + if (host) { + printf("%s %s\n", server_host, host->h_name); + addr_list_fprint(host->h_addr_list); + } else { + printf("*** Unknown host\n"); + } + return host; +} + +/* changes a c-string matching the perl regex \d+\.\d+\.\d+\.\d+ + * into a uint32_t + */ +static uint32_t str_to_addr(const char *addr) +{ + uint32_t split[4]; + uint32_t ip; + + sscanf(addr, "%d.%d.%d.%d", + &split[0], &split[1], &split[2], &split[3]); + + /* assuming sscanf worked */ + ip = (split[0] << 24) | + (split[1] << 16) | (split[2] << 8) | (split[3]); + + return htonl(ip); +} + +/* gethostbyaddr wrapper */ +static struct hostent *gethostbyaddr_wrapper(const char *address) +{ + struct in_addr addr; + + addr.s_addr = str_to_addr(address); + return gethostbyaddr((char *) &addr, 4, AF_INET); /* IPv4 only for now */ +} + +/* lookup the default nameserver and display it */ +static inline void server_print(void) +{ + struct sockaddr_in def = _res.nsaddr_list[0]; + char *ip = inet_ntoa(def.sin_addr); + + hostent_fprint(gethostbyaddr_wrapper(ip), "Server:"); + printf("\n"); +} + +/* alter the global _res nameserver structure to use + an explicit dns server instead of what is in /etc/resolv.h */ +static inline void set_default_dns(char *server) +{ + struct in_addr server_in_addr; + + if(inet_aton(server,&server_in_addr)) + { + _res.nscount = 1; + _res.nsaddr_list[0].sin_addr = server_in_addr; + } +} + +/* naive function to check whether char *s is an ip address */ +static int is_ip_address(const char *s) +{ + while (*s) { + if ((isdigit(*s)) || (*s == '.')) { + s++; + continue; + } + return 0; + } + return 1; +} + +/* ________________________________________________________________________ */ +int nslookup_main(int argc, char **argv) +{ + struct hostent *host; + + /* + * initialize DNS structure _res used in printing the default + * name server and in the explicit name server option feature. + */ + + res_init(); + + /* + * We allow 1 or 2 arguments. + * The first is the name to be looked up and the second is an + * optional DNS server with which to do the lookup. + * More than 3 arguments is an error to follow the pattern of the + * standard nslookup + */ + + if (argc < 2 || *argv[1]=='-' || argc > 3) + bb_show_usage(); + else if(argc == 3) + set_default_dns(argv[2]); + + server_print(); + if (is_ip_address(argv[1])) { + host = gethostbyaddr_wrapper(argv[1]); + } else { + host = xgethostbyname(argv[1]); + } + hostent_fprint(host, "Name: "); + if (host) { + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +/* $Id: nslookup.c,v 1.33 2004/10/13 07:25:01 andersen Exp $ */ diff --git a/busybox/networking/ping.c b/busybox/networking/ping.c new file mode 100644 index 000000000..50f3930ff --- /dev/null +++ b/busybox/networking/ping.c @@ -0,0 +1,472 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: ping.c,v 1.56 2004/03/15 08:28:48 andersen Exp $ + * Mini ping implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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" + + +static const int DEFDATALEN = 56; +static const int MAXIPLEN = 60; +static const int MAXICMPLEN = 76; +static const int MAXPACKET = 65468; +#define MAX_DUP_CHK (8 * 128) +static const int MAXWAIT = 10; +static const int PINGINTERVAL = 1; /* second */ + +#define O_QUIET (1 << 0) + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +static void ping(const char *host); + +/* common routines */ +static int in_cksum(unsigned short *buf, int sz) +{ + int nleft = sz; + int sum = 0; + unsigned short *w = buf; + unsigned short ans = 0; + + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + if (nleft == 1) { + *(unsigned char *) (&ans) = *(unsigned char *) w; + sum += ans; + } + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + ans = ~sum; + return (ans); +} + +/* simple version */ +#ifndef CONFIG_FEATURE_FANCY_PING +static char *hostname = NULL; +static void noresp(int ign) +{ + printf("No response from %s\n", hostname); + exit(EXIT_FAILURE); +} + +static void ping(const char *host) +{ + struct hostent *h; + struct sockaddr_in pingaddr; + struct icmp *pkt; + int pingsock, c; + char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; + + pingsock = create_icmp_socket(); + + memset(&pingaddr, 0, sizeof(struct sockaddr_in)); + + pingaddr.sin_family = AF_INET; + h = xgethostbyname(host); + memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); + hostname = h->h_name; + + pkt = (struct icmp *) packet; + memset(pkt, 0, sizeof(packet)); + pkt->icmp_type = ICMP_ECHO; + pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); + + c = sendto(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in)); + + if (c < 0 || c != sizeof(packet)) + bb_perror_msg_and_die("sendto"); + + signal(SIGALRM, noresp); + alarm(5); /* give the host 5000ms to respond */ + /* listen for replies */ + while (1) { + struct sockaddr_in from; + size_t fromlen = sizeof(from); + + if ((c = recvfrom(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &from, &fromlen)) < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("recvfrom"); + continue; + } + if (c >= 76) { /* ip + icmp */ + struct iphdr *iphdr = (struct iphdr *) packet; + + pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */ + if (pkt->icmp_type == ICMP_ECHOREPLY) + break; + } + } + printf("%s is alive!\n", hostname); + return; +} + +extern int ping_main(int argc, char **argv) +{ + argc--; + argv++; + if (argc < 1) + bb_show_usage(); + ping(*argv); + return EXIT_SUCCESS; +} + +#else /* ! CONFIG_FEATURE_FANCY_PING */ +/* full(er) version */ +static struct sockaddr_in pingaddr; +static int pingsock = -1; +static int datalen; /* intentionally uninitialized to work around gcc bug */ + +static long ntransmitted, nreceived, nrepeats, pingcount; +static int myid, options; +static unsigned long tmin = ULONG_MAX, tmax, tsum; +static char rcvd_tbl[MAX_DUP_CHK / 8]; + +struct hostent *hostent; + +static void sendping(int); +static void pingstats(int); +static void unpack(char *, int, struct sockaddr_in *); + +/**************************************************************************/ + +static void pingstats(int junk) +{ + int status; + + signal(SIGINT, SIG_IGN); + + printf("\n--- %s ping statistics ---\n", hostent->h_name); + printf("%ld packets transmitted, ", ntransmitted); + printf("%ld packets received, ", nreceived); + if (nrepeats) + printf("%ld duplicates, ", nrepeats); + if (ntransmitted) + printf("%ld%% packet loss\n", + (ntransmitted - nreceived) * 100 / ntransmitted); + if (nreceived) + printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n", + tmin / 10, tmin % 10, + (tsum / (nreceived + nrepeats)) / 10, + (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10); + if (nreceived != 0) + status = EXIT_SUCCESS; + else + status = EXIT_FAILURE; + exit(status); +} + +static void sendping(int junk) +{ + struct icmp *pkt; + int i; + char packet[datalen + 8]; + + pkt = (struct icmp *) packet; + + pkt->icmp_type = ICMP_ECHO; + pkt->icmp_code = 0; + pkt->icmp_cksum = 0; + pkt->icmp_seq = ntransmitted++; + pkt->icmp_id = myid; + CLR(pkt->icmp_seq % MAX_DUP_CHK); + + gettimeofday((struct timeval *) &packet[8], NULL); + pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); + + i = sendto(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in)); + + if (i < 0) + bb_perror_msg_and_die("sendto"); + else if ((size_t)i != sizeof(packet)) + bb_error_msg_and_die("ping wrote %d chars; %d expected", i, + (int)sizeof(packet)); + + signal(SIGALRM, sendping); + if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */ + alarm(PINGINTERVAL); + } else { /* done, wait for the last ping to come back */ + /* todo, don't necessarily need to wait so long... */ + signal(SIGALRM, pingstats); + alarm(MAXWAIT); + } +} + +static char *icmp_type_name (int id) +{ + switch (id) { + case ICMP_ECHOREPLY: return "Echo Reply"; + case ICMP_DEST_UNREACH: return "Destination Unreachable"; + case ICMP_SOURCE_QUENCH: return "Source Quench"; + case ICMP_REDIRECT: return "Redirect (change route)"; + case ICMP_ECHO: return "Echo Request"; + case ICMP_TIME_EXCEEDED: return "Time Exceeded"; + case ICMP_PARAMETERPROB: return "Parameter Problem"; + case ICMP_TIMESTAMP: return "Timestamp Request"; + case ICMP_TIMESTAMPREPLY: return "Timestamp Reply"; + case ICMP_INFO_REQUEST: return "Information Request"; + case ICMP_INFO_REPLY: return "Information Reply"; + case ICMP_ADDRESS: return "Address Mask Request"; + case ICMP_ADDRESSREPLY: return "Address Mask Reply"; + default: return "unknown ICMP type"; + } +} + +static void unpack(char *buf, int sz, struct sockaddr_in *from) +{ + struct icmp *icmppkt; + struct iphdr *iphdr; + struct timeval tv, *tp; + int hlen, dupflag; + unsigned long triptime; + + gettimeofday(&tv, NULL); + + /* check IP header */ + iphdr = (struct iphdr *) buf; + hlen = iphdr->ihl << 2; + /* discard if too short */ + if (sz < (datalen + ICMP_MINLEN)) + return; + + sz -= hlen; + icmppkt = (struct icmp *) (buf + hlen); + + if (icmppkt->icmp_id != myid) + return; /* not our ping */ + + if (icmppkt->icmp_type == ICMP_ECHOREPLY) { + ++nreceived; + tp = (struct timeval *) icmppkt->icmp_data; + + if ((tv.tv_usec -= tp->tv_usec) < 0) { + --tv.tv_sec; + tv.tv_usec += 1000000; + } + tv.tv_sec -= tp->tv_sec; + + triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100); + tsum += triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + + if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(icmppkt->icmp_seq % MAX_DUP_CHK); + dupflag = 0; + } + + if (options & O_QUIET) + return; + + printf("%d bytes from %s: icmp_seq=%u", sz, + inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), + icmppkt->icmp_seq); + printf(" ttl=%d", iphdr->ttl); + printf(" time=%lu.%lu ms", triptime / 10, triptime % 10); + if (dupflag) + printf(" (DUP!)"); + printf("\n"); + } else + if (icmppkt->icmp_type != ICMP_ECHO) + bb_error_msg("Warning: Got ICMP %d (%s)", + icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type)); +} + +static void ping(const char *host) +{ + char packet[datalen + MAXIPLEN + MAXICMPLEN]; + int sockopt; + + pingsock = create_icmp_socket(); + + memset(&pingaddr, 0, sizeof(struct sockaddr_in)); + + pingaddr.sin_family = AF_INET; + hostent = xgethostbyname(host); + if (hostent->h_addrtype != AF_INET) + bb_error_msg_and_die("unknown address type; only AF_INET is currently supported."); + + memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr)); + + /* enable broadcast pings */ + sockopt = 1; + setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt, + sizeof(sockopt)); + + /* set recv buf for broadcast pings */ + sockopt = 48 * 1024; + setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt, + sizeof(sockopt)); + + printf("PING %s (%s): %d data bytes\n", + hostent->h_name, + inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr), + datalen); + + signal(SIGINT, pingstats); + + /* start the ping's going ... */ + sendping(0); + + /* listen for replies */ + while (1) { + struct sockaddr_in from; + socklen_t fromlen = (socklen_t) sizeof(from); + int c; + + if ((c = recvfrom(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &from, &fromlen)) < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("recvfrom"); + continue; + } + unpack(packet, c, &from); + if (pingcount > 0 && nreceived >= pingcount) + break; + } + pingstats(0); +} + +extern int ping_main(int argc, char **argv) +{ + char *thisarg; + + datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */ + + argc--; + argv++; + options = 0; + /* Parse any options */ + while (argc >= 1 && **argv == '-') { + thisarg = *argv; + thisarg++; + switch (*thisarg) { + case 'q': + options |= O_QUIET; + break; + case 'c': + if (--argc <= 0) + bb_show_usage(); + argv++; + pingcount = atoi(*argv); + break; + case 's': + if (--argc <= 0) + bb_show_usage(); + argv++; + datalen = atoi(*argv); + break; + default: + bb_show_usage(); + } + argc--; + argv++; + } + if (argc < 1) + bb_show_usage(); + + myid = getpid() & 0xFFFF; + ping(*argv); + return EXIT_SUCCESS; +} +#endif /* ! CONFIG_FEATURE_FANCY_PING */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/busybox/networking/ping6.c b/busybox/networking/ping6.c new file mode 100644 index 000000000..72867f346 --- /dev/null +++ b/busybox/networking/ping6.c @@ -0,0 +1,515 @@ +/* vi: set sw=4 ts=4: */ +/* + * $Id: ping6.c,v 1.6 2004/03/15 08:28:48 andersen Exp $ + * Mini ping implementation for busybox + * + * Copyright (C) 1999 by Randolph Chung + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This version of ping is adapted from the ping in netkit-base 0.10, + * which is: + * + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Original copyright notice is retained at the end of this file. + * + * This version is an adaptation of ping.c from busybox. + * The code was modified by Bart Visscher + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* offsetof */ +#include "busybox.h" + +static const int DEFDATALEN = 56; +static const int MAXIPLEN = 60; +static const int MAXICMPLEN = 76; +static const int MAXPACKET = 65468; +#define MAX_DUP_CHK (8 * 128) +static const int MAXWAIT = 10; +static const int PINGINTERVAL = 1; /* second */ + +#define O_QUIET (1 << 0) +#define O_VERBOSE (1 << 1) + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +static void ping(const char *host); + +/* simple version */ +#ifndef CONFIG_FEATURE_FANCY_PING6 +static struct hostent *h; + +void noresp(int ign) +{ + printf("No response from %s\n", h->h_name); + exit(EXIT_FAILURE); +} + +static void ping(const char *host) +{ + struct sockaddr_in6 pingaddr; + struct icmp6_hdr *pkt; + int pingsock, c; + int sockopt; + char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; + + pingsock = create_icmp6_socket(); + + memset(&pingaddr, 0, sizeof(struct sockaddr_in)); + + pingaddr.sin6_family = AF_INET6; + h = xgethostbyname2(host, AF_INET6); + memcpy(&pingaddr.sin6_addr, h->h_addr, sizeof(pingaddr.sin6_addr)); + + pkt = (struct icmp6_hdr *) packet; + memset(pkt, 0, sizeof(packet)); + pkt->icmp6_type = ICMP6_ECHO_REQUEST; + + sockopt = offsetof(struct icmp6_hdr, icmp6_cksum); + setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt, + sizeof(sockopt)); + + c = sendto(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6)); + + if (c < 0 || c != sizeof(packet)) + bb_perror_msg_and_die("sendto"); + + signal(SIGALRM, noresp); + alarm(5); /* give the host 5000ms to respond */ + /* listen for replies */ + while (1) { + struct sockaddr_in6 from; + size_t fromlen = sizeof(from); + + if ((c = recvfrom(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &from, &fromlen)) < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("recvfrom"); + continue; + } + if (c >= 8) { /* icmp6_hdr */ + pkt = (struct icmp6_hdr *) packet; + if (pkt->icmp6_type == ICMP6_ECHO_REPLY) + break; + } + } + printf("%s is alive!\n", h->h_name); + return; +} + +extern int ping6_main(int argc, char **argv) +{ + argc--; + argv++; + if (argc < 1) + bb_show_usage(); + ping(*argv); + return EXIT_SUCCESS; +} + +#else /* ! CONFIG_FEATURE_FANCY_PING6 */ +/* full(er) version */ +static struct sockaddr_in6 pingaddr; +static int pingsock = -1; +static int datalen; /* intentionally uninitialized to work around gcc bug */ +static char* ifname; + +static long ntransmitted, nreceived, nrepeats, pingcount; +static int myid, options; +static unsigned long tmin = ULONG_MAX, tmax, tsum; +static char rcvd_tbl[MAX_DUP_CHK / 8]; + +# ifdef CONFIG_FEATURE_FANCY_PING +extern +# endif + struct hostent *hostent; + +static void sendping(int); +static void pingstats(int); +static void unpack(char *, int, struct sockaddr_in6 *, int); + +/**************************************************************************/ + +static void pingstats(int junk) +{ + int status; + + signal(SIGINT, SIG_IGN); + + printf("\n--- %s ping statistics ---\n", hostent->h_name); + printf("%ld packets transmitted, ", ntransmitted); + printf("%ld packets received, ", nreceived); + if (nrepeats) + printf("%ld duplicates, ", nrepeats); + if (ntransmitted) + printf("%ld%% packet loss\n", + (ntransmitted - nreceived) * 100 / ntransmitted); + if (nreceived) + printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n", + tmin / 10, tmin % 10, + (tsum / (nreceived + nrepeats)) / 10, + (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10); + if (nreceived != 0) + status = EXIT_SUCCESS; + else + status = EXIT_FAILURE; + exit(status); +} + +static void sendping(int junk) +{ + struct icmp6_hdr *pkt; + int i; + char packet[datalen + 8]; + + pkt = (struct icmp6_hdr *) packet; + + pkt->icmp6_type = ICMP6_ECHO_REQUEST; + pkt->icmp6_code = 0; + pkt->icmp6_cksum = 0; + pkt->icmp6_seq = ntransmitted++; + pkt->icmp6_id = myid; + CLR(pkt->icmp6_seq % MAX_DUP_CHK); + + gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL); + + i = sendto(pingsock, packet, sizeof(packet), 0, + (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6)); + + if (i < 0) + bb_perror_msg_and_die("sendto"); + else if ((size_t)i != sizeof(packet)) + bb_error_msg_and_die("ping wrote %d chars; %d expected", i, + (int)sizeof(packet)); + + signal(SIGALRM, sendping); + if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */ + alarm(PINGINTERVAL); + } else { /* done, wait for the last ping to come back */ + /* todo, don't necessarily need to wait so long... */ + signal(SIGALRM, pingstats); + alarm(MAXWAIT); + } +} + +static char *icmp6_type_name (int id) +{ + switch (id) { + case ICMP6_DST_UNREACH: return "Destination Unreachable"; + case ICMP6_PACKET_TOO_BIG: return "Packet too big"; + case ICMP6_TIME_EXCEEDED: return "Time Exceeded"; + case ICMP6_PARAM_PROB: return "Parameter Problem"; + case ICMP6_ECHO_REPLY: return "Echo Reply"; + case ICMP6_ECHO_REQUEST: return "Echo Request"; + case ICMP6_MEMBERSHIP_QUERY: return "Membership Query"; + case ICMP6_MEMBERSHIP_REPORT: return "Membership Report"; + case ICMP6_MEMBERSHIP_REDUCTION: return "Membership Reduction"; + default: return "unknown ICMP type"; + } +} + +static void unpack(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit) +{ + struct icmp6_hdr *icmppkt; + struct timeval tv, *tp; + int dupflag; + unsigned long triptime; + char buf[INET6_ADDRSTRLEN]; + + gettimeofday(&tv, NULL); + + /* discard if too short */ + if (sz < (datalen + sizeof(struct icmp6_hdr))) + return; + + icmppkt = (struct icmp6_hdr *) packet; + + if (icmppkt->icmp6_id != myid) + return; /* not our ping */ + + if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) { + ++nreceived; + tp = (struct timeval *) &icmppkt->icmp6_data8[4]; + + if ((tv.tv_usec -= tp->tv_usec) < 0) { + --tv.tv_sec; + tv.tv_usec += 1000000; + } + tv.tv_sec -= tp->tv_sec; + + triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100); + tsum += triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + + if (TST(icmppkt->icmp6_seq % MAX_DUP_CHK)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(icmppkt->icmp6_seq % MAX_DUP_CHK); + dupflag = 0; + } + + if (options & O_QUIET) + return; + + printf("%d bytes from %s: icmp6_seq=%u", sz, + inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr, + buf, sizeof(buf)), + icmppkt->icmp6_seq); + printf(" ttl=%d time=%lu.%lu ms", hoplimit, + triptime / 10, triptime % 10); + if (dupflag) + printf(" (DUP!)"); + printf("\n"); + } else + if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) + bb_error_msg("Warning: Got ICMP %d (%s)", + icmppkt->icmp6_type, icmp6_type_name (icmppkt->icmp6_type)); +} + +static void ping(const char *host) +{ + char packet[datalen + MAXIPLEN + MAXICMPLEN]; + char buf[INET6_ADDRSTRLEN]; + int sockopt; + struct msghdr msg; + struct sockaddr_in6 from; + struct iovec iov; + char control_buf[CMSG_SPACE(36)]; + + pingsock = create_icmp6_socket(); + + memset(&pingaddr, 0, sizeof(struct sockaddr_in)); + + pingaddr.sin6_family = AF_INET6; + hostent = xgethostbyname2(host, AF_INET6); + if (hostent->h_addrtype != AF_INET6) + bb_error_msg_and_die("unknown address type; only AF_INET6 is currently supported."); + + memcpy(&pingaddr.sin6_addr, hostent->h_addr, sizeof(pingaddr.sin6_addr)); + +#ifdef ICMP6_FILTER + { + struct icmp6_filter filt; + if (!(options & O_VERBOSE)) { + ICMP6_FILTER_SETBLOCKALL(&filt); +#if 0 + if ((options & F_FQDN) || (options & F_FQDNOLD) || + (options & F_NODEADDR) || (options & F_SUPTYPES)) + ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt); + else +#endif + ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); + } else { + ICMP6_FILTER_SETPASSALL(&filt); + } + if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + sizeof(filt)) < 0) + bb_error_msg_and_die("setsockopt(ICMP6_FILTER)"); + } +#endif /*ICMP6_FILTER*/ + + /* enable broadcast pings */ + sockopt = 1; + setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt, + sizeof(sockopt)); + + /* set recv buf for broadcast pings */ + sockopt = 48 * 1024; + setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt, + sizeof(sockopt)); + + sockopt = offsetof(struct icmp6_hdr, icmp6_cksum); + setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt, + sizeof(sockopt)); + + sockopt = 1; + setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, (char *) &sockopt, + sizeof(sockopt)); + + if (ifname) { + if ((pingaddr.sin6_scope_id = if_nametoindex(ifname)) == 0) + bb_error_msg_and_die("%s: invalid interface name", ifname); + } + + printf("PING %s (%s): %d data bytes\n", + hostent->h_name, + inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr, + buf, sizeof(buf)), + datalen); + + signal(SIGINT, pingstats); + + /* start the ping's going ... */ + sendping(0); + + /* listen for replies */ + msg.msg_name=&from; + msg.msg_namelen=sizeof(from); + msg.msg_iov=&iov; + msg.msg_iovlen=1; + msg.msg_control=control_buf; + iov.iov_base=packet; + iov.iov_len=sizeof(packet); + while (1) { + int c; + struct cmsghdr *cmsgptr = NULL; + int hoplimit=-1; + msg.msg_controllen=sizeof(control_buf); + + if ((c = recvmsg(pingsock, &msg, 0)) < 0) { + if (errno == EINTR) + continue; + bb_perror_msg("recvfrom"); + continue; + } + for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { + if (cmsgptr->cmsg_level == SOL_IPV6 && + cmsgptr->cmsg_type == IPV6_HOPLIMIT ) { + hoplimit=*(int*)CMSG_DATA(cmsgptr); + } + } + unpack(packet, c, &from, hoplimit); + if (pingcount > 0 && nreceived >= pingcount) + break; + } + pingstats(0); +} + +extern int ping6_main(int argc, char **argv) +{ + char *thisarg; + + datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */ + + argc--; + argv++; + options = 0; + /* Parse any options */ + while (argc >= 1 && **argv == '-') { + thisarg = *argv; + thisarg++; + switch (*thisarg) { + case 'v': + options &= ~O_QUIET; + options |= O_VERBOSE; + break; + case 'q': + options &= ~O_VERBOSE; + options |= O_QUIET; + break; + case 'c': + if (--argc <= 0) + bb_show_usage(); + argv++; + pingcount = atoi(*argv); + break; + case 's': + if (--argc <= 0) + bb_show_usage(); + argv++; + datalen = atoi(*argv); + break; + case 'I': + if (--argc <= 0) + bb_show_usage(); + argv++; + ifname = *argv; + break; + default: + bb_show_usage(); + } + argc--; + argv++; + } + if (argc < 1) + bb_show_usage(); + + myid = getpid() & 0xFFFF; + ping(*argv); + return EXIT_SUCCESS; +} +#endif /* ! CONFIG_FEATURE_FANCY_PING6 */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * 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..5c092f452 --- /dev/null +++ b/busybox/networking/route.c @@ -0,0 +1,714 @@ +/* route + * + * Similar to the standard Unix route, but with only the necessary + * parts for AF_INET and AF_INET6 + * + * Bjorn Wesen, Axis Communications AB + * + * Author of the original route: + * Fred N. van Kempen, + * (derived from FvK's 'route.c 1.70 01/04/94') + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * $Id: route.c,v 1.26 2004/03/19 23:27:08 mjn3 Exp $ + * + * displayroute() code added by Vladimir N. Oleynik + * adjustments by Larry Doolittle + * + * IPV6 support added by Bart Visscher + */ + +/* 2004/03/09 Manuel Novoa III + * + * Rewritten to fix several bugs, add additional error checking, and + * remove ridiculous amounts of bloat. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "inet_common.h" + +#ifndef RTF_UP +/* Keep this in sync with /usr/src/linux/include/linux/route.h */ +#define RTF_UP 0x0001 /* route usable */ +#define RTF_GATEWAY 0x0002 /* destination is a gateway */ +#define RTF_HOST 0x0004 /* host entry (net otherwise) */ +#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */ +#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */ +#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */ +#define RTF_MTU 0x0040 /* specific MTU for this route */ +#ifndef RTF_MSS +#define RTF_MSS RTF_MTU /* Compatibility :-( */ +#endif +#define RTF_WINDOW 0x0080 /* per route window clamping */ +#define RTF_IRTT 0x0100 /* Initial round trip time */ +#define RTF_REJECT 0x0200 /* Reject route */ +#endif + +#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */ +#define HAVE_NEW_ADDRT 1 +#endif + +#if HAVE_NEW_ADDRT +#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr) +#define full_mask(x) (x) +#else +#define mask_in_addr(x) ((x).rt_genmask) +#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr) +#endif + +/* The RTACTION entries must agree with tbl_verb[] below! */ +#define RTACTION_ADD 1 +#define RTACTION_DEL 2 + +/* For the various tbl_*[] arrays, the 1st byte is the offset to + * the next entry and the 2nd byte is return value. */ + +#define NET_FLAG 1 +#define HOST_FLAG 2 + +/* We remap '-' to '#' to avoid problems with getopt. */ +static const char tbl_hash_net_host[] = + "\007\001#net\0" +/* "\010\002#host\0" */ + "\007\002#host" /* Since last, we can save a byte. */ +; + +#define KW_TAKES_ARG 020 +#define KW_SETS_FLAG 040 + +#define KW_IPVx_METRIC 020 +#define KW_IPVx_NETMASK 021 +#define KW_IPVx_GATEWAY 022 +#define KW_IPVx_MSS 023 +#define KW_IPVx_WINDOW 024 +#define KW_IPVx_IRTT 025 +#define KW_IPVx_DEVICE 026 + +#define KW_IPVx_FLAG_ONLY 040 +#define KW_IPVx_REJECT 040 +#define KW_IPVx_MOD 041 +#define KW_IPVx_DYN 042 +#define KW_IPVx_REINSTATE 043 + +static const char tbl_ipvx[] = + /* 020 is the "takes an arg" bit */ +#if HAVE_NEW_ADDRT + "\011\020metric\0" +#endif + "\012\021netmask\0" + "\005\022gw\0" + "\012\022gateway\0" + "\006\023mss\0" + "\011\024window\0" +#ifdef RTF_IRTT + "\007\025irtt\0" +#endif + "\006\026dev\0" + "\011\026device\0" + /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */ +#ifdef RTF_REJECT + "\011\040reject\0" +#endif + "\006\041mod\0" + "\006\042dyn\0" +/* "\014\043reinstate\0" */ + "\013\043reinstate" /* Since last, we can save a byte. */ +; + +static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */ +#ifdef RTF_REJECT + RTF_REJECT, +#endif + RTF_MODIFIED, + RTF_DYNAMIC, + RTF_REINSTATE +}; + +static int kw_lookup(const char *kwtbl, char ***pargs) +{ + if (**pargs) { + do { + if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */ + *pargs += 1; + if (kwtbl[1] & KW_TAKES_ARG) { + if (!**pargs) { /* No more args! */ + bb_show_usage(); + } + *pargs += 1; /* Calling routine will use args[-1]. */ + } + return kwtbl[1]; + } + kwtbl += *kwtbl; + } while (*kwtbl); + } + return 0; +} + +/* Add or delete a route, depending on action. */ + +static void INET_setroute(int action, char **args) +{ + struct rtentry rt; + const char *netmask; + int skfd, isnet, xflag; + + assert((action == RTACTION_ADD) || (action == RTACTION_DEL)); + + /* Grab the -net or -host options. Remember they were transformed. */ + xflag = kw_lookup(tbl_hash_net_host, &args); + + /* If we did grab -net or -host, make sure we still have an arg left. */ + if (*args == NULL) { + bb_show_usage(); + } + + /* Clean out the RTREQ structure. */ + memset((char *) &rt, 0, sizeof(struct rtentry)); + + { + const char *target = *args++; + + /* Prefer hostname lookup is -host flag (xflag==1) was given. */ + isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst, + (xflag & HOST_FLAG)); + if (isnet < 0) { + bb_error_msg_and_die("resolving %s", target); + } + + } + + if (xflag) { /* Reinit isnet if -net or -host was specified. */ + isnet = (xflag & NET_FLAG); + } + + /* Fill in the other fields. */ + rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST)); + + netmask = bb_INET_default; + + while (*args) { + int k = kw_lookup(tbl_ipvx, &args); + const char *args_m1 = args[-1]; + + if (k & KW_IPVx_FLAG_ONLY) { + rt.rt_flags |= flags_ipvx[k & 3]; + continue; + } + +#if HAVE_NEW_ADDRT + if (k == KW_IPVx_METRIC) { + rt.rt_metric = bb_xgetularg10(args_m1) + 1; + continue; + } +#endif + + if (k == KW_IPVx_NETMASK) { + struct sockaddr mask; + + if (mask_in_addr(rt)) { + bb_show_usage(); + } + + netmask = args_m1; + isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0); + if (isnet < 0) { + bb_error_msg_and_die("resolving %s", netmask); + } + rt.rt_genmask = full_mask(mask); + continue; + } + + if (k == KW_IPVx_GATEWAY) { + if (rt.rt_flags & RTF_GATEWAY) { + bb_show_usage(); + } + + isnet = INET_resolve(args_m1, + (struct sockaddr_in *) &rt.rt_gateway, 1); + rt.rt_flags |= RTF_GATEWAY; + + if (isnet) { + if (isnet < 0) { + bb_error_msg_and_die("resolving %s", args_m1); + } + bb_error_msg_and_die("gateway %s is a NETWORK", args_m1); + } + continue; + } + + if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */ + rt.rt_flags |= RTF_MSS; + rt.rt_mss = bb_xgetularg10_bnd(args_m1, 64, 32768); + continue; + } + + if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */ + rt.rt_flags |= RTF_WINDOW; + rt.rt_window = bb_xgetularg10_bnd(args_m1, 128, INT_MAX); + continue; + } + +#ifdef RTF_IRTT + if (k == KW_IPVx_IRTT) { + rt.rt_flags |= RTF_IRTT; + rt.rt_irtt = bb_xgetularg10(args_m1); + rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */ +#if 0 /* FIXME: do we need to check anything of this? */ + if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) { + bb_error_msg_and_die("bad irtt"); + } +#endif + continue; + } +#endif + + /* Device is special in that it can be the last arg specified + * and doesn't requre the dev/device keyword in that case. */ + if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { + /* Don't use args_m1 here since args may have changed! */ + rt.rt_dev = args[-1]; + continue; + } + + /* Nothing matched. */ + bb_show_usage(); + } + +#ifdef RTF_REJECT + if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) { + rt.rt_dev = "lo"; + } +#endif + + /* sanity checks.. */ + if (mask_in_addr(rt)) { + unsigned long mask = mask_in_addr(rt); + + mask = ~ntohl(mask); + if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) { + bb_error_msg_and_die("netmask %.8x and host route conflict", + (unsigned int) mask); + } + if (mask & (mask + 1)) { + bb_error_msg_and_die("bogus netmask %s", netmask); + } + mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr; + if (mask & ~mask_in_addr(rt)) { + bb_error_msg_and_die("netmask and route address conflict"); + } + } + + /* Fill out netmask if still unset */ + if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) { + mask_in_addr(rt) = 0xffffffff; + } + + /* Create a socket to the INET kernel. */ + if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg_and_die("socket"); + } + + if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) { + bb_perror_msg_and_die("SIOC[ADD|DEL]RT"); + } + + /* Don't bother closing, as we're exiting after we return anyway. */ + /* close(skfd); */ +} + +#ifdef CONFIG_FEATURE_IPV6 + +static void INET6_setroute(int action, char **args) +{ + struct sockaddr_in6 sa6; + struct in6_rtmsg rt; + int prefix_len, skfd; + const char *devname; + + assert((action == RTACTION_ADD) || (action == RTACTION_DEL)); + + { + /* We know args isn't NULL from the check in route_main. */ + const char *target = *args++; + + if (strcmp(target, "default") == 0) { + prefix_len = 0; + memset(&sa6, 0, sizeof(sa6)); + } else { + char *cp; + if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */ + *cp = 0; + prefix_len = bb_xgetularg10_bnd(cp+1, 0, 128); + } else { + prefix_len = 128; + } + if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) { + bb_error_msg_and_die("resolving %s", target); + } + } + } + + /* Clean out the RTREQ structure. */ + memset((char *) &rt, 0, sizeof(struct in6_rtmsg)); + + memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); + + /* Fill in the other fields. */ + rt.rtmsg_dst_len = prefix_len; + rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP); + rt.rtmsg_metric = 1; + + devname = NULL; + + while (*args) { + int k = kw_lookup(tbl_ipvx, &args); + const char *args_m1 = args[-1]; + + if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) { + rt.rtmsg_flags |= flags_ipvx[k & 3]; + continue; + } + + if (k == KW_IPVx_METRIC) { + rt.rtmsg_metric = bb_xgetularg10(args_m1); + continue; + } + + if (k == KW_IPVx_GATEWAY) { + if (rt.rtmsg_flags & RTF_GATEWAY) { + bb_show_usage(); + } + + if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) { + bb_error_msg_and_die("resolving %s", args_m1); + } + memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr, + sizeof(struct in6_addr)); + rt.rtmsg_flags |= RTF_GATEWAY; + continue; + } + + /* Device is special in that it can be the last arg specified + * and doesn't requre the dev/device keyword in that case. */ + if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) { + /* Don't use args_m1 here since args may have changed! */ + devname = args[-1]; + continue; + } + + /* Nothing matched. */ + bb_show_usage(); + } + + /* Create a socket to the INET6 kernel. */ + if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg_and_die("socket"); + } + + rt.rtmsg_ifindex = 0; + + if (devname) { + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname); + + if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) { + bb_perror_msg_and_die("SIOGIFINDEX"); + } + rt.rtmsg_ifindex = ifr.ifr_ifindex; + } + + /* Tell the kernel to accept this route. */ + if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) { + bb_perror_msg_and_die("SIOC[ADD|DEL]RT"); + } + + /* Don't bother closing, as we're exiting after we return anyway. */ + /* close(skfd); */ +} +#endif + +static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */ + RTF_GATEWAY, + RTF_HOST, + RTF_REINSTATE, + RTF_DYNAMIC, + RTF_MODIFIED, +#ifdef CONFIG_FEATURE_IPV6 + RTF_DEFAULT, + RTF_ADDRCONF, + RTF_CACHE +#endif +}; + +#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED) +#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE) + +static const char flagchars[] = /* Must agree with flagvals[]. */ + "GHRDM" +#ifdef CONFIG_FEATURE_IPV6 + "DAC" +#endif +; + +static +#ifndef CONFIG_FEATURE_IPV6 +__inline +#endif +void set_flags(char *flagstr, int flags) +{ + int i; + + *flagstr++ = 'U'; + + for (i=0 ; (*flagstr = flagchars[i]) != 0 ; i++) { + if (flags & flagvals[i]) { + ++flagstr; + } + } +} + +void displayroutes(int noresolve, int netstatfmt) +{ + char devname[64], flags[16], sdest[16], sgw[16]; + unsigned long int d, g, m; + int flgs, ref, use, metric, mtu, win, ir; + struct sockaddr_in s_addr; + struct in_addr mask; + + FILE *fp = bb_xfopen("/proc/net/route", "r"); + + bb_printf("Kernel IP routing table\n" + "Destination Gateway Genmask" + " Flags %s Iface\n", + netstatfmt ? " MSS Window irtt" : "Metric Ref Use"); + + if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */ + goto ERROR; /* Empty or missing line, or read error. */ + } + while (1) { + int r; + r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", + devname, &d, &g, &flgs, &ref, &use, &metric, &m, + &mtu, &win, &ir); + if (r != 11) { + if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ + break; + } + ERROR: + bb_error_msg_and_die("fscanf"); + } + + if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */ + continue; + } + + set_flags(flags, (flgs & IPV4_MASK)); +#ifdef RTF_REJECT + if (flgs & RTF_REJECT) { + flags[0] = '!'; + } +#endif + + memset(&s_addr, 0, sizeof(struct sockaddr_in)); + s_addr.sin_family = AF_INET; + s_addr.sin_addr.s_addr = d; + INET_rresolve(sdest, sizeof(sdest), &s_addr, + (noresolve | 0x8000), m); /* Default instead of *. */ + + s_addr.sin_addr.s_addr = g; + INET_rresolve(sgw, sizeof(sgw), &s_addr, + (noresolve | 0x4000), m); /* Host instead of net. */ + + mask.s_addr = m; + bb_printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags); + if (netstatfmt) { + bb_printf("%5d %-5d %6d %s\n", mtu, win, ir, devname); + } else { + bb_printf("%-6d %-2d %7d %s\n", metric, ref, use, devname); + } + } +} + +#ifdef CONFIG_FEATURE_IPV6 + +static void INET6_displayroutes(int noresolve) +{ + char addr6[128], naddr6[128]; + /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses. + * We read the non-delimited strings into the tail of the buffer + * using fscanf and then modify the buffer by shifting forward + * while inserting ':'s and the nul terminator for the first string. + * Hence the strings are at addr6x and addr6x+40. This generates + * _much_ less code than the previous (upstream) approach. */ + char addr6x[80]; + char iface[16], flags[16]; + int iflags, metric, refcnt, use, prefix_len, slen; + struct sockaddr_in6 snaddr6; + + FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r"); + + bb_printf("Kernel IPv6 routing table\n%-44s%-40s" + "Flags Metric Ref Use Iface\n", + "Destination", "Next Hop"); + + while (1) { + int r; + r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n", + addr6x+14, &prefix_len, &slen, addr6x+40+7, + &metric, &use, &refcnt, &iflags, iface); + if (r != 9) { + if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */ + break; + } + ERROR: + bb_error_msg_and_die("fscanf"); + } + + /* Do the addr6x shift-and-insert changes to ':'-delimit addresses. + * For now, always do this to validate the proc route format, even + * if the interface is down. */ + { + int i = 0; + char *p = addr6x+14; + + do { + if (!*p) { + if (i==40) { /* nul terminator for 1st address? */ + addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */ + ++p; /* Skip and continue. */ + continue; + } + goto ERROR; + } + addr6x[i++] = *p++; + if (!((i+1)%5)) { + addr6x[i++] = ':'; + } + } while (i < 40+28+7); + } + + if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */ + continue; + } + + set_flags(flags, (iflags & IPV6_MASK)); + + r = 0; + do { + inet_pton(AF_INET6, addr6x + r, + (struct sockaddr *) &snaddr6.sin6_addr); + snaddr6.sin6_family = AF_INET6; + INET6_rresolve(naddr6, sizeof(naddr6), + (struct sockaddr_in6 *) &snaddr6, +#if 0 + (noresolve | 0x8000) /* Default instead of *. */ +#else + 0x0fff /* Apparently, upstream never resolves. */ +#endif + ); + + if (!r) { /* 1st pass */ + snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len); + r += 40; + } else { /* 2nd pass */ + /* Print the info. */ + bb_printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n", + addr6, naddr6, flags, metric, refcnt, use, iface); + break; + } + } while (1); + } +} + +#endif + +#define ROUTE_OPT_A 0x01 +#define ROUTE_OPT_n 0x02 +#define ROUTE_OPT_e 0x04 +#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */ + +/* 1st byte is offset to next entry offset. 2nd byte is return value. */ +static const char tbl_verb[] = /* 2nd byte matches RTACTION_* code */ + "\006\001add\0" + "\006\002del\0" +/* "\011\002delete\0" */ + "\010\002delete" /* Since last, we can save a byte. */ +; + +int route_main(int argc, char **argv) +{ + unsigned long opt; + int what; + char *family; + + /* First, remap '-net' and '-host' to avoid getopt problems. */ + { + char **p = argv; + + while (*++p) { + if ((strcmp(*p, "-net") == 0) || (strcmp(*p, "-host") == 0)) { + p[0][0] = '#'; + } + } + } + + opt = bb_getopt_ulflags(argc, argv, "A:ne", &family); + + if ((opt & ROUTE_OPT_A) && strcmp(family, "inet")) { +#ifdef CONFIG_FEATURE_IPV6 + if (strcmp(family, "inet6") == 0) { + opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */ + } else +#endif + bb_show_usage(); + } + + argv += optind; + + /* No more args means display the routing table. */ + if (!*argv) { + int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0; +#ifdef CONFIG_FEATURE_IPV6 + if (opt & ROUTE_OPT_INET6) + INET6_displayroutes(noresolve); + else +#endif + displayroutes(noresolve, opt & ROUTE_OPT_e); + + bb_xferror_stdout(); + bb_fflush_stdout_and_exit(EXIT_SUCCESS); + } + + /* Check verb. At the moment, must be add, del, or delete. */ + what = kw_lookup(tbl_verb, &argv); + if (!what || !*argv) { /* Unknown verb or no more args. */ + bb_show_usage(); + } + +#ifdef CONFIG_FEATURE_IPV6 + if (opt & ROUTE_OPT_INET6) + INET6_setroute(what, argv); + else +#endif + INET_setroute(what, argv); + + return EXIT_SUCCESS; +} diff --git a/busybox/networking/telnet.c b/busybox/networking/telnet.c new file mode 100644 index 000000000..6ad7712ab --- /dev/null +++ b/busybox/networking/telnet.c @@ -0,0 +1,769 @@ +/* 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 + * + * Modified 2004/02/11 to add ability to pass the USER variable to remote host + * by Fernando Silveira + * + */ + +#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) +#endif + +static inline void iacflush(void) +{ + write(G.netfd, G.iacbuf, G.iaclen); + G.iaclen = 0; +} + +/* Function prototypes */ +static void rawmode(void); +static void cookmode(void); +static void do_linemode(void); +static void will_charmode(void); +static void telopt(byte c); +static int subneg(byte c); + +/* Some globals */ +static int one = 1; + +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static char *ttype; +#endif + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static const char *autologin; +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static int win_width, win_height; +#endif + +static void doexit(int ev) +{ + cookmode(); + exit(ev); +} + +static void conescape(void) +{ + char b; + + if (G.gotsig) /* came from line mode... go raw */ + rawmode(); + + WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n" + " l go to line mode\r\n" + " c go to character mode\r\n" + " z suspend telnet\r\n" + " e exit telnet\r\n"); + + if (read(0, &b, 1) <= 0) + doexit(1); + + switch (b) + { + case 'l': + if (!G.gotsig) + { + do_linemode(); + goto rrturn; + } + break; + case 'c': + if (G.gotsig) + { + will_charmode(); + goto rrturn; + } + break; + case 'z': + cookmode(); + kill(0, SIGTSTP); + rawmode(); + break; + case 'e': + doexit(0); + } + + WriteCS(1, "continuing...\r\n"); + + if (G.gotsig) + cookmode(); + + rrturn: + G.gotsig = 0; + +} +static void handlenetoutput(int len) +{ + /* here we could do smart tricks how to handle 0xFF:s in output + * stream like writing twice every sequence of FF:s (thus doing + * many write()s. But I think interactive telnet application does + * not need to be 100% 8-bit clean, so changing every 0xff:s to + * 0x7f:s + * + * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com) + * I don't agree. + * first - I cannot use programs like sz/rz + * second - the 0x0D is sent as one character and if the next + * char is 0x0A then it's eaten by a server side. + * third - whay doy you have to make 'many write()s'? + * I don't understand. + * So I implemented it. It's realy useful for me. I hope that + * others people will find it interesting to. + */ + + int i, j; + byte * p = G.buf; + byte outbuf[4*DATABUFSIZE]; + + for (i = len, j = 0; i > 0; i--, p++) + { + if (*p == 0x1d) + { + conescape(); + return; + } + outbuf[j++] = *p; + if (*p == 0xff) + outbuf[j++] = 0xff; + else if (*p == 0x0d) + outbuf[j++] = 0x00; + } + if (j > 0 ) + write(G.netfd, outbuf, j); +} + + +static void handlenetinput(int len) +{ + int i; + int cstart = 0; + + for (i = 0; i < len; i++) + { + byte c = G.buf[i]; + + if (G.telstate == 0) /* most of the time state == 0 */ + { + if (c == IAC) + { + cstart = i; + G.telstate = TS_IAC; + } + } + else + switch (G.telstate) + { + case TS_0: + if (c == IAC) + G.telstate = TS_IAC; + else + G.buf[cstart++] = c; + break; + + case TS_IAC: + if (c == IAC) /* IAC IAC -> 0xFF */ + { + G.buf[cstart++] = c; + G.telstate = TS_0; + break; + } + /* else */ + switch (c) + { + case SB: + G.telstate = TS_SUB1; + break; + case DO: + case DONT: + case WILL: + case WONT: + G.telwish = c; + G.telstate = TS_OPT; + break; + default: + G.telstate = TS_0; /* DATA MARK must be added later */ + } + break; + case TS_OPT: /* WILL, WONT, DO, DONT */ + telopt(c); + G.telstate = TS_0; + break; + case TS_SUB1: /* Subnegotiation */ + case TS_SUB2: /* Subnegotiation */ + if (subneg(c)) + G.telstate = TS_0; + break; + } + } + if (G.telstate) + { + if (G.iaclen) iacflush(); + if (G.telstate == TS_0) G.telstate = 0; + + len = cstart; + } + + if (len) + write(1, G.buf, len); +} + + +/* ******************************* */ + +static inline void putiac(int c) +{ + G.iacbuf[G.iaclen++] = c; +} + + +static void putiac2(byte wwdd, byte c) +{ + if (G.iaclen + 3 > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(wwdd); + putiac(c); +} + +#if 0 +static void putiac1(byte c) +{ + if (G.iaclen + 2 > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(c); +} +#endif + +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static void putiac_subopt(byte c, char *str) +{ + int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 ) + + if (G.iaclen + len > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(SB); + putiac(c); + putiac(0); + + while(*str) + putiac(*str++); + + putiac(IAC); + putiac(SE); +} +#endif + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static void putiac_subopt_autologin(void) +{ + int len = strlen(autologin) + 6; // (2 + 1 + 1 + strlen + 2) + char *user = "USER"; + + if (G.iaclen + len > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(SB); + putiac(TELOPT_NEW_ENVIRON); + putiac(TELQUAL_IS); + putiac(NEW_ENV_VAR); + + while(*user) + putiac(*user++); + + putiac(NEW_ENV_VALUE); + + while(*autologin) + putiac(*autologin++); + + putiac(IAC); + putiac(SE); +} +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static void putiac_naws(byte c, int x, int y) +{ + if (G.iaclen + 9 > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(SB); + putiac(c); + + putiac((x >> 8) & 0xff); + putiac(x & 0xff); + putiac((y >> 8) & 0xff); + putiac(y & 0xff); + + putiac(IAC); + putiac(SE); +} +#endif + +/* void putiacstring (subneg strings) */ + +/* ******************************* */ + +static char const escapecharis[] = "\r\nEscape character is "; + +static void setConMode(void) +{ + if (G.telflags & UF_ECHO) + { + if (G.charmode == CHM_TRY) { + G.charmode = CHM_ON; + printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis); + rawmode(); + } + } + else + { + if (G.charmode != CHM_OFF) { + G.charmode = CHM_OFF; + printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis); + cookmode(); + } + } +} + +/* ******************************* */ + +static void will_charmode(void) +{ + G.charmode = CHM_TRY; + G.telflags |= (UF_ECHO | UF_SGA); + setConMode(); + + putiac2(DO, TELOPT_ECHO); + putiac2(DO, TELOPT_SGA); + iacflush(); +} + +static void do_linemode(void) +{ + G.charmode = CHM_TRY; + G.telflags &= ~(UF_ECHO | UF_SGA); + setConMode(); + + putiac2(DONT, TELOPT_ECHO); + putiac2(DONT, TELOPT_SGA); + iacflush(); +} + +/* ******************************* */ + +static inline void to_notsup(char c) +{ + if (G.telwish == WILL) putiac2(DONT, c); + else if (G.telwish == DO) putiac2(WONT, c); +} + +static inline void to_echo(void) +{ + /* if server requests ECHO, don't agree */ + if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; } + else if (G.telwish == DONT) return; + + if (G.telflags & UF_ECHO) + { + if (G.telwish == WILL) + return; + } + else + if (G.telwish == WONT) + return; + + if (G.charmode != CHM_OFF) + G.telflags ^= UF_ECHO; + + if (G.telflags & UF_ECHO) + putiac2(DO, TELOPT_ECHO); + else + putiac2(DONT, TELOPT_ECHO); + + setConMode(); + WriteCS(1, "\r\n"); /* sudden modec */ +} + +static inline void to_sga(void) +{ + /* daemon always sends will/wont, client do/dont */ + + if (G.telflags & UF_SGA) + { + if (G.telwish == WILL) + return; + } + else + if (G.telwish == WONT) + return; + + if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */ + putiac2(DO, TELOPT_SGA); + else + putiac2(DONT, TELOPT_SGA); + + return; +} + +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static inline void to_ttype(void) +{ + /* Tell server we will (or won't) do TTYPE */ + + if(ttype) + putiac2(WILL, TELOPT_TTYPE); + else + putiac2(WONT, TELOPT_TTYPE); + + return; +} +#endif + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static inline void to_new_environ(void) +{ + /* Tell server we will (or will not) do AUTOLOGIN */ + + if (autologin) + putiac2(WILL, TELOPT_NEW_ENVIRON); + else + putiac2(WONT, TELOPT_NEW_ENVIRON); + + return; +} +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static inline void to_naws(void) +{ + /* Tell server we will do NAWS */ + putiac2(WILL, TELOPT_NAWS); + return; +} +#endif + +static void telopt(byte c) +{ + switch (c) + { + case TELOPT_ECHO: to_echo(); break; + case TELOPT_SGA: to_sga(); break; +#ifdef CONFIG_FEATURE_TELNET_TTYPE + case TELOPT_TTYPE: to_ttype();break; +#endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + case TELOPT_NEW_ENVIRON: to_new_environ(); break; +#endif +#ifdef CONFIG_FEATURE_AUTOWIDTH + case TELOPT_NAWS: to_naws(); + putiac_naws(c, win_width, win_height); + break; +#endif + default: to_notsup(c); + break; + } +} + + +/* ******************************* */ + +/* subnegotiation -- ignore all (except TTYPE,NAWS) */ + +static int subneg(byte c) +{ + switch (G.telstate) + { + case TS_SUB1: + if (c == IAC) + G.telstate = TS_SUB2; +#ifdef CONFIG_FEATURE_TELNET_TTYPE + else + if (c == TELOPT_TTYPE) + putiac_subopt(TELOPT_TTYPE,ttype); +#endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + else + if (c == TELOPT_NEW_ENVIRON) + putiac_subopt_autologin(); +#endif + break; + case TS_SUB2: + if (c == SE) + return TRUE; + G.telstate = TS_SUB1; + /* break; */ + } + return FALSE; +} + +/* ******************************* */ + +static void fgotsig(int sig) +{ + G.gotsig = sig; +} + + +static void rawmode(void) +{ + tcsetattr(0, TCSADRAIN, &G.termios_raw); +} + +static void cookmode(void) +{ + tcsetattr(0, TCSADRAIN, &G.termios_def); +} + +extern int telnet_main(int argc, char** argv) +{ + int len; + struct sockaddr_in s_in; +#ifdef USE_POLL + struct pollfd ufds[2]; +#else + fd_set readfds; + int maxfd; +#endif + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + int opt; +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH + get_terminal_width_height(0, &win_width, &win_height); +#endif + +#ifdef CONFIG_FEATURE_TELNET_TTYPE + ttype = getenv("TERM"); +#endif + + memset(&G, 0, sizeof G); + + if (tcgetattr(0, &G.termios_def) < 0) + exit(1); + + G.termios_raw = G.termios_def; + cfmakeraw(&G.termios_raw); + + if (argc < 2) + bb_show_usage(); + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + autologin = NULL; + while ((opt = getopt(argc, argv, "al:")) != EOF) { + switch (opt) { + case 'l': + autologin = optarg; + break; + case 'a': + autologin = getenv("USER"); + break; + case '?': + bb_show_usage(); + break; + } + } + if (optind < argc) { + bb_lookup_host(&s_in, argv[optind++]); + s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] : + "telnet", "tcp", 23); + if (optind < argc) + bb_show_usage(); + } else + bb_show_usage(); +#else + bb_lookup_host(&s_in, argv[1]); + s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23); +#endif + + G.netfd = xconnect(&s_in); + + setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); + + signal(SIGINT, fgotsig); + +#ifdef USE_POLL + ufds[0].fd = 0; ufds[1].fd = G.netfd; + ufds[0].events = ufds[1].events = POLLIN; +#else + FD_ZERO(&readfds); + FD_SET(0, &readfds); + FD_SET(G.netfd, &readfds); + maxfd = G.netfd + 1; +#endif + + while (1) + { +#ifndef USE_POLL + fd_set rfds = readfds; + + switch (select(maxfd, &rfds, NULL, NULL, NULL)) +#else + switch (poll(ufds, 2, -1)) +#endif + { + case 0: + /* timeout */ + case -1: + /* error, ignore and/or log something, bay go to loop */ + if (G.gotsig) + conescape(); + else + sleep(1); + break; + default: + +#ifdef USE_POLL + if (ufds[0].revents) /* well, should check POLLIN, but ... */ +#else + if (FD_ISSET(0, &rfds)) +#endif + { + len = read(0, G.buf, DATABUFSIZE); + + if (len <= 0) + doexit(0); + + TRACE(0, ("Read con: %d\n", len)); + + handlenetoutput(len); + } + +#ifdef USE_POLL + if (ufds[1].revents) /* well, should check POLLIN, but ... */ +#else + if (FD_ISSET(G.netfd, &rfds)) +#endif + { + len = read(G.netfd, G.buf, DATABUFSIZE); + + if (len <= 0) + { + WriteCS(1, "Connection closed by foreign host.\r\n"); + doexit(1); + } + TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len)); + + handlenetinput(len); + } + } + } +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/networking/telnetd.c b/busybox/networking/telnetd.c new file mode 100644 index 000000000..491c66fd1 --- /dev/null +++ b/busybox/networking/telnetd.c @@ -0,0 +1,660 @@ +/* $Id: telnetd.c,v 1.13 2004/09/14 17:24:58 bug1 Exp $ + * + * Simple telnet server + * Bjorn Wesen, Axis Communications AB (bjornw@axis.com) + * + * This file is distributed under the Gnu Public License (GPL), + * please see the file LICENSE for further information. + * + * --------------------------------------------------------------------------- + * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN + **************************************************************************** + * + * The telnetd manpage says it all: + * + * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for + * a client, then creating a login process which has the slave side of the + * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the + * master side of the pseudo-terminal, implementing the telnet protocol and + * passing characters between the remote client and the login process. + * + * Vladimir Oleynik 2001 + * Set process group corrections, initial busybox port + */ + +/*#define DEBUG 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#define TELCMDS +#define TELOPTS +#endif +#include +#include +#include + +#include "busybox.h" + +#define BUFSIZE 4000 + +#ifdef CONFIG_LOGIN +static const char *loginpath = "/bin/login"; +#else +static const char *loginpath; +#endif +static const char *issuefile = "/etc/issue.net"; + +/* shell name and arguments */ + +static const char *argv_init[] = {NULL, NULL}; + +/* structure that describes a session */ + +struct tsession { +#ifdef CONFIG_FEATURE_TELNETD_INETD + int sockfd_read, sockfd_write, ptyfd; +#else /* CONFIG_FEATURE_TELNETD_INETD */ + struct tsession *next; + int sockfd, ptyfd; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + int shell_pid; + /* two circular buffers */ + char *buf1, *buf2; + int rdidx1, wridx1, size1; + int rdidx2, wridx2, size2; +}; + +/* + + This is how the buffers are used. The arrows indicate the movement + of data. + + +-------+ wridx1++ +------+ rdidx1++ +----------+ + | | <-------------- | buf1 | <-------------- | | + | | size1-- +------+ size1++ | | + | pty | | socket | + | | rdidx2++ +------+ wridx2++ | | + | | --------------> | buf2 | --------------> | | + +-------+ size2++ +------+ size2-- +----------+ + + Each session has got two buffers. + +*/ + +static int maxfd; + +static struct tsession *sessions; + + +/* + + Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored + and must be removed so as to not be interpreted by the terminal). Make an + uninterrupted string of characters fit for the terminal. Do this by packing + all characters meant for the terminal sequentially towards the end of bf. + + Return a pointer to the beginning of the characters meant for the terminal. + and make *num_totty the number of characters that should be sent to + the terminal. + + Note - If an IAC (3 byte quantity) starts before (bf + len) but extends + past (bf + len) then that IAC will be left unprocessed and *processed will be + less than len. + + FIXME - if we mean to send 0xFF to the terminal then it will be escaped, + what is the escape character? We aren't handling that situation here. + + CR-LF ->'s CR mapping is also done here, for convenience + + */ +static char * +remove_iacs(struct tsession *ts, int *pnum_totty) { + unsigned char *ptr0 = ts->buf1 + ts->wridx1; + unsigned char *ptr = ptr0; + unsigned char *totty = ptr; + unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); + int processed; + int num_totty; + + while (ptr < end) { + if (*ptr != IAC) { + int c = *ptr; + *totty++ = *ptr++; + /* We now map \r\n ==> \r for pragmatic reasons. + * Many client implementations send \r\n when + * the user hits the CarriageReturn key. + */ + if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end) + ptr++; + } + else { + /* + * TELOPT_NAWS support! + */ + if ((ptr+2) >= end) { + /* only the beginning of the IAC is in the + buffer we were asked to process, we can't + process this char. */ + break; + } + + /* + * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE + */ + else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) { + struct winsize ws; + if ((ptr+8) >= end) + break; /* incomplete, can't process */ + ws.ws_col = (ptr[3] << 8) | ptr[4]; + ws.ws_row = (ptr[5] << 8) | ptr[6]; + (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); + ptr += 9; + } + else { + /* skip 3-byte IAC non-SB cmd */ +#ifdef DEBUG + fprintf(stderr, "Ignoring IAC %s,%s\n", + TELCMD(*(ptr+1)), TELOPT(*(ptr+2))); +#endif + ptr += 3; + } + } + } + + processed = ptr - ptr0; + num_totty = totty - ptr0; + /* the difference between processed and num_to tty + is all the iacs we removed from the stream. + Adjust buf1 accordingly. */ + ts->wridx1 += processed - num_totty; + ts->size1 -= processed - num_totty; + *pnum_totty = num_totty; + /* move the chars meant for the terminal towards the end of the + buffer. */ + return memmove(ptr - num_totty, ptr0, num_totty); +} + + +static int +getpty(char *line) +{ + int p; +#ifdef CONFIG_FEATURE_DEVPTS + p = open("/dev/ptmx", 2); + if (p > 0) { + grantpt(p); + unlockpt(p); + strcpy(line, ptsname(p)); + return(p); + } +#else + struct stat stb; + int i; + int j; + + strcpy(line, "/dev/ptyXX"); + + for (i = 0; i < 16; i++) { + line[8] = "pqrstuvwxyzabcde"[i]; + line[9] = '0'; + if (stat(line, &stb) < 0) { + continue; + } + for (j = 0; j < 16; j++) { + line[9] = j < 10 ? j + '0' : j - 10 + 'a'; + if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) { + line[5] = 't'; + return p; + } + } + } +#endif /* CONFIG_FEATURE_DEVPTS */ + return -1; +} + + +static void +send_iac(struct tsession *ts, unsigned char command, int option) +{ + /* We rely on that there is space in the buffer for now. */ + char *b = ts->buf2 + ts->rdidx2; + *b++ = IAC; + *b++ = command; + *b++ = option; + ts->rdidx2 += 3; + ts->size2 += 3; +} + + +static struct tsession * +#ifdef CONFIG_FEATURE_TELNETD_INETD +make_new_session(void) +#else /* CONFIG_FEATURE_TELNETD_INETD */ +make_new_session(int sockfd) +#endif /* CONFIG_FEATURE_TELNETD_INETD */ +{ + struct termios termbuf; + int pty, pid; + char tty_name[32]; + struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2); + + ts->buf1 = (char *)(&ts[1]); + ts->buf2 = ts->buf1 + BUFSIZE; + +#ifdef CONFIG_FEATURE_TELNETD_INETD + ts->sockfd_read = 0; + ts->sockfd_write = 1; +#else /* CONFIG_FEATURE_TELNETD_INETD */ + ts->sockfd = sockfd; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + ts->rdidx1 = ts->wridx1 = ts->size1 = 0; + ts->rdidx2 = ts->wridx2 = ts->size2 = 0; + + /* Got a new connection, set up a tty and spawn a shell. */ + + pty = getpty(tty_name); + + if (pty < 0) { + syslog(LOG_ERR, "All network ports in use!"); + return 0; + } + + if (pty > maxfd) + maxfd = pty; + + ts->ptyfd = pty; + + /* Make the telnet client understand we will echo characters so it + * should not do it locally. We don't tell the client to run linemode, + * because we want to handle line editing and tab completion and other + * stuff that requires char-by-char support. + */ + + send_iac(ts, DO, TELOPT_ECHO); + send_iac(ts, DO, TELOPT_NAWS); + send_iac(ts, DO, TELOPT_LFLOW); + send_iac(ts, WILL, TELOPT_ECHO); + send_iac(ts, WILL, TELOPT_SGA); + + + if ((pid = fork()) < 0) { + syslog(LOG_ERR, "Can`t forking"); + } + if (pid == 0) { + /* In child, open the child's side of the tty. */ + int i; + + for(i = 0; i <= maxfd; i++) + close(i); + /* make new process group */ + setsid(); + + if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) { + syslog(LOG_ERR, "Could not open tty"); + exit(1); + } + dup(0); + dup(0); + + tcsetpgrp(0, getpid()); + + /* The pseudo-terminal allocated to the client is configured to operate in + * cooked mode, and with XTABS CRMOD enabled (see tty(4)). + */ + + tcgetattr(0, &termbuf); + termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ + termbuf.c_oflag |= ONLCR|XTABS; + termbuf.c_iflag |= ICRNL; + termbuf.c_iflag &= ~IXOFF; + /*termbuf.c_lflag &= ~ICANON;*/ + tcsetattr(0, TCSANOW, &termbuf); + + print_login_issue(issuefile, NULL); + + /* exec shell, with correct argv and env */ + execv(loginpath, (char *const *)argv_init); + + /* NOT REACHED */ + syslog(LOG_ERR, "execv error"); + exit(1); + } + + ts->shell_pid = pid; + + return ts; +} + +#ifndef CONFIG_FEATURE_TELNETD_INETD +static void +free_session(struct tsession *ts) +{ + struct tsession *t = sessions; + + /* Unlink this telnet session from the session list. */ + if(t == ts) + sessions = ts->next; + else { + while(t->next != ts) + t = t->next; + t->next = ts->next; + } + + kill(ts->shell_pid, SIGKILL); + + wait4(ts->shell_pid, NULL, 0, NULL); + + close(ts->ptyfd); + close(ts->sockfd); + + if(ts->ptyfd == maxfd || ts->sockfd == maxfd) + maxfd--; + if(ts->ptyfd == maxfd || ts->sockfd == maxfd) + maxfd--; + + free(ts); +} +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + +int +telnetd_main(int argc, char **argv) +{ +#ifndef CONFIG_FEATURE_TELNETD_INETD + struct sockaddr_in sa; + int master_fd; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + fd_set rdfdset, wrfdset; + int selret; +#ifndef CONFIG_FEATURE_TELNETD_INETD + int on = 1; + int portnbr = 23; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + int c; + static const char options[] = +#ifdef CONFIG_FEATURE_TELNETD_INETD + "f:l:"; +#else /* CONFIG_EATURE_TELNETD_INETD */ + "f:l:p:"; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + int maxlen, w, r; + +#ifndef CONFIG_LOGIN + loginpath = DEFAULT_SHELL; +#endif + + for (;;) { + c = getopt( argc, argv, options); + if (c == EOF) break; + switch (c) { + case 'f': + issuefile = optarg; + break; + case 'l': + loginpath = optarg; + break; +#ifndef CONFIG_FEATURE_TELNETD_INETD + case 'p': + portnbr = atoi(optarg); + break; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + default: + bb_show_usage(); + } + } + + if (access(loginpath, X_OK) < 0) { + bb_error_msg_and_die ("'%s' unavailable.", loginpath); + } + + argv_init[0] = loginpath; + + openlog(bb_applet_name, 0, LOG_USER); + +#ifdef CONFIG_FEATURE_TELNETD_INETD + maxfd = 1; + sessions = make_new_session(); +#else /* CONFIG_EATURE_TELNETD_INETD */ + sessions = 0; + + /* Grab a TCP socket. */ + + master_fd = socket(AF_INET, SOCK_STREAM, 0); + if (master_fd < 0) { + bb_perror_msg_and_die("socket"); + } + (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + /* Set it to listen to specified port. */ + + memset((void *)&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(portnbr); + + if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + bb_perror_msg_and_die("bind"); + } + + if (listen(master_fd, 1) < 0) { + bb_perror_msg_and_die("listen"); + } + + if (daemon(0, 0) < 0) + bb_perror_msg_and_die("daemon"); + + + maxfd = master_fd; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + do { + struct tsession *ts; + + FD_ZERO(&rdfdset); + FD_ZERO(&wrfdset); + + /* select on the master socket, all telnet sockets and their + * ptys if there is room in their respective session buffers. + */ + +#ifndef CONFIG_FEATURE_TELNETD_INETD + FD_SET(master_fd, &rdfdset); +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + ts = sessions; +#ifndef CONFIG_FEATURE_TELNETD_INETD + while (ts) { +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + /* buf1 is used from socket to pty + * buf2 is used from pty to socket + */ + if (ts->size1 > 0) { + FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */ + } + if (ts->size1 < BUFSIZE) { +#ifdef CONFIG_FEATURE_TELNETD_INETD + FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */ +#else /* CONFIG_FEATURE_TELNETD_INETD */ + FD_SET(ts->sockfd, &rdfdset); /* can read from socket */ +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + } + if (ts->size2 > 0) { +#ifdef CONFIG_FEATURE_TELNETD_INETD + FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */ +#else /* CONFIG_FEATURE_TELNETD_INETD */ + FD_SET(ts->sockfd, &wrfdset); /* can write to socket */ +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + } + if (ts->size2 < BUFSIZE) { + FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */ + } +#ifndef CONFIG_FEATURE_TELNETD_INETD + ts = ts->next; + } +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); + + if (!selret) + break; + +#ifndef CONFIG_FEATURE_TELNETD_INETD + /* First check for and accept new sessions. */ + if (FD_ISSET(master_fd, &rdfdset)) { + int fd, salen; + + salen = sizeof(sa); + if ((fd = accept(master_fd, (struct sockaddr *)&sa, + &salen)) < 0) { + continue; + } else { + /* Create a new session and link it into + our active list. */ + struct tsession *new_ts = make_new_session(fd); + if (new_ts) { + new_ts->next = sessions; + sessions = new_ts; + if (fd > maxfd) + maxfd = fd; + } else { + close(fd); + } + } + } + + /* Then check for data tunneling. */ + + ts = sessions; + while (ts) { /* For all sessions... */ +#endif /* CONFIG_FEATURE_TELNETD_INETD */ +#ifndef CONFIG_FEATURE_TELNETD_INETD + struct tsession *next = ts->next; /* in case we free ts. */ +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) { + int num_totty; + char *ptr; + /* Write to pty from buffer 1. */ + + ptr = remove_iacs(ts, &num_totty); + + w = write(ts->ptyfd, ptr, num_totty); + if (w < 0) { +#ifdef CONFIG_FEATURE_TELNETD_INETD + exit(0); +#else /* CONFIG_FEATURE_TELNETD_INETD */ + free_session(ts); + ts = next; + continue; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + } + ts->wridx1 += w; + ts->size1 -= w; + if (ts->wridx1 == BUFSIZE) + ts->wridx1 = 0; + } + +#ifdef CONFIG_FEATURE_TELNETD_INETD + if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) { +#else /* CONFIG_FEATURE_TELNETD_INETD */ + if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) { +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + /* Write to socket from buffer 2. */ + maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); +#ifdef CONFIG_FEATURE_TELNETD_INETD + w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen); + if (w < 0) + exit(0); +#else /* CONFIG_FEATURE_TELNETD_INETD */ + w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen); + if (w < 0) { + free_session(ts); + ts = next; + continue; + } +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + ts->wridx2 += w; + ts->size2 -= w; + if (ts->wridx2 == BUFSIZE) + ts->wridx2 = 0; + } + +#ifdef CONFIG_FEATURE_TELNETD_INETD + if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) { +#else /* CONFIG_FEATURE_TELNETD_INETD */ + if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) { +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + /* Read from socket to buffer 1. */ + maxlen = MIN(BUFSIZE - ts->rdidx1, + BUFSIZE - ts->size1); +#ifdef CONFIG_FEATURE_TELNETD_INETD + r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen); + if (!r || (r < 0 && errno != EINTR)) + exit(0); +#else /* CONFIG_FEATURE_TELNETD_INETD */ + r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen); + if (!r || (r < 0 && errno != EINTR)) { + free_session(ts); + ts = next; + continue; + } +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + if(!*(ts->buf1 + ts->rdidx1 + r - 1)) { + r--; + if(!r) + continue; + } + ts->rdidx1 += r; + ts->size1 += r; + if (ts->rdidx1 == BUFSIZE) + ts->rdidx1 = 0; + } + + if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) { + /* Read from pty to buffer 2. */ + maxlen = MIN(BUFSIZE - ts->rdidx2, + BUFSIZE - ts->size2); + r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen); + if (!r || (r < 0 && errno != EINTR)) { +#ifdef CONFIG_FEATURE_TELNETD_INETD + exit(0); +#else /* CONFIG_FEATURE_TELNETD_INETD */ + free_session(ts); + ts = next; + continue; +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + } + ts->rdidx2 += r; + ts->size2 += r; + if (ts->rdidx2 == BUFSIZE) + ts->rdidx2 = 0; + } + + if (ts->size1 == 0) { + ts->rdidx1 = 0; + ts->wridx1 = 0; + } + if (ts->size2 == 0) { + ts->rdidx2 = 0; + ts->wridx2 = 0; + } +#ifndef CONFIG_FEATURE_TELNETD_INETD + ts = next; + } +#endif /* CONFIG_FEATURE_TELNETD_INETD */ + + } while (1); + + return 0; +} diff --git a/busybox/networking/tftp.c b/busybox/networking/tftp.c new file mode 100644 index 000000000..3c947318b --- /dev/null +++ b/busybox/networking/tftp.c @@ -0,0 +1,584 @@ +/* ------------------------------------------------------------------------- */ +/* tftp.c */ +/* */ +/* A simple tftp client for busybox. */ +/* Tries to follow RFC1350. */ +/* Only "octet" mode supported. */ +/* Optional blocksize negotiation (RFC2347 + RFC2348) */ +/* */ +/* Copyright (C) 2001 Magnus Damm */ +/* */ +/* 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 CONFIG_FEATURE_TFTP_DEBUG + +#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ +#define TFTP_TIMEOUT 5 /* seconds */ + +/* opcodes we support */ + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 +#define TFTP_OACK 6 + +static const char *tftp_bb_error_msg[] = { + "Undefined error", + "File not found", + "Access violation", + "Disk full or allocation error", + "Illegal TFTP operation", + "Unknown transfer ID", + "File already exists", + "No such user" +}; + +const int tftp_cmd_get = 1; +const int tftp_cmd_put = 2; + +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + +static int tftp_blocksize_check(int blocksize, int bufsize) +{ + /* Check if the blocksize is valid: + * RFC2348 says between 8 and 65464, + * but our implementation makes it impossible + * to use blocksizes smaller than 22 octets. + */ + + if ((bufsize && (blocksize > bufsize)) || + (blocksize < 8) || (blocksize > 65464)) { + bb_error_msg("bad blocksize"); + return 0; + } + + return blocksize; +} + +static char *tftp_option_get(char *buf, int len, char *option) +{ + int opt_val = 0; + int opt_found = 0; + int k; + + while (len > 0) { + + /* Make sure the options are terminated correctly */ + + for (k = 0; k < len; k++) { + if (buf[k] == '\0') { + break; + } + } + + if (k >= len) { + break; + } + + if (opt_val == 0) { + if (strcasecmp(buf, option) == 0) { + opt_found = 1; + } + } + else { + if (opt_found) { + return buf; + } + } + + k++; + + buf += k; + len -= k; + + opt_val ^= 1; + } + + return NULL; +} + +#endif + +static inline int tftp(const int cmd, const struct hostent *host, + const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize) +{ + const int cmd_get = cmd & tftp_cmd_get; + const int cmd_put = cmd & tftp_cmd_put; + const int bb_tftp_num_retries = 5; + + struct sockaddr_in sa; + struct sockaddr_in from; + struct timeval tv; + socklen_t fromlen; + fd_set rfds; + char *cp; + unsigned short tmp; + int socketfd; + int len; + int opcode = 0; + int finished = 0; + int timeout = bb_tftp_num_retries; + unsigned short block_nr = 1; + +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + int want_option_ack = 0; +#endif + + /* Can't use RESERVE_CONFIG_BUFFER here since the allocation + * size varies meaning BUFFERS_GO_ON_STACK would fail */ + char *buf=xmalloc(tftp_bufsize + 4); + + tftp_bufsize += 4; + + if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg("socket"); + return EXIT_FAILURE; + } + + len = sizeof(sa); + + memset(&sa, 0, len); + bind(socketfd, (struct sockaddr *)&sa, len); + + sa.sin_family = host->h_addrtype; + sa.sin_port = port; + memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, + sizeof(sa.sin_addr)); + + /* build opcode */ + + if (cmd_get) { + opcode = TFTP_RRQ; + } + + if (cmd_put) { + opcode = TFTP_WRQ; + } + + while (1) { + + cp = buf; + + /* first create the opcode part */ + + *((unsigned short *) cp) = htons(opcode); + + cp += 2; + + /* add filename and mode */ + + if ((cmd_get && (opcode == TFTP_RRQ)) || + (cmd_put && (opcode == TFTP_WRQ))) { + int too_long = 0; + + /* see if the filename fits into buf */ + /* and fill in packet */ + + len = strlen(remotefile) + 1; + + if ((cp + len) >= &buf[tftp_bufsize - 1]) { + too_long = 1; + } + else { + safe_strncpy(cp, remotefile, len); + cp += len; + } + + if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) { + bb_error_msg("too long remote-filename"); + break; + } + + /* add "mode" part of the package */ + + memcpy(cp, "octet", 6); + cp += 6; + +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + + len = tftp_bufsize - 4; /* data block size */ + + if (len != TFTP_BLOCKSIZE_DEFAULT) { + + if ((&buf[tftp_bufsize - 1] - cp) < 15) { + bb_error_msg("too long remote-filename"); + break; + } + + /* add "blksize" + number of blocks */ + + memcpy(cp, "blksize", 8); + cp += 8; + + cp += snprintf(cp, 6, "%d", len) + 1; + + want_option_ack = 1; + } +#endif + } + + /* add ack and data */ + + if ((cmd_get && (opcode == TFTP_ACK)) || + (cmd_put && (opcode == TFTP_DATA))) { + + *((unsigned short *) cp) = htons(block_nr); + + cp += 2; + + block_nr++; + + if (cmd_put && (opcode == TFTP_DATA)) { + len = bb_full_read(localfd, cp, tftp_bufsize - 4); + + if (len < 0) { + bb_perror_msg("read"); + break; + } + + if (len != (tftp_bufsize - 4)) { + finished++; + } + + cp += len; + } + } + + + /* send packet */ + + + timeout = bb_tftp_num_retries; /* re-initialize */ + do { + + len = cp - buf; + +#ifdef CONFIG_FEATURE_TFTP_DEBUG + fprintf(stderr, "sending %u bytes\n", len); + for (cp = buf; cp < &buf[len]; cp++) + fprintf(stderr, "%02x ", (unsigned char)*cp); + fprintf(stderr, "\n"); +#endif + if (sendto(socketfd, buf, len, 0, + (struct sockaddr *) &sa, sizeof(sa)) < 0) { + bb_perror_msg("send"); + len = -1; + break; + } + + + if (finished && (opcode == TFTP_ACK)) { + break; + } + + /* receive packet */ + + memset(&from, 0, sizeof(from)); + fromlen = sizeof(from); + + tv.tv_sec = TFTP_TIMEOUT; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(socketfd, &rfds); + + switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) { + case 1: + len = recvfrom(socketfd, buf, tftp_bufsize, 0, + (struct sockaddr *) &from, &fromlen); + + if (len < 0) { + bb_perror_msg("recvfrom"); + break; + } + + timeout = 0; + + if (sa.sin_port == port) { + sa.sin_port = from.sin_port; + } + if (sa.sin_port == from.sin_port) { + break; + } + + /* fall-through for bad packets! */ + /* discard the packet - treat as timeout */ + timeout = bb_tftp_num_retries; + + case 0: + bb_error_msg("timeout"); + + timeout--; + if (timeout == 0) { + len = -1; + bb_error_msg("last timeout"); + } + break; + + default: + bb_perror_msg("select"); + len = -1; + } + + } while (timeout && (len >= 0)); + + if ((finished) || (len < 0)) { + break; + } + + /* process received packet */ + + + opcode = ntohs(*((unsigned short *) buf)); + tmp = ntohs(*((unsigned short *) &buf[2])); + +#ifdef CONFIG_FEATURE_TFTP_DEBUG + fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp); +#endif + + if (opcode == TFTP_ERROR) { + char *msg = NULL; + + if (buf[4] != '\0') { + msg = &buf[4]; + buf[tftp_bufsize - 1] = '\0'; + } else if (tmp < (sizeof(tftp_bb_error_msg) + / sizeof(char *))) { + + msg = (char *) tftp_bb_error_msg[tmp]; + } + + if (msg) { + bb_error_msg("server says: %s", msg); + } + + break; + } + +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + if (want_option_ack) { + + want_option_ack = 0; + + if (opcode == TFTP_OACK) { + + /* server seems to support options */ + + char *res; + + res = tftp_option_get(&buf[2], len-2, + "blksize"); + + if (res) { + int blksize = atoi(res); + + if (tftp_blocksize_check(blksize, + tftp_bufsize - 4)) { + + if (cmd_put) { + opcode = TFTP_DATA; + } + else { + opcode = TFTP_ACK; + } +#ifdef CONFIG_FEATURE_TFTP_DEBUG + fprintf(stderr, "using blksize %u\n", blksize); +#endif + tftp_bufsize = blksize + 4; + block_nr = 0; + continue; + } + } + /* FIXME: + * we should send ERROR 8 */ + bb_error_msg("bad server option"); + break; + } + + bb_error_msg("warning: blksize not supported by server" + " - reverting to 512"); + + tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4; + } +#endif + + if (cmd_get && (opcode == TFTP_DATA)) { + + if (tmp == block_nr) { + + len = bb_full_write(localfd, &buf[4], len - 4); + + if (len < 0) { + bb_perror_msg("write"); + break; + } + + if (len != (tftp_bufsize - 4)) { + finished++; + } + + opcode = TFTP_ACK; + continue; + } + } + + if (cmd_put && (opcode == TFTP_ACK)) { + + if (tmp == (unsigned short)(block_nr - 1)) { + if (finished) { + break; + } + + opcode = TFTP_DATA; + continue; + } + } + } + +#ifdef CONFIG_FEATURE_CLEAN_UP + close(socketfd); + + free(buf); +#endif + + return finished ? EXIT_SUCCESS : EXIT_FAILURE; +} + +int tftp_main(int argc, char **argv) +{ + struct hostent *host = NULL; + const char *localfile = NULL; + const char *remotefile = NULL; + int port; + int cmd = 0; + int fd = -1; + int flags = 0; + int opt; + int result; + int blocksize = TFTP_BLOCKSIZE_DEFAULT; + + /* figure out what to pass to getopt */ + +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE +#define BS "b:" +#else +#define BS +#endif + +#ifdef CONFIG_FEATURE_TFTP_GET +#define GET "g" +#else +#define GET +#endif + +#ifdef CONFIG_FEATURE_TFTP_PUT +#define PUT "p" +#else +#define PUT +#endif + + while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) { + switch (opt) { +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + case 'b': + blocksize = atoi(optarg); + if (!tftp_blocksize_check(blocksize, 0)) { + return EXIT_FAILURE; + } + break; +#endif +#ifdef CONFIG_FEATURE_TFTP_GET + case 'g': + cmd = tftp_cmd_get; + flags = O_WRONLY | O_CREAT | O_TRUNC; + break; +#endif +#ifdef CONFIG_FEATURE_TFTP_PUT + case 'p': + cmd = tftp_cmd_put; + flags = O_RDONLY; + break; +#endif + case 'l': + localfile = optarg; + break; + case 'r': + remotefile = optarg; + break; + } + } + + if ((cmd == 0) || (optind == argc)) { + bb_show_usage(); + } + if(localfile && strcmp(localfile, "-") == 0) { + fd = fileno((cmd==tftp_cmd_get)? stdout : stdin); + } + if(localfile == NULL) + localfile = remotefile; + if(remotefile == NULL) + remotefile = localfile; + if (fd==-1) { + fd = open(localfile, flags, 0644); + } + if (fd < 0) { + bb_perror_msg_and_die("local file"); + } + + host = xgethostbyname(argv[optind]); + port = bb_lookup_port(argv[optind + 1], "udp", 69); + +#ifdef CONFIG_FEATURE_TFTP_DEBUG + fprintf(stderr, "using server \"%s\", remotefile \"%s\", " + "localfile \"%s\".\n", + inet_ntoa(*((struct in_addr *) host->h_addr)), + remotefile, localfile); +#endif + + result = tftp(cmd, host, remotefile, fd, port, blocksize); + +#ifdef CONFIG_FEATURE_CLEAN_UP + if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) { + close(fd); + } +#endif + return(result); +} diff --git a/busybox/networking/traceroute.c b/busybox/networking/traceroute.c new file mode 100644 index 000000000..44ffdf07e --- /dev/null +++ b/busybox/networking/traceroute.c @@ -0,0 +1,548 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson. + * + * Special for busybox ported by Vladimir Oleynik 2001 + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * traceroute host - trace the route ip packets follow going to "host". + * Notes + * ----- + * This program must be run by root or be setuid. (I suggest that + * you *don't* make it setuid -- casual use could result in a lot + * of unnecessary traffic on our poor, congested nets.) + * + * I stole the idea for this program from Steve Deering. Since + * the first release, I've learned that had I attended the right + * IETF working group meetings, I also could have stolen it from Guy + * Almes or Matt Mathis. I don't know (or care) who came up with + * the idea first. I envy the originators' perspicacity and I'm + * glad they didn't keep the idea a secret. + * + * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or + * enhancements to the original distribution. + * + * I've hacked up a round-trip-route version of this that works by + * sending a loose-source-routed udp datagram through the destination + * back to yourself. Unfortunately, SO many gateways botch source + * routing, the thing is almost worthless. Maybe one day... + * + * -- Van Jacobson (van@helios.ee.lbl.gov) + * Tue Dec 20 03:50:13 PST 1988 + */ + +#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE +//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE +#undef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG /* not in documentation man */ + +#include +#include +#include +#include +#include +#include +#include "inet_common.h" +#include +#include +#include +#include +#include + + +#define MAXPACKET 65535 /* max ip packet size */ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +/* + * format of a (udp) probe packet. + */ +struct opacket { + struct ip ip; + struct udphdr udp; + u_char seq; /* sequence number of this packet */ + u_char ttl; /* ttl packet left with */ + struct timeval tv; /* time packet left */ +}; + +/* + * Definitions for internet protocol version 4. + * Per RFC 791, September 1981. + */ +#define IPVERSION 4 + + +#include "busybox.h" + +static u_char packet[512]; /* last inbound (icmp) packet */ +static struct opacket *outpacket; /* last output (udp) packet */ + +static int s; /* receive (icmp) socket file descriptor */ +static int sndsock; /* send (udp) socket file descriptor */ + +static struct sockaddr whereto; /* Who to try to reach */ +static int datalen; /* How much data */ + +static char *hostname; + +static int max_ttl = 30; +static u_short ident; +static u_short port = 32768+666; /* start udp dest port # for probe packets */ + +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE +static int verbose; +#endif +static int waittime = 5; /* time to wait for response (in seconds) */ +static int nflag; /* print addresses numerically */ + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +static inline void +inetname(struct sockaddr_in *from) +{ + char *cp; + static char domain[MAXHOSTNAMELEN + 1]; + char name[MAXHOSTNAMELEN + 1]; + static int first = 1; + const char *ina; + + if (first && !nflag) { + first = 0; + if (getdomainname(domain, MAXHOSTNAMELEN) != 0) + domain[0] = 0; + } + cp = 0; + if (!nflag && from->sin_addr.s_addr != INADDR_ANY) { + if(INET_rresolve(name, sizeof(name), from, 0x4000, 0xffffffff) >= 0) { + if ((cp = strchr(name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = (char *)name; + } + } + ina = inet_ntoa(from->sin_addr); + if (nflag) + printf(" %s", ina); + else + printf(" %s (%s)", (cp ? cp : ina), ina); +} + +static inline void +print(u_char *buf, int cc, struct sockaddr_in *from) +{ + struct ip *ip; + int hlen; + + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + cc -= hlen; + + inetname(from); +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE + if (verbose) + printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst)); +#endif +} + +static inline double +deltaT(struct timeval *t1p, struct timeval *t2p) +{ + double dt; + + dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + + (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; + return (dt); +} + +static inline int +wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer) +{ + fd_set fds; + static struct timeval wait; + int cc = 0; + int fromlen = sizeof (*from); + + FD_ZERO(&fds); + FD_SET(sock, &fds); + if (reset_timer) { + /* + * traceroute could hang if someone else has a ping + * running and our ICMP reply gets dropped but we don't + * realize it because we keep waking up to handle those + * other ICMP packets that keep coming in. To fix this, + * "reset_timer" will only be true if the last packet that + * came in was for us or if this is the first time we're + * waiting for a reply since sending out a probe. Note + * that this takes advantage of the select() feature on + * Linux where the remaining timeout is written to the + * struct timeval area. + */ + wait.tv_sec = waittime; + wait.tv_usec = 0; + } + + if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0) + cc=recvfrom(s, (char *)packet, sizeof(packet), 0, + (struct sockaddr *)from, &fromlen); + + return(cc); +} + +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE +/* + * Convert an ICMP "type" field to a printable string. + */ +static inline const char * +pr_type(u_char t) +{ + static const char * const ttab[] = { + "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable", + "Source Quench", "Redirect", "ICMP 6", "ICMP 7", + "Echo", "ICMP 9", "ICMP 10", "Time Exceeded", + "Param Problem", "Timestamp", "Timestamp Reply", "Info Request", + "Info Reply" + }; + + if(t > 16) + return("OUT-OF-RANGE"); + + return(ttab[t]); +} +#endif + +static inline int +packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq) +{ + struct icmp *icp; + u_char type, code; + int hlen; + struct ip *ip; + + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE + if (verbose) + printf("packet too short (%d bytes) from %s\n", cc, + inet_ntoa(from->sin_addr)); +#endif + return (0); + } + cc -= hlen; + icp = (struct icmp *)(buf + hlen); + type = icp->icmp_type; code = icp->icmp_code; + if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) || + type == ICMP_UNREACH) { + struct ip *hip; + struct udphdr *up; + + hip = &icp->icmp_ip; + hlen = hip->ip_hl << 2; + up = (struct udphdr *)((u_char *)hip + hlen); + if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP && + up->source == htons(ident) && + up->dest == htons(port+seq)) + return (type == ICMP_TIMXCEED? -1 : code+1); + } +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE + if (verbose) { + int i; + u_long *lp = (u_long *)&icp->icmp_ip; + + printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n", + cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst), + type, pr_type(type), icp->icmp_code); + for (i = 4; i < cc ; i += sizeof(long)) + printf("%2d: x%8.8lx\n", i, *lp++); + } +#endif + return(0); +} + +static void /* not inline */ +send_probe(int seq, int ttl) +{ + struct opacket *op = outpacket; + struct ip *ip = &op->ip; + struct udphdr *up = &op->udp; + int i; + struct timezone tz; + + ip->ip_off = 0; + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_p = IPPROTO_UDP; + ip->ip_len = datalen; + ip->ip_ttl = ttl; + ip->ip_v = IPVERSION; + ip->ip_id = htons(ident+seq); + + up->source = htons(ident); + up->dest = htons(port+seq); + up->len = htons((u_short)(datalen - sizeof(struct ip))); + up->check = 0; + + op->seq = seq; + op->ttl = ttl; + (void) gettimeofday(&op->tv, &tz); + + i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto, + sizeof(struct sockaddr)); + if (i < 0 || i != datalen) { + if (i<0) + perror("sendto"); + printf("traceroute: wrote %s %d chars, ret=%d\n", hostname, + datalen, i); + (void) fflush(stdout); + } +} + + +int +#ifndef CONFIG_TRACEROUTE +main(int argc, char *argv[]) +#else +traceroute_main(int argc, char *argv[]) +#endif +{ + extern char *optarg; + extern int optind; + struct hostent *hp; + struct sockaddr_in from, *to; + int ch, i, on, probe, seq, tos, ttl; + + int options = 0; /* socket options */ + char *source = 0; + int nprobes = 3; + + on = 1; + seq = tos = 0; + to = (struct sockaddr_in *)&whereto; + while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF) + switch(ch) { + case 'd': +#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG + options |= SO_DEBUG; +#endif + break; + case 'm': + max_ttl = atoi(optarg); + if (max_ttl <= 1) + bb_error_msg_and_die("max ttl must be >1."); + break; + case 'n': + nflag++; + break; + case 'p': + port = atoi(optarg); + if (port < 1) + bb_error_msg_and_die("port must be >0."); + break; + case 'q': + nprobes = atoi(optarg); + if (nprobes < 1) + bb_error_msg_and_die("nprobes must be >0."); + break; + case 'r': + options |= SO_DONTROUTE; + break; + case 's': + /* + * set the ip source address of the outbound + * probe (e.g., on a multi-homed host). + */ + source = optarg; + break; + case 't': + tos = atoi(optarg); + if (tos < 0 || tos > 255) + bb_error_msg_and_die("tos must be 0 to 255."); + break; + case 'v': +#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE + verbose++; +#endif + break; + case 'w': + waittime = atoi(optarg); + if (waittime <= 1) + bb_error_msg_and_die("wait must be >1 sec."); + break; + default: + bb_show_usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + bb_show_usage(); + + setlinebuf (stdout); + + memset(&whereto, 0, sizeof(struct sockaddr)); + hp = xgethostbyname(*argv); + to->sin_family = hp->h_addrtype; + memcpy(&to->sin_addr, hp->h_addr, hp->h_length); + hostname = (char *)hp->h_name; + if (*++argv) + datalen = atoi(*argv); + if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket)) + bb_error_msg_and_die("packet size must be 0 <= s < %d.", + MAXPACKET - sizeof(struct opacket)); + datalen += sizeof(struct opacket); + outpacket = (struct opacket *)xmalloc((unsigned)datalen); + memset(outpacket, 0, datalen); + outpacket->ip.ip_dst = to->sin_addr; + outpacket->ip.ip_tos = tos; + outpacket->ip.ip_v = IPVERSION; + outpacket->ip.ip_id = 0; + + ident = (getpid() & 0xffff) | 0x8000; + + if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket); + + s = create_icmp_socket(); + +#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG + if (options & SO_DEBUG) + (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); +#endif + if (options & SO_DONTROUTE) + (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); +#ifdef SO_SNDBUF + if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen, + sizeof(datalen)) < 0) + bb_perror_msg_and_die("SO_SNDBUF"); +#endif +#ifdef IP_HDRINCL + if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on, + sizeof(on)) < 0) + bb_perror_msg_and_die("IP_HDRINCL"); +#endif +#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG + if (options & SO_DEBUG) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); +#endif + if (options & SO_DONTROUTE) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); + + if (source) { + memset(&from, 0, sizeof(struct sockaddr)); + from.sin_family = AF_INET; + from.sin_addr.s_addr = inet_addr(source); + if (from.sin_addr.s_addr == -1) + bb_error_msg_and_die("unknown host %s", source); + outpacket->ip.ip_src = from.sin_addr; +#ifndef IP_HDRINCL + if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0) + bb_perror_msg_and_die("bind"); +#endif + } + + fprintf(stderr, "traceroute to %s (%s)", hostname, + inet_ntoa(to->sin_addr)); + if (source) + fprintf(stderr, " from %s", source); + fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen); + + for (ttl = 1; ttl <= max_ttl; ++ttl) { + u_long lastaddr = 0; + int got_there = 0; + int unreachable = 0; + + printf("%2d ", ttl); + for (probe = 0; probe < nprobes; ++probe) { + int cc, reset_timer; + struct timeval t1, t2; + struct timezone tz; + struct ip *ip; + + (void) gettimeofday(&t1, &tz); + send_probe(++seq, ttl); + reset_timer = 1; + while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) { + (void) gettimeofday(&t2, &tz); + if ((i = packet_ok(packet, cc, &from, seq))) { + reset_timer = 1; + if (from.sin_addr.s_addr != lastaddr) { + print(packet, cc, &from); + lastaddr = from.sin_addr.s_addr; + } + printf(" %g ms", deltaT(&t1, &t2)); + switch(i - 1) { + case ICMP_UNREACH_PORT: + ip = (struct ip *)packet; + if (ip->ip_ttl <= 1) + printf(" !"); + ++got_there; + break; + case ICMP_UNREACH_NET: + ++unreachable; + printf(" !N"); + break; + case ICMP_UNREACH_HOST: + ++unreachable; + printf(" !H"); + break; + case ICMP_UNREACH_PROTOCOL: + ++got_there; + printf(" !P"); + break; + case ICMP_UNREACH_NEEDFRAG: + ++unreachable; + printf(" !F"); + break; + case ICMP_UNREACH_SRCFAIL: + ++unreachable; + printf(" !S"); + break; + } + break; + } else + reset_timer = 0; + } + if (cc == 0) + printf(" *"); + (void) fflush(stdout); + } + putchar('\n'); + if (got_there || unreachable >= nprobes-1) + return 0; + } + + return 0; +} diff --git a/busybox/networking/udhcp/AUTHORS b/busybox/networking/udhcp/AUTHORS new file mode 100644 index 000000000..f3f43364a --- /dev/null +++ b/busybox/networking/udhcp/AUTHORS @@ -0,0 +1,13 @@ +udhcp server/client package +----------------------- + +Russ Dill +Matthew Ramsay +Chris Trew + +Other Credits: +-------------- +Moreton Bay (http://www.moretonbay.com/) +Vladimir Oleynik Size optimizations + + diff --git a/busybox/networking/udhcp/COPYING b/busybox/networking/udhcp/COPYING new file mode 100644 index 000000000..a43ea2126 --- /dev/null +++ b/busybox/networking/udhcp/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + 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 + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/busybox/networking/udhcp/ChangeLog b/busybox/networking/udhcp/ChangeLog new file mode 100644 index 000000000..bf2982f4b --- /dev/null +++ b/busybox/networking/udhcp/ChangeLog @@ -0,0 +1,260 @@ +0.9.9 (pending) ++ Various other size optimizations (Vladimir) ++ Change strerror(errno) to %m (Vladimir N. Oleynik ) ++ Fixed a little endian problem in mton (Bastian Blank ) ++ Fixed a arpping alignment problem (Rui He ) ++ Added sanity check for max_leases (udhcp bug #1285) (me) ++ Finally got rid of the trailing space in enviromental vars (me) ++ added an new enviromental variable: $mask. It contains the number + of subnet bits for tools like ip route that require it. + (Bastian Blank , me) + +0.9.8 (021031) ++ split up README files (me) ++ use /dev/urandom to seed xid's (instead of time(0)) (me) ++ fixed renew behavior (me) ++ udhcp now fits nicely into busybox + (Glenn McGrath as well as myself) ++ updated client manpage (me) ++ both client and server now use sockets for signal handling, + hopefully, this will be the last needed change in signal + handling, I'm fairly certain all the possible races are now + closed. (me) ++ The server now restarts the auto_time timer when it receives + a SIGUSR1 (write out config file). (me) ++ Improve signal handling (David Poole) ++ Fix to config file parsing (Matt Kraai) ++ Fix load lease logic (me) ++ Fix clear_lease logic (me) ++ -h is now an alias for -H (udhcp bug #1253) ++ Shorter timeout on not receiving offers (me) ++ Improved signal behavior by client (me) ++ Would never assign end address (Keith Smith ) ++ Was improperly reporting yiaddr as siaddr (ben-udhcp@bdlow.net) + udhcp bug#1256 ++ Fixed reading of client id (David Poole ) ++ change sys_errlist[] to strerror() as it aparently doesn't exist + (Erik Andersen ) ++ fixed get_raw_packet so it returns -2 on non fatal errors + (Ted Lemon ) ++ Improved (hopefully) NAKing behavior (me) ++ Added -b option (Jouni Malinen) ++ Compute checksums correctly on big endian hosts + (Jouni Malinen ) + +0.9.7 (020526) ++ Use add_lease in read_leases, sanitizes leases more, and clears out exprired + ones if there is no more room (me) ++ Moved udhcpd.leases to /var/lib/misc/udhcpd.leases (Debian bug #147747) ++ Change (obsolete) AF_INET in arping.c to PF_PACKET (Debian bug #127049) ++ Added script hook for DHCPNAK (nak), as well as providing the message option + (me) ++ Generate the paramaters request list by seeing what options in options.c are + ored with OPTION_REQ in options.c ++ Fix dhcp renew forgetfullness on client (bug #1230) ++ Fix dhcp release bug on client (bug #1231) ++ Set option request list for DHCP renew (bug #1233) ++ Set BOOTREQUEST/REPLY properly ++ Change client-identifier field to popularly expected behavior (me) ++ Only reopen port on errors (me) ++ Change fork/close/setsid structures to daemon() (me) ++ Allow user to specify udhcpd config file at run time (Steven, me) ++ Write pidfile after changing it (Steven CTR Carr ) ++ Added env var docs to udhcpc man page (Matt) ++ Standardized lowercase udhcp in documentation (me) ++ Accept packets without a UDP checksum (me) ++ Accept packets with extra garbage (me) ++ Better error handling in files.c (me) ++ Combined read_interface function to reduce COMBINED_BINARY size (me) ++ Drop calc_length(), some servers choke on smaller packets (me) ++ Try to clean some fat out (me) + +0.9.6 (011001) ++ Added bootp paramaters to server (me) ++ Added bootp paramaters to client (me) ++ Added vendor id to client (me) ++ Better pidfile handling in client and server (me) ++ Added man pages (Matt Kraai ) + +0.9.5 (010914) ++ Fixed $HOME and $PATH env passing (me) ++ Fixed client to only listen for raw packets on correct interface (me) ++ added --quit,-q option to quit after a lease is obtained (me) ++ Fixed 100% CPU utilization by client when interface is down (me) + +0.9.4 (010827) ++ Force broadcast to broken clients that request unicast (ie, MSFT 98) ++ Make install rules (Adam J. Richter ) ++ One scripts, instead of many (Adam) ++ Removed script paramater info files (env vars only) (Adam) ++ Controlling of forking behavior in client (Adam) ++ General script.c/dhcpc.c cleanups (Adam) + +0.9.3 (010820) ++ Increased debugging verbosity (me) ++ Cut trailing whitespace when reading config file (me) ++ added hostname option to client (me) ++ fixed a strncpy bug in script.c (me) ++ fixed a leaky socket in dhcpc.c (me) ++ fixed a leaky socket in dhcpd.c (me) + +0.9.2 (010810) ++ Added raw sockets to client (me) ++ alignment fixes (Mark Huang) ++ compiler warning fixes (Mark Huang) ++ client now sends parameter list (Mark Huang/me) ++ added ipttl option ++ Does now not request broadcast packets + +0.9.1 (010806) ++ Added udhcpc client ++ reorganized functions/files ++ listening socket now only binds to one interface + +0.9.0 (010720) Major rewrite, current changes, goals: ++ should not segfault on bogus packets. ++ Options can be read from sname and file fields. ++ supports all DHCP messages (release, decline, inform). ++ IP block is now specified by a range of IP's. ++ Leases file now contains lease time (relative, or absolute). ++ Just about any DHCP option is now supported. ++ DNS entries are no longer read from resolv.conf ++ Lease file can be written periodically when the process receives a SIGUSR1 ++ arpping should be supported on all arches. ++ support for DHCP relays. ++ DHCP messages can be unicast if the client requests it. ++ many, many, many other things. + +0.8.29 (000323) ++ stable(?) release + + +0.8.28 (000323) ++ removed alarm as it was causing server to go down ++ removed debugging ++ break down dhcpd.c into manageable files + + +0.8.27 (000221) ++ OFFER also sends gateway/subnet (for picky dhcp clients) ++ multiple DNS now handled from resolv.conf if available ++ multiple WINS (from dhcpd.conf) + +0.8.25 (000120) ++ now compiles *and* runs on a generic linux system + tested with a windows 98 client and the sample config + files in the samples directory. + +0.8.24 (000117) ++ makeiplist tool has basic functionality in place ++ new sample config files ++ route add -host 255.255.255.255 dev eth0 added for generic linux + +0.8.23 (000117) ++ NETtel specific fix for ignoring dhcp requests on 2nd interface + +0.8.22 (000113) ++ minor changes to compile under a generic linux system ++ minor config file location changes for a generic linux system ++ makeiplist fixes.. still incomplete.. but etting closer + +0.8.21 (000113) ++ now sends the correct server ip instead of hardcoded value ++ minor debugging fixes for critical messages + +0.8.20 (000106) ++ cut out dhcp server checking.. this was causing dialout ppp + sessions with idle time set to never time out. ++ also removed the 10 second pause before launching.. as this + was originally to stop it replying to a dhcp client + on a NETtel which was really a bad way to do it in the + first place :-) + +0.8.19 (000104) ++ fixes for route add -host on a machine that needs to run both + a DHCP client and server (dual eth box) + +0.8.18 (991220) + ++ Race conditions fixed by disabling alarm whilst the server is busy ++ Fixed continous clearing of the offered array so that it is only cleared + when it is dirty - (could change the position of when dirty is set) + +0.8.17 (991212) + +- has problems clearing out the offered array + +0.8.16 (991203) ++ Non blocking error is changes to informational as it is not really + an error + +0.8.15 (991129) ++ Servs the dns field 3 times (Nettel only) so that windows servers + dont time out whilst nettel is booting + +0.8.14 (991126) ++ added owner check for the offered array so clean out time may be + increased ++ added new func to print out chadder/MAC + +0.8.13 (991125) ++ added win95 support (w95 changed xid halfway through conversation) ++ had to change the offered array to use hardware addresses instead of xid ++ fixed re offered bug ++ added more debugging + +0.8.12 (991111) ++ debugging was real bad.. cleaned up a bit.. needs overhaul + + +0.8.11 (991110) ++ fixed up offeredAddr array to actually be used now!! offeredAddr is + used to see if another simultaneous connecting client was offered + an address that we are about to offer another client (multiple + client bug) ++ removed re_offered variable as it breaks multiple client support ++ added lease time to ACK -- doesn't work if in OFFER ++ decreased internal array clear delay to 60 seconds ++ minor findAddr bug (returning -1 instead of 0) ++ if clients xid already in offeredAddr offer the same addr and don't add a + new addr to offered (caused by a client issuing multiple DISCOVERs) + +0.8.10 (991105) ++ \n bug in arpping ++ minor debugging changes (removed printfs etc) ++ started browseiplist (not finished) + +0.8.9 (19991105) ++ fixed options array size bug (options were cut off) + +0.8.8 (19991105) ++ ignores requests from dhcpcd on the same machine + +0.8.7 (19991104) ++ don't die if we can't bind to search for existing DHCP server ++ slightly more verbose syslogging + +0.8.6 (19991103) ++ added makeiplist (not finished -- core dumps) ++ minor debug changes + +0.8.5 (19991029) ++ exits if another DHCP server is already on the network ++ added Linux Makefile + +0.8.4 (19991026) ++ minor bug fix in findaddr preventing an addr being found + +0.8.3 (19991025) ++ fixed up debugging ++ minor hwaddr issues + +0.8.2 (19991022) ++ free leases (new arpping code from dhcpcd) ++ fixed bug where crashes if no leases/iplist file ++ syslogging and debugging switch ++ serve DNS from resolv.conf ++ fixed bug where new lease added if same mac offered ++ now checks the ip is free b4 offering ++ now supports wins server + diff --git a/busybox/networking/udhcp/Config.in b/busybox/networking/udhcp/Config.in new file mode 100644 index 000000000..fc07a9b7f --- /dev/null +++ b/busybox/networking/udhcp/Config.in @@ -0,0 +1,62 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "udhcp Server/Client" + +config CONFIG_UDHCPD + bool "udhcp Server (udhcpd)" + default n + help + uDHCPd is a DHCP server geared primarily toward embedded systems, + while striving to be fully functional and RFC compliant. + + See http://udhcp.busybox.net for further details. + +config CONFIG_UDHCPC + bool "udhcp Client (udhcpc)" + default n + help + uDHCPc is a DHCP client geared primarily toward embedded systems, + while striving to be fully functional and RFC compliant. + + The udhcp client negotiates a lease with the DHCP server and + notifies a set of scripts when a lease is obtained or lost. + + See http://udhcp.busybox.net for further details. + +config CONFIG_DUMPLEASES + bool "Lease display utility (dumpleases)" + default n + depends on CONFIG_UDHCPD + help + dumpleases displays the leases written out by the udhcpd server. + Lease times are stored in the file by time remaining in lease, or + by the absolute time that it expires in seconds from epoch. + + See http://udhcp.busybox.net for further details. + +config CONFIG_FEATURE_UDHCP_SYSLOG + bool " Log udhcp messages to syslog (instead of stdout)" + default n + depends on CONFIG_UDHCPD || CONFIG_UDHCPC + help + If selected, udhcpd will log all its messages to syslog, otherwise, + it will attempt to log them to stdout. + + See http://udhcp.busybox.net for further details. + +config CONFIG_FEATURE_UDHCP_DEBUG + bool " Compile udhcp with noisy debugging messages" + default n + depends on CONFIG_UDHCPD || CONFIG_UDHCPC + help + If selected, udhcpd will output extra debugging output. If using + this option, compile uDHCP with "-g", and do not fork the daemon to + the background. + + See http://udhcp.busybox.net for further details. + +endmenu + diff --git a/busybox/networking/udhcp/Makefile b/busybox/networking/udhcp/Makefile new file mode 100644 index 000000000..3d32db50a --- /dev/null +++ b/busybox/networking/udhcp/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=../.. +top_builddir=../.. +srcdir=$(top_srcdir)/networking/udhcp +UDHCP_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/networking/udhcp/Makefile.in b/busybox/networking/udhcp/Makefile.in new file mode 100644 index 000000000..94750f693 --- /dev/null +++ b/busybox/networking/udhcp/Makefile.in @@ -0,0 +1,54 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +UDHCP_AR:=udhcp.a +ifndef $(UDHCP_DIR) +UDHCP_DIR:=$(top_builddir)/networking/udhcp/ +endif +srcdir=$(top_srcdir)/networking/udhcp + +#ok, so I forgot how to do an or, but this is a quick and dirty hack +ifeq ($(CONFIG_UDHCPC), y) +CONFIG_UDHCP_SHARED=y +else +ifeq ($(CONFIG_UDHCPD), y) +CONFIG_UDHCP_SHARED=y +else +CONFIG_UDHCP_SHARED=n +endif +endif + +UDHCP-y:= +UDHCP-$(CONFIG_UDHCP_SHARED) += common.c options.c packet.c pidfile.c \ + signalpipe.c socket.c +UDHCP-$(CONFIG_UDHCPC) += dhcpc.c clientpacket.c clientsocket.c \ + script.c +UDHCP-$(CONFIG_UDHCPD) += dhcpd.c arpping.c files.c leases.c \ + serverpacket.c static_leases.c +UDHCP-$(CONFIG_DUMPLEASES) += dumpleases.c +UDHCP_OBJS=$(patsubst %.c,$(UDHCP_DIR)%.o, $(UDHCP-y)) + +libraries-y+=$(UDHCP_DIR)$(UDHCP_AR) + +$(UDHCP_DIR)$(UDHCP_AR): $(UDHCP_OBJS) + $(AR) -ro $@ $(UDHCP_OBJS) + +$(UDHCP_OBJS): $(UDHCP_DIR)%.o : $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DIN_BUSYBOX -c $< -o $@ + diff --git a/busybox/networking/udhcp/README b/busybox/networking/udhcp/README new file mode 100644 index 000000000..dd992949a --- /dev/null +++ b/busybox/networking/udhcp/README @@ -0,0 +1,53 @@ +udhcp server/client package readme +------------------------- + +The udhcp server/client package is primarily geared towards embedded +systems. It does however, strive to be fully functional, and RFC +compliant. + + +compile time options +------------------- + +The Makefile contains three of the compile time options: + + UDHCP_DEBUG: If UDHCP_DEBUG is defined, udhcpd will output extra + debugging output, compile with -g, and not fork to the background when + run. + UDHCP_SYSLOG: If UDHCP_SYSLOG is defined, udhcpd will log all its + messages syslog, otherwise, it will attempt to log them to stdout. + + COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd, + is created. If called as udhcpd, the dhcp server will be started. + If called as udhcpc, the dhcp client will be started. + +dhcpd.h contains the other three compile time options: + + LEASE_TIME: The default lease time if not specified in the config + file. + + LEASES_FILE: The default file for storing leases. + + DHCPD_CONFIG_FILE: The defualt config file to use. + +options.c contains a set of dhcp options for the client: + + name[10]: The name of the option as it will appear in scripts + + flags: The type of option, as well as if it will be requested + by the client (OPTION_REQ) + + code: The DHCP code for this option + + +busybox drop-in +-------------- +udhcp is now a drop-in component for busybox (http://busybox.net). +To update busybox to the latest revision, simply do a: + +cp *.[ch] README AUTHORS COPYING ChangeLog TODO \ + /networking/udhcp + +The only two files udhcp does not provide are config.in and +Makefile.in, so these may need to be updated from time to time. + diff --git a/busybox/networking/udhcp/README.dumpleases b/busybox/networking/udhcp/README.dumpleases new file mode 100644 index 000000000..6367710c4 --- /dev/null +++ b/busybox/networking/udhcp/README.dumpleases @@ -0,0 +1,17 @@ +udhcp lease dump (dumpleases) +---------------------------- + +dumpleases displays the leases written out by the udhcpd server. Lease +times are stored in the file by time remaining in lease (for systems +without clock that works when there is no power), or by the absolute +time that it expires in seconds from epoch. dumpleases accepts the +following command line options: + +-a, --absolute Interpret lease times as expiration time. +-r, --remaining Interpret lease times as remaining time. +-f, --file=FILE Read lease information from FILE. +-h, --help Display help. + +Note that if udhcpd has not written a leases file recently, the output +of may not be up to date. + diff --git a/busybox/networking/udhcp/README.udhcpc b/busybox/networking/udhcp/README.udhcpc new file mode 100644 index 000000000..d720a37cf --- /dev/null +++ b/busybox/networking/udhcp/README.udhcpc @@ -0,0 +1,141 @@ +udhcp client (udhcpc) +-------------------- + +The udhcp client negotiates a lease with the DHCP server and notifies +a set of scripts when a leases is obtained or lost. + + +command line options +------------------- + +The command line options for the udhcp client are: + +-c, --clientid=CLIENTID Client identifier +-H, --hostname=HOSTNAME Client hostname +-h, Alias for -H +-f, --foreground Do not fork after getting lease +-b, --background Fork to background if lease cannot be + immediately negotiated. +-i, --interface=INTERFACE Interface to use (default: eth0) +-n, --now Exit with failure if lease cannot be + immediately negotiated. +-p, --pidfile=file Store process ID of daemon in file +-q, --quit Quit after obtaining lease +-r, --request=IP IP address to request (default: none) +-s, --script=file Run file at dhcp events (default: + /usr/share/udhcpc/default.script) +-v, --version Display version + + +If the requested IP address cannot be obtained, the client accepts the +address that the server offers. + + +udhcp client scripts +------------------- + +When an event occurs, udhcpc calls the action script. udhcpc never does +any configuration of the network interface itself, but instead relies on +a set of scripts. The script by default is +/usr/share/udhcpc/default.script but this can be changed via the command +line arguments. The three possible arguments to the script are: + + deconfig: This argument is used when udhcpc starts, and + when a leases is lost. The script must put the interface in an + up, but deconfigured state, ie: ifconfig $interface 0.0.0.0. + + bound: This argument is used when udhcpc moves from an + unbound, to a bound state. All of the paramaters are set in + enviromental variables, The script should configure the interface, + and set any other relavent parameters (default gateway, dns server, + etc). + + renew: This argument is used when a DHCP lease is renewed. All of + the paramaters are set in enviromental variables. This argument is + used when the interface is already configured, so the IP address, + will not change, however, the other DHCP paramaters, such as the + default gateway, subnet mask, and dns server may change. + + nak: This argument is used with udhcpc receives a NAK message. + The script with the deconfig argument will be called directly + afterwards, so no changes to the network interface are neccessary. + This hook is provided for purely informational purposes (the + message option may contain a reason for the NAK). + +The paramaters for enviromental variables are as follows: + + $HOME - The set $HOME env or "/" + $PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin" + $1 - What action the script should perform + interface - The interface this was obtained on + ip - The obtained IP + mask - The number of bits in the netmask (ie: 24) + siaddr - The bootp next server option + sname - The bootp server name option + boot_file - The bootp boot file option + subnet - The assigend subnet mask + timezone - Offset in seconds from UTC + router - A list of routers + timesvr - A list of time servers + namesvr - A list of IEN 116 name servers + dns - A list of DNS server + logsvr - A list of MIT-LCS UDP log servers + cookiesvr - A list of RFC 865 cookie servers + lprsvr - A list of LPR servers + hostname - The assigned hostname + bootsize - The length in 512 octect blocks of the bootfile + domain - The domain name of the network + swapsvr - The IP address of the client's swap server + rootpath - The path name of the client's root disk + ipttl - The TTL to use for this network + mtu - The MTU to use for this network + broadcast - The broadcast address for this network + ntpsrv - A list of NTP servers + wins - A list of WINS servers + lease - The lease time, in seconds + dhcptype - DHCP message type (safely ignored) + serverid - The IP of the server + message - Reason for a DHCPNAK + tftp - The TFTP server name + bootfile - The bootfile name + +additional options are easily added in options.c. + + +note on udhcpc's random seed +--------------------------- + +udhcpc will seed its random number generator (used for generating xid's) +by reading /dev/urandom. If you have a lot of embedded systems on the same +network, with no entropy, you can either seed /dev/urandom by a method of +your own, or doing the following on startup: + +ifconfig eth0 > /dev/urandom + +in order to seed /dev/urandom with some data (mac address) unique to your +system. If reading /dev/urandom fails, udhcpc will fall back to its old +behavior of seeding with time(0). + + +signals accepted by udhcpc +------------------------- + +udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state, +and SIGUSR2 will force a release of the current lease, and cause udhcpc to +go into an inactive state (until it is killed, or receives a SIGUSR1). You do +not need to sleep between sending signals, as signals received are processed +sequencially in the order they are received. + + +compile time options +------------------- + +options.c contains a set of dhcp options for the client: + + name[10]: The name of the option as it will appear in scripts + + flags: The type of option, as well as if it will be requested + by the client (OPTION_REQ) + + code: The DHCP code for this option + diff --git a/busybox/networking/udhcp/README.udhcpd b/busybox/networking/udhcp/README.udhcpd new file mode 100644 index 000000000..169de78ec --- /dev/null +++ b/busybox/networking/udhcp/README.udhcpd @@ -0,0 +1,59 @@ +udhcp server (udhcpd) +-------------------- + +The only command line argument to udhcpd is an optional specifed +config file. If no config file is specified, udhcpd uses the default +config file, /etc/udhcpd.conf. Ex: + +udhcpd /etc/udhcpd.eth1.conf + +The udhcp server employs a number of simple config files: + +udhcpd.leases +------------ + +The udhcpd.leases behavior is designed for an embedded system. The +file is written either every auto_time seconds, or when a SIGUSR1 +is received (the auto_time timer restarts if a SIGUSR1 is received). +If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will +finish writing the leases file and wait for the aftermentioned script +to be executed and finish before quiting, so you do not need to sleep +between sending signals. When the file is written, a script can be +optionally called to commit the file to flash. Lease times are stored +in the file by time remaining in lease (for systems without clock +that works when there is no power), or by the absolute time that it +expires in seconds from epoch. In the remaining format, expired leases +are stored as zero. The file is of the format: + +16 byte MAC +4 byte ip address +u32 expire time +16 byte MAC +4 byte ip address +u32 expire time +. +etc. + +example: hexdump udhcpd.leases + +0000000 1000 c95a 27d9 0000 0000 0000 0000 0000 +0000010 a8c0 150a 0d00 2d29 5000 23fc 8566 0000 +0000020 0000 0000 0000 0000 a8c0 140a 0d00 4e29 +0000030 + + +udhcpd.conf +---------- + +The format is fairly simple, there is a sample file with all the +available options and comments describing them in samples/udhcpd.conf + +compile time options +------------------- + +dhcpd.h contains the other two compile time options: + + LEASE_TIME: The default lease time if not specified in the config + file. + + DHCPD_CONFIG_FILE: The defualt config file to use. diff --git a/busybox/networking/udhcp/TODO b/busybox/networking/udhcp/TODO new file mode 100644 index 000000000..6febe5ab4 --- /dev/null +++ b/busybox/networking/udhcp/TODO @@ -0,0 +1,16 @@ +TODO +---- ++ Check for valid IP, netmask, hostname, paths, strings, etc ++ Integrade README.*'s with manpages ++ using time(0) breaks if the system clock changes, find a portable solution ++ make failure of reading functions revert to previous value, not the default ++ sanity code for option[OPT_LEN] ++ fix aliasing (ie: eth0:0) ++ better standard linux distro support ++ make sure packet generation works on a wide varitey of arches ++ Interoperability testing ++ Hooks within the DHCP server + * Server notification when a lease is added/removed ++ Additional bootp support in client/server ++ Make serverid option in server configurable ++ Possibly add failure message to DHCP NAK diff --git a/busybox/networking/udhcp/arpping.c b/busybox/networking/udhcp/arpping.c new file mode 100644 index 000000000..7cc2be42e --- /dev/null +++ b/busybox/networking/udhcp/arpping.c @@ -0,0 +1,106 @@ +/* + * arpping.c + * + * Mostly stolen from: dhcpcd - DHCP client daemon + * by Yoichi Hariguchi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcpd.h" +#include "arpping.h" +#include "common.h" + +/* args: yiaddr - what IP to ping + * ip - our ip + * mac - our arp address + * interface - interface to use + * retn: 1 addr free + * 0 addr used + * -1 error + */ + +/* FIXME: match response against chaddr */ +int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface) +{ + + int timeout = 2; + int optval = 1; + int s; /* socket */ + int rv = 1; /* return value */ + struct sockaddr addr; /* for interface name */ + struct arpMsg arp; + fd_set fdset; + struct timeval tm; + time_t prevTime; + + + if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) { +#ifdef IN_BUSYBOX + LOG(LOG_ERR, bb_msg_can_not_create_raw_socket); +#else + LOG(LOG_ERR, "Could not open raw socket"); +#endif + return -1; + } + + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { + LOG(LOG_ERR, "Could not setsocketopt on raw socket"); + close(s); + return -1; + } + + /* send arp request */ + memset(&arp, 0, sizeof(arp)); + memcpy(arp.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */ + memcpy(arp.h_source, mac, 6); /* MAC SA */ + arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ + arp.htype = htons(ARPHRD_ETHER); /* hardware type */ + arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ + arp.hlen = 6; /* hardware address length */ + arp.plen = 4; /* protocol address length */ + arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ + memcpy(arp.sInaddr, &ip, sizeof(ip)); /* source IP address */ + memcpy(arp.sHaddr, mac, 6); /* source hardware address */ + memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr)); /* target IP address */ + + memset(&addr, 0, sizeof(addr)); + strcpy(addr.sa_data, interface); + if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) + rv = 0; + + /* wait arp reply, and check it */ + tm.tv_usec = 0; + prevTime = uptime(); + while (timeout > 0) { + FD_ZERO(&fdset); + FD_SET(s, &fdset); + tm.tv_sec = timeout; + if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) { + DEBUG(LOG_ERR, "Error on ARPING request: %m"); + if (errno != EINTR) rv = 0; + } else if (FD_ISSET(s, &fdset)) { + if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0; + if (arp.operation == htons(ARPOP_REPLY) && + bcmp(arp.tHaddr, mac, 6) == 0 && + *((uint32_t *) arp.sInaddr) == yiaddr) { + DEBUG(LOG_INFO, "Valid arp reply receved for this address"); + rv = 0; + break; + } + } + timeout -= uptime() - prevTime; + prevTime = uptime(); + } + close(s); + DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V"); + return rv; +} diff --git a/busybox/networking/udhcp/arpping.h b/busybox/networking/udhcp/arpping.h new file mode 100644 index 000000000..6f27d9f75 --- /dev/null +++ b/busybox/networking/udhcp/arpping.h @@ -0,0 +1,35 @@ +/* + * arpping .h + */ + +#ifndef ARPPING_H +#define ARPPING_H + +#include +#include +#include +#include + +struct arpMsg { + /* Ethernet header */ + u_char h_dest[6]; /* destination ether addr */ + u_char h_source[6]; /* source ether addr */ + u_short h_proto; /* packet type ID field */ + + /* ARP packet */ + uint16_t htype; /* hardware type (must be ARPHRD_ETHER) */ + uint16_t ptype; /* protocol type (must be ETH_P_IP) */ + uint8_t hlen; /* hardware address length (must be 6) */ + uint8_t plen; /* protocol address length (must be 4) */ + uint16_t operation; /* ARP opcode */ + uint8_t sHaddr[6]; /* sender's hardware address */ + uint8_t sInaddr[4]; /* sender's IP address */ + uint8_t tHaddr[6]; /* target's hardware address */ + uint8_t tInaddr[4]; /* target's IP address */ + uint8_t pad[18]; /* pad for min. Ethernet payload (60 bytes) */ +} __attribute__ ((packed)); + +/* function prototypes */ +int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface); + +#endif diff --git a/busybox/networking/udhcp/clientpacket.c b/busybox/networking/udhcp/clientpacket.c new file mode 100644 index 000000000..ec96601cb --- /dev/null +++ b/busybox/networking/udhcp/clientpacket.c @@ -0,0 +1,248 @@ +/* clientpacket.c + * + * Packet generation and dispatching functions for the DHCP client. + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#include +#else +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + + +#include "dhcpd.h" +#include "clientpacket.h" +#include "options.h" +#include "dhcpc.h" +#include "common.h" + + +/* Create a random xid */ +unsigned long random_xid(void) +{ + static int initialized; + if (!initialized) { + int fd; + unsigned long seed; + + fd = open("/dev/urandom", 0); + if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) { + LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %m"); + seed = time(0); + } + if (fd >= 0) close(fd); + srand(seed); + initialized++; + } + return rand(); +} + + +/* initialize a packet with the proper defaults */ +static void init_packet(struct dhcpMessage *packet, char type) +{ + struct vendor { + char vendor, length; + char str[sizeof("udhcp "VERSION)]; + } vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION}; + + init_header(packet, type); + memcpy(packet->chaddr, client_config.arp, 6); + add_option_string(packet->options, client_config.clientid); + if (client_config.hostname) add_option_string(packet->options, client_config.hostname); + add_option_string(packet->options, (uint8_t *) &vendor_id); +} + + +/* Add a parameter request list for stubborn DHCP servers. Pull the data + * from the struct in options.c. Don't do bounds checking here because it + * goes towards the head of the packet. */ +static void add_requests(struct dhcpMessage *packet) +{ + int end = end_option(packet->options); + int i, len = 0; + + packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; + for (i = 0; dhcp_options[i].code; i++) + if (dhcp_options[i].flags & OPTION_REQ) + packet->options[end + OPT_DATA + len++] = dhcp_options[i].code; + packet->options[end + OPT_LEN] = len; + packet->options[end + OPT_DATA + len] = DHCP_END; + +} + + +/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ +int send_discover(unsigned long xid, unsigned long requested) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPDISCOVER); + packet.xid = xid; + if (requested) + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + + add_requests(&packet); + LOG(LOG_DEBUG, "Sending discover..."); + return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); +} + + +/* Broadcasts a DHCP request message */ +int send_selecting(unsigned long xid, unsigned long server, unsigned long requested) +{ + struct dhcpMessage packet; + struct in_addr addr; + + init_packet(&packet, DHCPREQUEST); + packet.xid = xid; + + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + add_requests(&packet); + addr.s_addr = requested; + LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr)); + return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); +} + + +/* Unicasts or broadcasts a DHCP renew message */ +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr) +{ + struct dhcpMessage packet; + int ret = 0; + + init_packet(&packet, DHCPREQUEST); + packet.xid = xid; + packet.ciaddr = ciaddr; + + add_requests(&packet); + LOG(LOG_DEBUG, "Sending renew..."); + if (server) + ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); + else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); + return ret; +} + + +/* Unicasts a DHCP release message */ +int send_release(unsigned long server, unsigned long ciaddr) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPRELEASE); + packet.xid = random_xid(); + packet.ciaddr = ciaddr; + + add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + LOG(LOG_DEBUG, "Sending release..."); + return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); +} + + +/* return -1 on errors that are fatal for the socket, -2 for those that aren't */ +int get_raw_packet(struct dhcpMessage *payload, int fd) +{ + int bytes; + struct udp_dhcp_packet packet; + uint32_t source, dest; + uint16_t check; + + memset(&packet, 0, sizeof(struct udp_dhcp_packet)); + bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); + if (bytes < 0) { + DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring"); + usleep(500000); /* possible down interface, looping condition */ + return -1; + } + + if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { + DEBUG(LOG_INFO, "message too short, ignoring"); + return -2; + } + + if (bytes < ntohs(packet.ip.tot_len)) { + DEBUG(LOG_INFO, "Truncated packet"); + return -2; + } + + /* ignore any extra garbage bytes */ + bytes = ntohs(packet.ip.tot_len); + + /* Make sure its the right packet for us, and that it passes sanity checks */ + if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION || + packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) || + bytes > (int) sizeof(struct udp_dhcp_packet) || + ntohs(packet.udp.len) != (uint16_t) (bytes - sizeof(packet.ip))) { + DEBUG(LOG_INFO, "unrelated/bogus packet"); + return -2; + } + + /* check IP checksum */ + check = packet.ip.check; + packet.ip.check = 0; + if (check != checksum(&(packet.ip), sizeof(packet.ip))) { + DEBUG(LOG_INFO, "bad IP header checksum, ignoring"); + return -1; + } + + /* verify the UDP checksum by replacing the header with a psuedo header */ + source = packet.ip.saddr; + dest = packet.ip.daddr; + check = packet.udp.check; + packet.udp.check = 0; + memset(&packet.ip, 0, sizeof(packet.ip)); + + packet.ip.protocol = IPPROTO_UDP; + packet.ip.saddr = source; + packet.ip.daddr = dest; + packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ + if (check && check != checksum(&packet, bytes)) { + DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring"); + return -2; + } + + memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp))); + + if (ntohl(payload->cookie) != DHCP_MAGIC) { + LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring"); + return -2; + } + DEBUG(LOG_INFO, "oooooh!!! got some!"); + return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); + +} diff --git a/busybox/networking/udhcp/clientpacket.h b/busybox/networking/udhcp/clientpacket.h new file mode 100644 index 000000000..8e5441bc7 --- /dev/null +++ b/busybox/networking/udhcp/clientpacket.h @@ -0,0 +1,14 @@ +#ifndef _CLIENTPACKET_H +#define _CLIENTPACKET_H + +#include "packet.h" + +unsigned long random_xid(void); +int send_discover(unsigned long xid, unsigned long requested); +int send_selecting(unsigned long xid, unsigned long server, unsigned long requested); +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); +int send_release(unsigned long server, unsigned long ciaddr); +int get_raw_packet(struct dhcpMessage *payload, int fd); + +#endif diff --git a/busybox/networking/udhcp/clientsocket.c b/busybox/networking/udhcp/clientsocket.c new file mode 100644 index 000000000..7c1b6e87c --- /dev/null +++ b/busybox/networking/udhcp/clientsocket.c @@ -0,0 +1,62 @@ +/* + * clientsocket.c -- DHCP client socket creation + * + * udhcp client + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#include +#else +#include +#include +#include +#endif + +#include "clientsocket.h" +#include "common.h" + + +int raw_socket(int ifindex) +{ + int fd; + struct sockaddr_ll sock; + + DEBUG(LOG_INFO, "Opening raw socket on ifindex %d", ifindex); + if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { + DEBUG(LOG_ERR, "socket call failed: %m"); + return -1; + } + + sock.sll_family = AF_PACKET; + sock.sll_protocol = htons(ETH_P_IP); + sock.sll_ifindex = ifindex; + if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) { + DEBUG(LOG_ERR, "bind call failed: %m"); + close(fd); + return -1; + } + + return fd; +} diff --git a/busybox/networking/udhcp/clientsocket.h b/busybox/networking/udhcp/clientsocket.h new file mode 100644 index 000000000..17a55c154 --- /dev/null +++ b/busybox/networking/udhcp/clientsocket.h @@ -0,0 +1,7 @@ +/* clientsocket.h */ +#ifndef _CLIENTSOCKET_H +#define _CLIENTSOCKET_H + +int raw_socket(int ifindex); + +#endif diff --git a/busybox/networking/udhcp/common.c b/busybox/networking/udhcp/common.c new file mode 100644 index 000000000..bf2ac4417 --- /dev/null +++ b/busybox/networking/udhcp/common.c @@ -0,0 +1,162 @@ +/* common.c + * + * Functions for debugging and logging as well as some other + * simple helper functions. + * + * Russ Dill 2001-2003 + * Rewritten by Vladimir Oleynik (C) 2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "pidfile.h" + + +static int daemonized; + +long uptime(void) +{ + struct sysinfo info; + sysinfo(&info); + return info.uptime; +} + + +/* + * This function makes sure our first socket calls + * aren't going to fd 1 (printf badness...) and are + * not later closed by daemon() + */ +static inline void sanitize_fds(void) +{ + int zero; + if ((zero = open(_PATH_DEVNULL, O_RDWR, 0)) < 0) return; + while (zero < 3) zero = dup(zero); + close(zero); +} + + +void background(const char *pidfile) +{ +#ifdef __uClinux__ + LOG(LOG_ERR, "Cannot background in uclinux (yet)"); +#else /* __uClinux__ */ + int pid_fd; + + /* hold lock during fork. */ + pid_fd = pidfile_acquire(pidfile); + if (daemon(0, 0) == -1) { + perror("fork"); + exit(1); + } + daemonized++; + pidfile_write_release(pid_fd); +#endif /* __uClinux__ */ +} + + +#ifdef UDHCP_SYSLOG +void udhcp_logging(int level, const char *fmt, ...) +{ + va_list p; + va_list p2; + + va_start(p, fmt); + __va_copy(p2, p); + if(!daemonized) { + vprintf(fmt, p); + putchar('\n'); + } + vsyslog(level, fmt, p2); + va_end(p); +} + + +void start_log_and_pid(const char *client_server, const char *pidfile) +{ + int pid_fd; + + /* Make sure our syslog fd isn't overwritten */ + sanitize_fds(); + + /* do some other misc startup stuff while we are here to save bytes */ + pid_fd = pidfile_acquire(pidfile); + pidfile_write_release(pid_fd); + + /* equivelent of doing a fflush after every \n */ + setlinebuf(stdout); + + openlog(client_server, LOG_PID | LOG_CONS, LOG_LOCAL0); + udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION); +} + + +#else + + +static char *syslog_level_msg[] = { + [LOG_EMERG] = "EMERGENCY!", + [LOG_ALERT] = "ALERT!", + [LOG_CRIT] = "critical!", + [LOG_WARNING] = "warning", + [LOG_ERR] = "error", + [LOG_INFO] = "info", + [LOG_DEBUG] = "debug" +}; + + +void udhcp_logging(int level, const char *fmt, ...) +{ + va_list p; + + va_start(p, fmt); + if(!daemonized) { + printf("%s, ", syslog_level_msg[level]); + vprintf(fmt, p); + putchar('\n'); + } + va_end(p); +} + + +void start_log_and_pid(const char *client_server, const char *pidfile) +{ + int pid_fd; + + /* Make sure our syslog fd isn't overwritten */ + sanitize_fds(); + + /* do some other misc startup stuff while we are here to save bytes */ + pid_fd = pidfile_acquire(pidfile); + pidfile_write_release(pid_fd); + + /* equivelent of doing a fflush after every \n */ + setlinebuf(stdout); + + udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION); +} +#endif + diff --git a/busybox/networking/udhcp/common.h b/busybox/networking/udhcp/common.h new file mode 100644 index 000000000..ca19a2497 --- /dev/null +++ b/busybox/networking/udhcp/common.h @@ -0,0 +1,56 @@ +/* common.h + * + * Russ Dill September 2001 + * Rewritten by Vladimir Oleynik (C) 2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _COMMON_H +#define _COMMON_H + +#include "version.h" +#include "libbb_udhcp.h" + + +#ifndef UDHCP_SYSLOG +enum syslog_levels { + LOG_EMERG = 0, + LOG_ALERT, + LOG_CRIT, + LOG_WARNING, + LOG_ERR, + LOG_INFO, + LOG_DEBUG +}; +#else +#include +#endif + +long uptime(void); +void background(const char *pidfile); +void start_log_and_pid(const char *client_server, const char *pidfile); +void background(const char *pidfile); +void udhcp_logging(int level, const char *fmt, ...); + +#define LOG(level, str, args...) udhcp_logging(level, str, ## args) + +#ifdef UDHCP_DEBUG +# define DEBUG(level, str, args...) LOG(level, str, ## args) +#else +# define DEBUG(level, str, args...) do {;} while(0) +#endif + +#endif diff --git a/busybox/networking/udhcp/dhcpc.c b/busybox/networking/udhcp/dhcpc.c new file mode 100644 index 000000000..449b51763 --- /dev/null +++ b/busybox/networking/udhcp/dhcpc.c @@ -0,0 +1,517 @@ +/* dhcpc.c + * + * udhcp DHCP client + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcpd.h" +#include "dhcpc.h" +#include "options.h" +#include "clientpacket.h" +#include "clientsocket.h" +#include "script.h" +#include "socket.h" +#include "common.h" +#include "signalpipe.h" + +static int state; +static unsigned long requested_ip; /* = 0 */ +static unsigned long server_addr; +static unsigned long timeout; +static int packet_num; /* = 0 */ +static int fd = -1; + +#define LISTEN_NONE 0 +#define LISTEN_KERNEL 1 +#define LISTEN_RAW 2 +static int listen_mode; + +struct client_config_t client_config = { + /* Default options. */ + abort_if_no_lease: 0, + foreground: 0, + quit_after_lease: 0, + background_if_no_lease: 0, + interface: "eth0", + pidfile: NULL, + script: DEFAULT_SCRIPT, + clientid: NULL, + hostname: NULL, + ifindex: 0, + arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */ +}; + +#ifndef IN_BUSYBOX +static void __attribute__ ((noreturn)) show_usage(void) +{ + printf( +"Usage: udhcpc [OPTIONS]\n\n" +" -c, --clientid=CLIENTID Client identifier\n" +" -H, --hostname=HOSTNAME Client hostname\n" +" -h Alias for -H\n" +" -f, --foreground Do not fork after getting lease\n" +" -b, --background Fork to background if lease cannot be\n" +" immediately negotiated.\n" +" -i, --interface=INTERFACE Interface to use (default: eth0)\n" +" -n, --now Exit with failure if lease cannot be\n" +" immediately negotiated.\n" +" -p, --pidfile=file Store process ID of daemon in file\n" +" -q, --quit Quit after obtaining lease\n" +" -r, --request=IP IP address to request (default: none)\n" +" -s, --script=file Run file at dhcp events (default:\n" +" " DEFAULT_SCRIPT ")\n" +" -v, --version Display version\n" + ); + exit(0); +} +#else +#define show_usage bb_show_usage +extern void show_usage(void) __attribute__ ((noreturn)); +#endif + + +/* just a little helper */ +static void change_mode(int new_mode) +{ + DEBUG(LOG_INFO, "entering %s listen mode", + new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); + if (fd >= 0) close(fd); + fd = -1; + listen_mode = new_mode; +} + + +/* perform a renew */ +static void perform_renew(void) +{ + LOG(LOG_INFO, "Performing a DHCP renew"); + switch (state) { + case BOUND: + change_mode(LISTEN_KERNEL); + case RENEWING: + case REBINDING: + state = RENEW_REQUESTED; + break; + case RENEW_REQUESTED: /* impatient are we? fine, square 1 */ + run_script(NULL, "deconfig"); + case REQUESTING: + case RELEASED: + change_mode(LISTEN_RAW); + state = INIT_SELECTING; + break; + case INIT_SELECTING: + break; + } + + /* start things over */ + packet_num = 0; + + /* Kill any timeouts because the user wants this to hurry along */ + timeout = 0; +} + + +/* perform a release */ +static void perform_release(void) +{ + char buffer[16]; + struct in_addr temp_addr; + + /* send release packet */ + if (state == BOUND || state == RENEWING || state == REBINDING) { + temp_addr.s_addr = server_addr; + sprintf(buffer, "%s", inet_ntoa(temp_addr)); + temp_addr.s_addr = requested_ip; + LOG(LOG_INFO, "Unicasting a release of %s to %s", + inet_ntoa(temp_addr), buffer); + send_release(server_addr, requested_ip); /* unicast */ + run_script(NULL, "deconfig"); + } + LOG(LOG_INFO, "Entering released state"); + + change_mode(LISTEN_NONE); + state = RELEASED; + timeout = 0x7fffffff; +} + + +static void client_background(void) +{ + background(client_config.pidfile); + client_config.foreground = 1; /* Do not fork again. */ + client_config.background_if_no_lease = 0; +} + + +#ifdef COMBINED_BINARY +int udhcpc_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + uint8_t *temp, *message; + unsigned long t1 = 0, t2 = 0, xid = 0; + unsigned long start = 0, lease; + fd_set rfds; + int retval; + struct timeval tv; + int c, len; + struct dhcpMessage packet; + struct in_addr temp_addr; + long now; + int max_fd; + int sig; + + static const struct option arg_options[] = { + {"clientid", required_argument, 0, 'c'}, + {"foreground", no_argument, 0, 'f'}, + {"background", no_argument, 0, 'b'}, + {"hostname", required_argument, 0, 'H'}, + {"hostname", required_argument, 0, 'h'}, + {"interface", required_argument, 0, 'i'}, + {"now", no_argument, 0, 'n'}, + {"pidfile", required_argument, 0, 'p'}, + {"quit", no_argument, 0, 'q'}, + {"request", required_argument, 0, 'r'}, + {"script", required_argument, 0, 's'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + + /* get options */ + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index); + if (c == -1) break; + + switch (c) { + case 'c': + len = strlen(optarg) > 255 ? 255 : strlen(optarg); + if (client_config.clientid) free(client_config.clientid); + client_config.clientid = xmalloc(len + 2); + client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; + client_config.clientid[OPT_LEN] = len; + client_config.clientid[OPT_DATA] = '\0'; + strncpy(client_config.clientid + OPT_DATA, optarg, len); + break; + case 'f': + client_config.foreground = 1; + break; + case 'b': + client_config.background_if_no_lease = 1; + break; + case 'h': + case 'H': + len = strlen(optarg) > 255 ? 255 : strlen(optarg); + if (client_config.hostname) free(client_config.hostname); + client_config.hostname = xmalloc(len + 2); + client_config.hostname[OPT_CODE] = DHCP_HOST_NAME; + client_config.hostname[OPT_LEN] = len; + strncpy(client_config.hostname + 2, optarg, len); + break; + case 'i': + client_config.interface = optarg; + break; + case 'n': + client_config.abort_if_no_lease = 1; + break; + case 'p': + client_config.pidfile = optarg; + break; + case 'q': + client_config.quit_after_lease = 1; + break; + case 'r': + requested_ip = inet_addr(optarg); + break; + case 's': + client_config.script = optarg; + break; + case 'v': + printf("udhcpcd, version %s\n\n", VERSION); + return 0; + break; + default: + show_usage(); + } + } + + /* Start the log, sanitize fd's, and write a pid file */ + start_log_and_pid("udhcpc", client_config.pidfile); + + if (read_interface(client_config.interface, &client_config.ifindex, + NULL, client_config.arp) < 0) + return 1; + + if (!client_config.clientid) { + client_config.clientid = xmalloc(6 + 3); + client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; + client_config.clientid[OPT_LEN] = 7; + client_config.clientid[OPT_DATA] = 1; + memcpy(client_config.clientid + 3, client_config.arp, 6); + } + + /* setup the signal pipe */ + udhcp_sp_setup(); + + state = INIT_SELECTING; + run_script(NULL, "deconfig"); + change_mode(LISTEN_RAW); + + for (;;) { + + tv.tv_sec = timeout - uptime(); + tv.tv_usec = 0; + + if (listen_mode != LISTEN_NONE && fd < 0) { + if (listen_mode == LISTEN_KERNEL) + fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface); + else + fd = raw_socket(client_config.ifindex); + if (fd < 0) { + LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m"); + return 0; + } + } + max_fd = udhcp_sp_fd_set(&rfds, fd); + + if (tv.tv_sec > 0) { + DEBUG(LOG_INFO, "Waiting on select..."); + retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); + } else retval = 0; /* If we already timed out, fall through */ + + now = uptime(); + if (retval == 0) { + /* timeout dropped to zero */ + switch (state) { + case INIT_SELECTING: + if (packet_num < 3) { + if (packet_num == 0) + xid = random_xid(); + + /* send discover packet */ + send_discover(xid, requested_ip); /* broadcast */ + + timeout = now + ((packet_num == 2) ? 4 : 2); + packet_num++; + } else { + run_script(NULL, "leasefail"); + if (client_config.background_if_no_lease) { + LOG(LOG_INFO, "No lease, forking to background."); + client_background(); + } else if (client_config.abort_if_no_lease) { + LOG(LOG_INFO, "No lease, failing."); + return 1; + } + /* wait to try again */ + packet_num = 0; + timeout = now + 60; + } + break; + case RENEW_REQUESTED: + case REQUESTING: + if (packet_num < 3) { + /* send request packet */ + if (state == RENEW_REQUESTED) + send_renew(xid, server_addr, requested_ip); /* unicast */ + else send_selecting(xid, server_addr, requested_ip); /* broadcast */ + + timeout = now + ((packet_num == 2) ? 10 : 2); + packet_num++; + } else { + /* timed out, go back to init state */ + if (state == RENEW_REQUESTED) run_script(NULL, "deconfig"); + state = INIT_SELECTING; + timeout = now; + packet_num = 0; + change_mode(LISTEN_RAW); + } + break; + case BOUND: + /* Lease is starting to run out, time to enter renewing state */ + state = RENEWING; + change_mode(LISTEN_KERNEL); + DEBUG(LOG_INFO, "Entering renew state"); + /* fall right through */ + case RENEWING: + /* Either set a new T1, or enter REBINDING state */ + if ((t2 - t1) <= (lease / 14400 + 1)) { + /* timed out, enter rebinding state */ + state = REBINDING; + timeout = now + (t2 - t1); + DEBUG(LOG_INFO, "Entering rebinding state"); + } else { + /* send a request packet */ + send_renew(xid, server_addr, requested_ip); /* unicast */ + + t1 = (t2 - t1) / 2 + t1; + timeout = t1 + start; + } + break; + case REBINDING: + /* Either set a new T2, or enter INIT state */ + if ((lease - t2) <= (lease / 14400 + 1)) { + /* timed out, enter init state */ + state = INIT_SELECTING; + LOG(LOG_INFO, "Lease lost, entering init state"); + run_script(NULL, "deconfig"); + timeout = now; + packet_num = 0; + change_mode(LISTEN_RAW); + } else { + /* send a request packet */ + send_renew(xid, 0, requested_ip); /* broadcast */ + + t2 = (lease - t2) / 2 + t2; + timeout = t2 + start; + } + break; + case RELEASED: + /* yah, I know, *you* say it would never happen */ + timeout = 0x7fffffff; + break; + } + } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) { + /* a packet is ready, read it */ + + if (listen_mode == LISTEN_KERNEL) + len = get_packet(&packet, fd); + else len = get_raw_packet(&packet, fd); + + if (len == -1 && errno != EINTR) { + DEBUG(LOG_INFO, "error on read, %m, reopening socket"); + change_mode(listen_mode); /* just close and reopen */ + } + if (len < 0) continue; + + if (packet.xid != xid) { + DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)", + (unsigned long) packet.xid, xid); + continue; + } + + if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { + DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring"); + continue; + } + + switch (state) { + case INIT_SELECTING: + /* Must be a DHCPOFFER to one of our xid's */ + if (*message == DHCPOFFER) { + if ((temp = get_option(&packet, DHCP_SERVER_ID))) { + memcpy(&server_addr, temp, 4); + xid = packet.xid; + requested_ip = packet.yiaddr; + + /* enter requesting state */ + state = REQUESTING; + timeout = now; + packet_num = 0; + } else { + DEBUG(LOG_ERR, "No server ID in message"); + } + } + break; + case RENEW_REQUESTED: + case REQUESTING: + case RENEWING: + case REBINDING: + if (*message == DHCPACK) { + if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) { + LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease"); + lease = 60 * 60; + } else { + memcpy(&lease, temp, 4); + lease = ntohl(lease); + } + + /* enter bound state */ + t1 = lease / 2; + + /* little fixed point for n * .875 */ + t2 = (lease * 0x7) >> 3; + temp_addr.s_addr = packet.yiaddr; + LOG(LOG_INFO, "Lease of %s obtained, lease time %ld", + inet_ntoa(temp_addr), lease); + start = now; + timeout = t1 + start; + requested_ip = packet.yiaddr; + run_script(&packet, + ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); + + state = BOUND; + change_mode(LISTEN_NONE); + if (client_config.quit_after_lease) + return 0; + if (!client_config.foreground) + client_background(); + + } else if (*message == DHCPNAK) { + /* return to init state */ + LOG(LOG_INFO, "Received DHCP NAK"); + run_script(&packet, "nak"); + if (state != REQUESTING) + run_script(NULL, "deconfig"); + state = INIT_SELECTING; + timeout = now; + requested_ip = 0; + packet_num = 0; + change_mode(LISTEN_RAW); + sleep(3); /* avoid excessive network traffic */ + } + break; + /* case BOUND, RELEASED: - ignore all packets */ + } + } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) { + switch (sig) { + case SIGUSR1: + perform_renew(); + break; + case SIGUSR2: + perform_release(); + break; + case SIGTERM: + LOG(LOG_INFO, "Received SIGTERM"); + return 0; + } + } else if (retval == -1 && errno == EINTR) { + /* a signal was caught */ + } else { + /* An error occured */ + DEBUG(LOG_ERR, "Error on select"); + } + + } + return 0; +} diff --git a/busybox/networking/udhcp/dhcpc.h b/busybox/networking/udhcp/dhcpc.h new file mode 100644 index 000000000..9c4aa95f7 --- /dev/null +++ b/busybox/networking/udhcp/dhcpc.h @@ -0,0 +1,37 @@ +/* dhcpc.h */ +#ifndef _DHCPC_H +#define _DHCPC_H + +#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" + +/* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */ +#include "libbb_udhcp.h" + +#define INIT_SELECTING 0 +#define REQUESTING 1 +#define BOUND 2 +#define RENEWING 3 +#define REBINDING 4 +#define INIT_REBOOT 5 +#define RENEW_REQUESTED 6 +#define RELEASED 7 + + +struct client_config_t { + char foreground; /* Do not fork */ + char quit_after_lease; /* Quit after obtaining lease */ + char abort_if_no_lease; /* Abort if no lease */ + char background_if_no_lease; /* Fork to background if no lease */ + char *interface; /* The name of the interface to use */ + char *pidfile; /* Optionally store the process ID */ + char *script; /* User script to run at dhcp events */ + uint8_t *clientid; /* Optional client id to use */ + uint8_t *hostname; /* Optional hostname to use */ + int ifindex; /* Index number of the interface to use */ + uint8_t arp[6]; /* Our arp address */ +}; + +extern struct client_config_t client_config; + + +#endif diff --git a/busybox/networking/udhcp/dhcpd.c b/busybox/networking/udhcp/dhcpd.c new file mode 100644 index 000000000..ab3ddfe4f --- /dev/null +++ b/busybox/networking/udhcp/dhcpd.c @@ -0,0 +1,273 @@ +/* dhcpd.c + * + * udhcp Server + * Copyright (C) 1999 Matthew Ramsay + * Chris Trew + * + * Rewrite by Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcpd.h" +#include "arpping.h" +#include "socket.h" +#include "options.h" +#include "files.h" +#include "serverpacket.h" +#include "common.h" +#include "signalpipe.h" +#include "static_leases.h" + + +/* globals */ +struct dhcpOfferedAddr *leases; +struct server_config_t server_config; + + +#ifdef COMBINED_BINARY +int udhcpd_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + fd_set rfds; + struct timeval tv; + int server_socket = -1; + int bytes, retval; + struct dhcpMessage packet; + uint8_t *state; + uint8_t *server_id, *requested; + uint32_t server_id_align, requested_align; + unsigned long timeout_end; + struct option_set *option; + struct dhcpOfferedAddr *lease; + struct dhcpOfferedAddr static_lease; + int max_sock; + unsigned long num_ips; + + uint32_t static_lease_ip; + + memset(&server_config, 0, sizeof(struct server_config_t)); + read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]); + + /* Start the log, sanitize fd's, and write a pid file */ + start_log_and_pid("udhcpd", server_config.pidfile); + + if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { + memcpy(&server_config.lease, option->data + 2, 4); + server_config.lease = ntohl(server_config.lease); + } + else server_config.lease = LEASE_TIME; + + /* Sanity check */ + num_ips = ntohl(server_config.end) - ntohl(server_config.start); + if (server_config.max_leases > num_ips) { + LOG(LOG_ERR, "max_leases value (%lu) not sane, " + "setting to %lu instead", + server_config.max_leases, num_ips); + server_config.max_leases = num_ips; + } + + leases = xcalloc(server_config.max_leases, sizeof(struct dhcpOfferedAddr)); + read_leases(server_config.lease_file); + + if (read_interface(server_config.interface, &server_config.ifindex, + &server_config.server, server_config.arp) < 0) + return 1; + +#ifndef UDHCP_DEBUG + background(server_config.pidfile); /* hold lock during fork. */ +#endif + + /* Setup the signal pipe */ + udhcp_sp_setup(); + + timeout_end = time(0) + server_config.auto_time; + while(1) { /* loop until universe collapses */ + + if (server_socket < 0) + if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) { + LOG(LOG_ERR, "FATAL: couldn't create server socket, %m"); + return 2; + } + + max_sock = udhcp_sp_fd_set(&rfds, server_socket); + if (server_config.auto_time) { + tv.tv_sec = timeout_end - time(0); + tv.tv_usec = 0; + } + if (!server_config.auto_time || tv.tv_sec > 0) { + retval = select(max_sock + 1, &rfds, NULL, NULL, + server_config.auto_time ? &tv : NULL); + } else retval = 0; /* If we already timed out, fall through */ + + if (retval == 0) { + write_leases(); + timeout_end = time(0) + server_config.auto_time; + continue; + } else if (retval < 0 && errno != EINTR) { + DEBUG(LOG_INFO, "error on select"); + continue; + } + + switch (udhcp_sp_read(&rfds)) { + case SIGUSR1: + LOG(LOG_INFO, "Received a SIGUSR1"); + write_leases(); + /* why not just reset the timeout, eh */ + timeout_end = time(0) + server_config.auto_time; + continue; + case SIGTERM: + LOG(LOG_INFO, "Received a SIGTERM"); + return 0; + case 0: break; /* no signal */ + default: continue; /* signal or error (probably EINTR) */ + } + + if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ + if (bytes == -1 && errno != EINTR) { + DEBUG(LOG_INFO, "error on read, %m, reopening socket"); + close(server_socket); + server_socket = -1; + } + continue; + } + + if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { + DEBUG(LOG_ERR, "couldn't get option from packet, ignoring"); + continue; + } + + /* Look for a static lease */ + static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr); + + if(static_lease_ip) + { + printf("Found static lease: %x\n", static_lease_ip); + + memcpy(&static_lease.chaddr, &packet.chaddr, 16); + static_lease.yiaddr = static_lease_ip; + static_lease.expires = 0; + + lease = &static_lease; + + } + else + { + lease = find_lease_by_chaddr(packet.chaddr); + } + + switch (state[0]) { + case DHCPDISCOVER: + DEBUG(LOG_INFO,"received DISCOVER"); + + if (sendOffer(&packet) < 0) { + LOG(LOG_ERR, "send OFFER failed"); + } + break; + case DHCPREQUEST: + DEBUG(LOG_INFO, "received REQUEST"); + + requested = get_option(&packet, DHCP_REQUESTED_IP); + server_id = get_option(&packet, DHCP_SERVER_ID); + + if (requested) memcpy(&requested_align, requested, 4); + if (server_id) memcpy(&server_id_align, server_id, 4); + + if (lease) { + if (server_id) { + /* SELECTING State */ + DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align)); + if (server_id_align == server_config.server && requested && + requested_align == lease->yiaddr) { + sendACK(&packet, lease->yiaddr); + } + } else { + if (requested) { + /* INIT-REBOOT State */ + if (lease->yiaddr == requested_align) + sendACK(&packet, lease->yiaddr); + else sendNAK(&packet); + } else { + /* RENEWING or REBINDING State */ + if (lease->yiaddr == packet.ciaddr) + sendACK(&packet, lease->yiaddr); + else { + /* don't know what to do!!!! */ + sendNAK(&packet); + } + } + } + + /* what to do if we have no record of the client */ + } else if (server_id) { + /* SELECTING State */ + + } else if (requested) { + /* INIT-REBOOT State */ + if ((lease = find_lease_by_yiaddr(requested_align))) { + if (lease_expired(lease)) { + /* probably best if we drop this lease */ + memset(lease->chaddr, 0, 16); + /* make some contention for this address */ + } else sendNAK(&packet); + } else if (requested_align < server_config.start || + requested_align > server_config.end) { + sendNAK(&packet); + } /* else remain silent */ + + } else { + /* RENEWING or REBINDING State */ + } + break; + case DHCPDECLINE: + DEBUG(LOG_INFO,"received DECLINE"); + if (lease) { + memset(lease->chaddr, 0, 16); + lease->expires = time(0) + server_config.decline_time; + } + break; + case DHCPRELEASE: + DEBUG(LOG_INFO,"received RELEASE"); + if (lease) lease->expires = time(0); + break; + case DHCPINFORM: + DEBUG(LOG_INFO,"received INFORM"); + send_inform(&packet); + break; + default: + LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]); + } + } + + return 0; +} + diff --git a/busybox/networking/udhcp/dhcpd.h b/busybox/networking/udhcp/dhcpd.h new file mode 100644 index 000000000..c47f6aa3f --- /dev/null +++ b/busybox/networking/udhcp/dhcpd.h @@ -0,0 +1,140 @@ +/* dhcpd.h */ +#ifndef _DHCPD_H +#define _DHCPD_H + +#include +#include + +#include "libbb_udhcp.h" +#include "leases.h" +#include "version.h" + +/************************************/ +/* Defaults _you_ may want to tweak */ +/************************************/ + +/* the period of time the client is allowed to use that address */ +#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */ +#define LEASES_FILE "/var/lib/misc/udhcpd.leases" + +/* where to find the DHCP server configuration file */ +#define DHCPD_CONF_FILE "/etc/udhcpd.conf" + +/*****************************************************************/ +/* Do not modify below here unless you know what you are doing!! */ +/*****************************************************************/ + +/* DHCP protocol -- see RFC 2131 */ +#define SERVER_PORT 67 +#define CLIENT_PORT 68 + +#define DHCP_MAGIC 0x63825363 + +/* DHCP option codes (partial list) */ +#define DHCP_PADDING 0x00 +#define DHCP_SUBNET 0x01 +#define DHCP_TIME_OFFSET 0x02 +#define DHCP_ROUTER 0x03 +#define DHCP_TIME_SERVER 0x04 +#define DHCP_NAME_SERVER 0x05 +#define DHCP_DNS_SERVER 0x06 +#define DHCP_LOG_SERVER 0x07 +#define DHCP_COOKIE_SERVER 0x08 +#define DHCP_LPR_SERVER 0x09 +#define DHCP_HOST_NAME 0x0c +#define DHCP_BOOT_SIZE 0x0d +#define DHCP_DOMAIN_NAME 0x0f +#define DHCP_SWAP_SERVER 0x10 +#define DHCP_ROOT_PATH 0x11 +#define DHCP_IP_TTL 0x17 +#define DHCP_MTU 0x1a +#define DHCP_BROADCAST 0x1c +#define DHCP_NTP_SERVER 0x2a +#define DHCP_WINS_SERVER 0x2c +#define DHCP_REQUESTED_IP 0x32 +#define DHCP_LEASE_TIME 0x33 +#define DHCP_OPTION_OVER 0x34 +#define DHCP_MESSAGE_TYPE 0x35 +#define DHCP_SERVER_ID 0x36 +#define DHCP_PARAM_REQ 0x37 +#define DHCP_MESSAGE 0x38 +#define DHCP_MAX_SIZE 0x39 +#define DHCP_T1 0x3a +#define DHCP_T2 0x3b +#define DHCP_VENDOR 0x3c +#define DHCP_CLIENT_ID 0x3d + +#define DHCP_END 0xFF + + +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +#define ETH_10MB 1 +#define ETH_10MB_LEN 6 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +#define BROADCAST_FLAG 0x8000 + +#define OPTION_FIELD 0 +#define FILE_FIELD 1 +#define SNAME_FIELD 2 + +/* miscellaneous defines */ +#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff" +#define OPT_CODE 0 +#define OPT_LEN 1 +#define OPT_DATA 2 + +struct option_set { + uint8_t *data; + struct option_set *next; +}; + +struct static_lease { + uint8_t *mac; + uint32_t *ip; + struct static_lease *next; +}; + +struct server_config_t { + uint32_t server; /* Our IP, in network order */ + uint32_t start; /* Start address of leases, network order */ + uint32_t end; /* End of leases, network order */ + struct option_set *options; /* List of DHCP options loaded from the config file */ + char *interface; /* The name of the interface to use */ + int ifindex; /* Index number of the interface to use */ + uint8_t arp[6]; /* Our arp address */ + unsigned long lease; /* lease time in seconds (host order) */ + unsigned long max_leases; /* maximum number of leases (including reserved address) */ + char remaining; /* should the lease file be interpreted as lease time remaining, or + * as the time the lease expires */ + unsigned long auto_time; /* how long should udhcpd wait before writing a config file. + * if this is zero, it will only write one on SIGUSR1 */ + unsigned long decline_time; /* how long an address is reserved if a client returns a + * decline message */ + unsigned long conflict_time; /* how long an arp conflict offender is leased for */ + unsigned long offer_time; /* how long an offered address is reserved */ + unsigned long min_lease; /* minimum lease a client can request*/ + char *lease_file; + char *pidfile; + char *notify_file; /* What to run whenever leases are written */ + uint32_t siaddr; /* next server bootp option */ + char *sname; /* bootp server name */ + char *boot_file; /* bootp boot file option */ + struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */ +}; + +extern struct server_config_t server_config; +extern struct dhcpOfferedAddr *leases; + + +#endif diff --git a/busybox/networking/udhcp/dumpleases.c b/busybox/networking/udhcp/dumpleases.c new file mode 100644 index 000000000..a9036dfbd --- /dev/null +++ b/busybox/networking/udhcp/dumpleases.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcpd.h" +#include "leases.h" +#include "libbb_udhcp.h" + +#define REMAINING 0 +#define ABSOLUTE 1 + + +#ifndef IN_BUSYBOX +static void __attribute__ ((noreturn)) show_usage(void) +{ + printf( +"Usage: dumpleases -f -[r|a]\n\n" +" -f, --file=FILENAME Leases file to load\n" +" -r, --remaining Interepret lease times as time remaing\n" +" -a, --absolute Interepret lease times as expire time\n"); + exit(0); +} +#else +#define show_usage bb_show_usage +#endif + + +#ifdef IN_BUSYBOX +int dumpleases_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + FILE *fp; + int i, c, mode = REMAINING; + long expires; + const char *file = LEASES_FILE; + struct dhcpOfferedAddr lease; + struct in_addr addr; + + static const struct option options[] = { + {"absolute", 0, 0, 'a'}, + {"remaining", 0, 0, 'r'}, + {"file", 1, 0, 'f'}, + {0, 0, 0, 0} + }; + + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "arf:", options, &option_index); + if (c == -1) break; + + switch (c) { + case 'a': mode = ABSOLUTE; break; + case 'r': mode = REMAINING; break; + case 'f': + file = optarg; + break; + default: + show_usage(); + } + } + + fp = xfopen(file, "r"); + + printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at"); + /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */ + while (fread(&lease, sizeof(lease), 1, fp)) { + + for (i = 0; i < 6; i++) { + printf("%02x", lease.chaddr[i]); + if (i != 5) printf(":"); + } + addr.s_addr = lease.yiaddr; + printf(" %-15s", inet_ntoa(addr)); + expires = ntohl(lease.expires); + printf(" "); + if (mode == REMAINING) { + if (!expires) printf("expired\n"); + else { + if (expires > 60*60*24) { + printf("%ld days, ", expires / (60*60*24)); + expires %= 60*60*24; + } + if (expires > 60*60) { + printf("%ld hours, ", expires / (60*60)); + expires %= 60*60; + } + if (expires > 60) { + printf("%ld minutes, ", expires / 60); + expires %= 60; + } + printf("%ld seconds\n", expires); + } + } else printf("%s", ctime(&expires)); + } + fclose(fp); + + return 0; +} diff --git a/busybox/networking/udhcp/files.c b/busybox/networking/udhcp/files.c new file mode 100644 index 000000000..73a3bbc6e --- /dev/null +++ b/busybox/networking/udhcp/files.c @@ -0,0 +1,346 @@ +/* + * files.c -- DHCP server file manipulation * + * Rewrite by Russ Dill July 2001 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "static_leases.h" + +#include "dhcpd.h" +#include "files.h" +#include "options.h" +#include "common.h" + +/* + * Domain names may have 254 chars, and string options can be 254 + * chars long. However, 80 bytes will be enough for most, and won't + * hog up memory. If you have a special application, change it + */ +#define READ_CONFIG_BUF_SIZE 80 + +/* on these functions, make sure you datatype matches */ +static int read_ip(const char *line, void *arg) +{ + struct in_addr *addr = arg; + struct hostent *host; + int retval = 1; + + if (!inet_aton(line, addr)) { + if ((host = gethostbyname(line))) + addr->s_addr = *((unsigned long *) host->h_addr_list[0]); + else retval = 0; + } + return retval; +} + +static int read_mac(const char *line, void *arg) +{ + uint8_t *mac_bytes = arg; + struct ether_addr *temp_ether_addr; + int retval = 1; + + temp_ether_addr = ether_aton(line); + + if(temp_ether_addr == NULL) + retval = 0; + else + memcpy(mac_bytes, temp_ether_addr, 6); + + return retval; +} + + +static int read_str(const char *line, void *arg) +{ + char **dest = arg; + + if (*dest) free(*dest); + *dest = strdup(line); + + return 1; +} + + +static int read_u32(const char *line, void *arg) +{ + uint32_t *dest = arg; + char *endptr; + *dest = strtoul(line, &endptr, 0); + return endptr[0] == '\0'; +} + + +static int read_yn(const char *line, void *arg) +{ + char *dest = arg; + int retval = 1; + + if (!strcasecmp("yes", line)) + *dest = 1; + else if (!strcasecmp("no", line)) + *dest = 0; + else retval = 0; + + return retval; +} + + +/* read a dhcp option and add it to opt_list */ +static int read_opt(const char *const_line, void *arg) +{ + struct option_set **opt_list = arg; + char *opt, *val, *endptr; + struct dhcp_option *option; + int retval = 0, length; + char buffer[8]; + char *line; + uint16_t *result_u16 = (uint16_t *) buffer; + uint32_t *result_u32 = (uint32_t *) buffer; + + /* Cheat, the only const line we'll actually get is "" */ + line = (char *) const_line; + if (!(opt = strtok(line, " \t="))) return 0; + + for (option = dhcp_options; option->code; option++) + if (!strcasecmp(option->name, opt)) + break; + + if (!option->code) return 0; + + do { + if (!(val = strtok(NULL, ", \t"))) break; + length = option_lengths[option->flags & TYPE_MASK]; + retval = 0; + opt = buffer; /* new meaning for variable opt */ + switch (option->flags & TYPE_MASK) { + case OPTION_IP: + retval = read_ip(val, buffer); + break; + case OPTION_IP_PAIR: + retval = read_ip(val, buffer); + if (!(val = strtok(NULL, ", \t/-"))) retval = 0; + if (retval) retval = read_ip(val, buffer + 4); + break; + case OPTION_STRING: + length = strlen(val); + if (length > 0) { + if (length > 254) length = 254; + opt = val; + retval = 1; + } + break; + case OPTION_BOOLEAN: + retval = read_yn(val, buffer); + break; + case OPTION_U8: + buffer[0] = strtoul(val, &endptr, 0); + retval = (endptr[0] == '\0'); + break; + case OPTION_U16: + *result_u16 = htons(strtoul(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_S16: + *result_u16 = htons(strtol(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_U32: + *result_u32 = htonl(strtoul(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + case OPTION_S32: + *result_u32 = htonl(strtol(val, &endptr, 0)); + retval = (endptr[0] == '\0'); + break; + default: + break; + } + if (retval) + attach_option(opt_list, option, opt, length); + } while (retval && option->flags & OPTION_LIST); + return retval; +} + +static int read_staticlease(const char *const_line, void *arg) +{ + + char *line; + char *mac_string; + char *ip_string; + uint8_t *mac_bytes; + uint32_t *ip; + + + /* Allocate memory for addresses */ + mac_bytes = xmalloc(sizeof(unsigned char) * 8); + ip = xmalloc(sizeof(uint32_t)); + + /* Read mac */ + line = (char *) const_line; + mac_string = strtok(line, " \t"); + read_mac(mac_string, mac_bytes); + + /* Read ip */ + ip_string = strtok(NULL, " \t"); + read_ip(ip_string, ip); + + addStaticLease(arg, mac_bytes, ip); + +#ifdef UDHCP_DEBUG + printStaticLeases(arg); +#endif + + return 1; + +} + + +static const struct config_keyword keywords[] = { + /* keyword handler variable address default */ + {"start", read_ip, &(server_config.start), "192.168.0.20"}, + {"end", read_ip, &(server_config.end), "192.168.0.254"}, + {"interface", read_str, &(server_config.interface), "eth0"}, + {"option", read_opt, &(server_config.options), ""}, + {"opt", read_opt, &(server_config.options), ""}, + {"max_leases", read_u32, &(server_config.max_leases), "254"}, + {"remaining", read_yn, &(server_config.remaining), "yes"}, + {"auto_time", read_u32, &(server_config.auto_time), "7200"}, + {"decline_time",read_u32, &(server_config.decline_time),"3600"}, + {"conflict_time",read_u32,&(server_config.conflict_time),"3600"}, + {"offer_time", read_u32, &(server_config.offer_time), "60"}, + {"min_lease", read_u32, &(server_config.min_lease), "60"}, + {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE}, + {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, + {"notify_file", read_str, &(server_config.notify_file), ""}, + {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, + {"sname", read_str, &(server_config.sname), ""}, + {"boot_file", read_str, &(server_config.boot_file), ""}, + {"static_lease",read_staticlease, &(server_config.static_leases), ""}, + /*ADDME: static lease */ + {"", NULL, NULL, ""} +}; + + +int read_config(const char *file) +{ + FILE *in; + char buffer[READ_CONFIG_BUF_SIZE], *token, *line; +#ifdef UDHCP_DEBUG + char orig[READ_CONFIG_BUF_SIZE]; +#endif + int i, lm = 0; + + for (i = 0; keywords[i].keyword[0]; i++) + if (keywords[i].def[0]) + keywords[i].handler(keywords[i].def, keywords[i].var); + + if (!(in = fopen(file, "r"))) { + LOG(LOG_ERR, "unable to open config file: %s", file); + return 0; + } + + while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) { + lm++; + if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0'; +#ifdef UDHCP_DEBUG + strcpy(orig, buffer); +#endif + if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0'; + + if (!(token = strtok(buffer, " \t"))) continue; + if (!(line = strtok(NULL, ""))) continue; + + /* eat leading whitespace */ + line = line + strspn(line, " \t="); + /* eat trailing whitespace */ + for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--); + line[i] = '\0'; + + for (i = 0; keywords[i].keyword[0]; i++) + if (!strcasecmp(token, keywords[i].keyword)) + if (!keywords[i].handler(line, keywords[i].var)) { + LOG(LOG_ERR, "Failure parsing line %d of %s", lm, file); + DEBUG(LOG_ERR, "unable to parse '%s'", orig); + /* reset back to the default value */ + keywords[i].handler(keywords[i].def, keywords[i].var); + } + } + fclose(in); + return 1; +} + + +void write_leases(void) +{ + FILE *fp; + unsigned int i; + char buf[255]; + time_t curr = time(0); + unsigned long tmp_time; + + if (!(fp = fopen(server_config.lease_file, "w"))) { + LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file); + return; + } + + for (i = 0; i < server_config.max_leases; i++) { + if (leases[i].yiaddr != 0) { + + /* screw with the time in the struct, for easier writing */ + tmp_time = leases[i].expires; + + if (server_config.remaining) { + if (lease_expired(&(leases[i]))) + leases[i].expires = 0; + else leases[i].expires -= curr; + } /* else stick with the time we got */ + leases[i].expires = htonl(leases[i].expires); + fwrite(&leases[i], sizeof(struct dhcpOfferedAddr), 1, fp); + + /* Then restore it when done. */ + leases[i].expires = tmp_time; + } + } + fclose(fp); + + if (server_config.notify_file) { + sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file); + system(buf); + } +} + + +void read_leases(const char *file) +{ + FILE *fp; + unsigned int i = 0; + struct dhcpOfferedAddr lease; + + if (!(fp = fopen(file, "r"))) { + LOG(LOG_ERR, "Unable to open %s for reading", file); + return; + } + + while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) { + /* ADDME: is it a static lease */ + if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) { + lease.expires = ntohl(lease.expires); + if (!server_config.remaining) lease.expires -= time(0); + if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) { + LOG(LOG_WARNING, "Too many leases while loading %s\n", file); + break; + } + i++; + } + } + DEBUG(LOG_INFO, "Read %d leases", i); + fclose(fp); +} diff --git a/busybox/networking/udhcp/files.h b/busybox/networking/udhcp/files.h new file mode 100644 index 000000000..279738adf --- /dev/null +++ b/busybox/networking/udhcp/files.h @@ -0,0 +1,17 @@ +/* files.h */ +#ifndef _FILES_H +#define _FILES_H + +struct config_keyword { + const char *keyword; + int (* const handler)(const char *line, void *var); + void *var; + const char *def; +}; + + +int read_config(const char *file); +void write_leases(void); +void read_leases(const char *file); + +#endif diff --git a/busybox/networking/udhcp/frontend.c b/busybox/networking/udhcp/frontend.c new file mode 100644 index 000000000..fa77ab977 --- /dev/null +++ b/busybox/networking/udhcp/frontend.c @@ -0,0 +1,16 @@ +#include + +extern int udhcpd_main(int argc, char *argv[]); +extern int udhcpc_main(int argc, char *argv[]); + +int main(int argc, char *argv[]) +{ + int ret = 0; + char *base = strrchr(argv[0], '/'); + + if (strstr(base ? (base + 1) : argv[0], "dhcpd")) + ret = udhcpd_main(argc, argv); + else ret = udhcpc_main(argc, argv); + + return ret; +} diff --git a/busybox/networking/udhcp/leases.c b/busybox/networking/udhcp/leases.c new file mode 100644 index 000000000..4da21a23b --- /dev/null +++ b/busybox/networking/udhcp/leases.c @@ -0,0 +1,158 @@ +/* + * leases.c -- tools to manage DHCP leases + * Russ Dill July 2001 + */ + +#include +#include +#include +#include +#include + +#include "dhcpd.h" +#include "files.h" +#include "options.h" +#include "leases.h" +#include "arpping.h" +#include "common.h" + +#include "static_leases.h" + + +uint8_t blank_chaddr[] = {[0 ... 15] = 0}; + +/* clear every lease out that chaddr OR yiaddr matches and is nonzero */ +void clear_lease(uint8_t *chaddr, uint32_t yiaddr) +{ + unsigned int i, j; + + for (j = 0; j < 16 && !chaddr[j]; j++); + + for (i = 0; i < server_config.max_leases; i++) + if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) || + (yiaddr && leases[i].yiaddr == yiaddr)) { + memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr)); + } +} + + +/* add a lease into the table, clearing out any old ones */ +struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease) +{ + struct dhcpOfferedAddr *oldest; + + /* clean out any old ones */ + clear_lease(chaddr, yiaddr); + + oldest = oldest_expired_lease(); + + if (oldest) { + memcpy(oldest->chaddr, chaddr, 16); + oldest->yiaddr = yiaddr; + oldest->expires = time(0) + lease; + } + + return oldest; +} + + +/* true if a lease has expired */ +int lease_expired(struct dhcpOfferedAddr *lease) +{ + return (lease->expires < (unsigned long) time(0)); +} + + +/* Find the oldest expired lease, NULL if there are no expired leases */ +struct dhcpOfferedAddr *oldest_expired_lease(void) +{ + struct dhcpOfferedAddr *oldest = NULL; + unsigned long oldest_lease = time(0); + unsigned int i; + + + for (i = 0; i < server_config.max_leases; i++) + if (oldest_lease > leases[i].expires) { + oldest_lease = leases[i].expires; + oldest = &(leases[i]); + } + return oldest; + +} + + +/* Find the first lease that matches chaddr, NULL if no match */ +struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr) +{ + unsigned int i; + + for (i = 0; i < server_config.max_leases; i++) + if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]); + + return NULL; +} + + +/* Find the first lease that matches yiaddr, NULL is no match */ +struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr) +{ + unsigned int i; + + for (i = 0; i < server_config.max_leases; i++) + if (leases[i].yiaddr == yiaddr) return &(leases[i]); + + return NULL; +} + + +/* check is an IP is taken, if it is, add it to the lease table */ +static int check_ip(uint32_t addr) +{ + struct in_addr temp; + + if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) { + temp.s_addr = addr; + LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds", + inet_ntoa(temp), server_config.conflict_time); + add_lease(blank_chaddr, addr, server_config.conflict_time); + return 1; + } else return 0; +} + + +/* find an assignable address, it check_expired is true, we check all the expired leases as well. + * Maybe this should try expired leases by age... */ +uint32_t find_address(int check_expired) +{ + uint32_t addr, ret; + struct dhcpOfferedAddr *lease = NULL; + + addr = ntohl(server_config.start); /* addr is in host order here */ + for (;addr <= ntohl(server_config.end); addr++) { + + /* ie, 192.168.55.0 */ + if (!(addr & 0xFF)) continue; + + /* ie, 192.168.55.255 */ + if ((addr & 0xFF) == 0xFF) continue; + + /* Only do if it isn't an assigned as a static lease */ + if(!reservedIp(server_config.static_leases, htonl(addr))) + { + + /* lease is not taken */ + ret = htonl(addr); + if ((!(lease = find_lease_by_yiaddr(ret)) || + + /* or it expired and we are checking for expired leases */ + (check_expired && lease_expired(lease))) && + + /* and it isn't on the network */ + !check_ip(ret)) { + return ret; + break; + } + } + } + return 0; +} diff --git a/busybox/networking/udhcp/leases.h b/busybox/networking/udhcp/leases.h new file mode 100644 index 000000000..b13fa72d3 --- /dev/null +++ b/busybox/networking/udhcp/leases.h @@ -0,0 +1,23 @@ +/* leases.h */ +#ifndef _LEASES_H +#define _LEASES_H + + +struct dhcpOfferedAddr { + uint8_t chaddr[16]; + uint32_t yiaddr; /* network order */ + uint32_t expires; /* host order */ +}; + +extern uint8_t blank_chaddr[]; + +void clear_lease(uint8_t *chaddr, uint32_t yiaddr); +struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease); +int lease_expired(struct dhcpOfferedAddr *lease); +struct dhcpOfferedAddr *oldest_expired_lease(void); +struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr); +struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr); +uint32_t find_address(int check_expired); + + +#endif diff --git a/busybox/networking/udhcp/libbb_udhcp.h b/busybox/networking/udhcp/libbb_udhcp.h new file mode 100644 index 000000000..51e157142 --- /dev/null +++ b/busybox/networking/udhcp/libbb_udhcp.h @@ -0,0 +1,54 @@ +/* libbb_udhcp.h - busybox compatability wrapper */ + +/* bit of a hack, do this no matter what the order of the includes. + * (for busybox) */ + +#ifdef CONFIG_INSTALL_NO_USR +#undef DEFAULT_SCRIPT +#define DEFAULT_SCRIPT "/share/udhcpc/default.script" +#endif + +#ifndef _LIBBB_UDHCP_H +#define _LIBBB_UDHCP_H + +#ifdef IN_BUSYBOX +#include "busybox.h" + +#ifdef CONFIG_FEATURE_UDHCP_SYSLOG +#define UDHCP_SYSLOG +#endif + +#ifdef CONFIG_FEATURE_UDHCP_DEBUG +#define UDHCP_DEBUG +#endif + +#define COMBINED_BINARY +#include "version.h" + +#define xfopen bb_xfopen + +#else /* ! BB_VER */ + +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#define xmalloc malloc +#define xcalloc calloc + +static inline FILE *xfopen(const char *file, const char *mode) +{ + FILE *fp; + if (!(fp = fopen(file, mode))) { + perror("could not open input file"); + exit(0); + } + return fp; +} + +#endif /* BB_VER */ + +#endif /* _LIBBB_UDHCP_H */ diff --git a/busybox/networking/udhcp/options.c b/busybox/networking/udhcp/options.c new file mode 100644 index 000000000..d75bc5aff --- /dev/null +++ b/busybox/networking/udhcp/options.c @@ -0,0 +1,228 @@ +/* + * options.c -- DHCP server option packet tools + * Rewrite by Russ Dill July 2001 + */ + +#include +#include + +#include "dhcpd.h" +#include "files.h" +#include "options.h" +#include "common.h" + + +/* supported options are easily added here */ +struct dhcp_option dhcp_options[] = { + /* name[10] flags code */ + {"subnet", OPTION_IP | OPTION_REQ, 0x01}, + {"timezone", OPTION_S32, 0x02}, + {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, + {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, + {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, + {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, + {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, + {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, + {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, + {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, + {"bootsize", OPTION_U16, 0x0d}, + {"domain", OPTION_STRING | OPTION_REQ, 0x0f}, + {"swapsvr", OPTION_IP, 0x10}, + {"rootpath", OPTION_STRING, 0x11}, + {"ipttl", OPTION_U8, 0x17}, + {"mtu", OPTION_U16, 0x1a}, + {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, + {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a}, + {"wins", OPTION_IP | OPTION_LIST, 0x2c}, + {"requestip", OPTION_IP, 0x32}, + {"lease", OPTION_U32, 0x33}, + {"dhcptype", OPTION_U8, 0x35}, + {"serverid", OPTION_IP, 0x36}, + {"message", OPTION_STRING, 0x38}, + {"tftp", OPTION_STRING, 0x42}, + {"bootfile", OPTION_STRING, 0x43}, + {"", 0x00, 0x00} +}; + +/* Lengths of the different option types */ +int option_lengths[] = { + [OPTION_IP] = 4, + [OPTION_IP_PAIR] = 8, + [OPTION_BOOLEAN] = 1, + [OPTION_STRING] = 1, + [OPTION_U8] = 1, + [OPTION_U16] = 2, + [OPTION_S16] = 2, + [OPTION_U32] = 4, + [OPTION_S32] = 4 +}; + + +/* get an option with bounds checking (warning, not aligned). */ +uint8_t *get_option(struct dhcpMessage *packet, int code) +{ + int i, length; + uint8_t *optionptr; + int over = 0, done = 0, curr = OPTION_FIELD; + + optionptr = packet->options; + i = 0; + length = 308; + while (!done) { + if (i >= length) { + LOG(LOG_WARNING, "bogus packet, option fields too long."); + return NULL; + } + if (optionptr[i + OPT_CODE] == code) { + if (i + 1 + optionptr[i + OPT_LEN] >= length) { + LOG(LOG_WARNING, "bogus packet, option fields too long."); + return NULL; + } + return optionptr + i + 2; + } + switch (optionptr[i + OPT_CODE]) { + case DHCP_PADDING: + i++; + break; + case DHCP_OPTION_OVER: + if (i + 1 + optionptr[i + OPT_LEN] >= length) { + LOG(LOG_WARNING, "bogus packet, option fields too long."); + return NULL; + } + over = optionptr[i + 3]; + i += optionptr[OPT_LEN] + 2; + break; + case DHCP_END: + if (curr == OPTION_FIELD && over & FILE_FIELD) { + optionptr = packet->file; + i = 0; + length = 128; + curr = FILE_FIELD; + } else if (curr == FILE_FIELD && over & SNAME_FIELD) { + optionptr = packet->sname; + i = 0; + length = 64; + curr = SNAME_FIELD; + } else done = 1; + break; + default: + i += optionptr[OPT_LEN + i] + 2; + } + } + return NULL; +} + + +/* return the position of the 'end' option (no bounds checking) */ +int end_option(uint8_t *optionptr) +{ + int i = 0; + + while (optionptr[i] != DHCP_END) { + if (optionptr[i] == DHCP_PADDING) i++; + else i += optionptr[i + OPT_LEN] + 2; + } + return i; +} + + +/* add an option string to the options (an option string contains an option code, + * length, then data) */ +int add_option_string(uint8_t *optionptr, uint8_t *string) +{ + int end = end_option(optionptr); + + /* end position + string length + option code/length + end option */ + if (end + string[OPT_LEN] + 2 + 1 >= 308) { + LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]); + return 0; + } + DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]); + memcpy(optionptr + end, string, string[OPT_LEN] + 2); + optionptr[end + string[OPT_LEN] + 2] = DHCP_END; + return string[OPT_LEN] + 2; +} + + +/* add a one to four byte option to a packet */ +int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data) +{ + char length = 0; + int i; + uint8_t option[2 + 4]; + uint8_t *u8; + uint16_t *u16; + uint32_t *u32; + uint32_t aligned; + u8 = (uint8_t *) &aligned; + u16 = (uint16_t *) &aligned; + u32 = &aligned; + + for (i = 0; dhcp_options[i].code; i++) + if (dhcp_options[i].code == code) { + length = option_lengths[dhcp_options[i].flags & TYPE_MASK]; + } + + if (!length) { + DEBUG(LOG_ERR, "Could not add option 0x%02x", code); + return 0; + } + + option[OPT_CODE] = code; + option[OPT_LEN] = length; + + switch (length) { + case 1: *u8 = data; break; + case 2: *u16 = data; break; + case 4: *u32 = data; break; + } + memcpy(option + 2, &aligned, length); + return add_option_string(optionptr, option); +} + + +/* find option 'code' in opt_list */ +struct option_set *find_option(struct option_set *opt_list, char code) +{ + while (opt_list && opt_list->data[OPT_CODE] < code) + opt_list = opt_list->next; + + if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list; + else return NULL; +} + + +/* add an option to the opt_list */ +void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length) +{ + struct option_set *existing, *new, **curr; + + /* add it to an existing option */ + if ((existing = find_option(*opt_list, option->code))) { + DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name); + if (option->flags & OPTION_LIST) { + if (existing->data[OPT_LEN] + length <= 255) { + existing->data = realloc(existing->data, + existing->data[OPT_LEN] + length + 2); + memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); + existing->data[OPT_LEN] += length; + } /* else, ignore the data, we could put this in a second option in the future */ + } /* else, ignore the new data */ + } else { + DEBUG(LOG_INFO, "Attaching option %s to list", option->name); + + /* make a new option */ + new = xmalloc(sizeof(struct option_set)); + new->data = xmalloc(length + 2); + new->data[OPT_CODE] = option->code; + new->data[OPT_LEN] = length; + memcpy(new->data + 2, buffer, length); + + curr = opt_list; + while (*curr && (*curr)->data[OPT_CODE] < option->code) + curr = &(*curr)->next; + + new->next = *curr; + *curr = new; + } +} diff --git a/busybox/networking/udhcp/options.h b/busybox/networking/udhcp/options.h new file mode 100644 index 000000000..fbe54c884 --- /dev/null +++ b/busybox/networking/udhcp/options.h @@ -0,0 +1,40 @@ +/* options.h */ +#ifndef _OPTIONS_H +#define _OPTIONS_H + +#include "packet.h" + +#define TYPE_MASK 0x0F + +enum { + OPTION_IP=1, + OPTION_IP_PAIR, + OPTION_STRING, + OPTION_BOOLEAN, + OPTION_U8, + OPTION_U16, + OPTION_S16, + OPTION_U32, + OPTION_S32 +}; + +#define OPTION_REQ 0x10 /* have the client request this option */ +#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */ + +struct dhcp_option { + char name[10]; + char flags; + uint8_t code; +}; + +extern struct dhcp_option dhcp_options[]; +extern int option_lengths[]; + +uint8_t *get_option(struct dhcpMessage *packet, int code); +int end_option(uint8_t *optionptr); +int add_option_string(uint8_t *optionptr, uint8_t *string); +int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data); +struct option_set *find_option(struct option_set *opt_list, char code); +void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length); + +#endif diff --git a/busybox/networking/udhcp/packet.c b/busybox/networking/udhcp/packet.c new file mode 100644 index 000000000..1c6bb0ca4 --- /dev/null +++ b/busybox/networking/udhcp/packet.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#include +#else +#include +#include +#include +#endif +#include + +#include "packet.h" +#include "dhcpd.h" +#include "options.h" +#include "common.h" + + +void init_header(struct dhcpMessage *packet, char type) +{ + memset(packet, 0, sizeof(struct dhcpMessage)); + switch (type) { + case DHCPDISCOVER: + case DHCPREQUEST: + case DHCPRELEASE: + case DHCPINFORM: + packet->op = BOOTREQUEST; + break; + case DHCPOFFER: + case DHCPACK: + case DHCPNAK: + packet->op = BOOTREPLY; + } + packet->htype = ETH_10MB; + packet->hlen = ETH_10MB_LEN; + packet->cookie = htonl(DHCP_MAGIC); + packet->options[0] = DHCP_END; + add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); +} + + +/* read a packet from socket fd, return -1 on read error, -2 on packet error */ +int get_packet(struct dhcpMessage *packet, int fd) +{ + int bytes; + int i; + const char broken_vendors[][8] = { + "MSFT 98", + "" + }; + char unsigned *vendor; + + memset(packet, 0, sizeof(struct dhcpMessage)); + bytes = read(fd, packet, sizeof(struct dhcpMessage)); + if (bytes < 0) { + DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring"); + return -1; + } + + if (ntohl(packet->cookie) != DHCP_MAGIC) { + LOG(LOG_ERR, "received bogus message, ignoring"); + return -2; + } + DEBUG(LOG_INFO, "Received a packet"); + + if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) { + for (i = 0; broken_vendors[i][0]; i++) { + if (vendor[OPT_LEN - 2] == (uint8_t) strlen(broken_vendors[i]) && + !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) { + DEBUG(LOG_INFO, "broken client (%s), forcing broadcast", + broken_vendors[i]); + packet->flags |= htons(BROADCAST_FLAG); + } + } + } + + + return bytes; +} + + +uint16_t checksum(void *addr, int count) +{ + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + register int32_t sum = 0; + uint16_t *source = (uint16_t *) addr; + + while (count > 1) { + /* This is the inner loop */ + sum += *source++; + count -= 2; + } + + /* Add left-over byte, if any */ + if (count > 0) { + /* Make sure that the left-over byte is added correctly both + * with little and big endian hosts */ + uint16_t tmp = 0; + *(uint8_t *) (&tmp) = * (uint8_t *) source; + sum += tmp; + } + /* Fold 32-bit sum to 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; +} + + +/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */ +int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex) +{ + int fd; + int result; + struct sockaddr_ll dest; + struct udp_dhcp_packet packet; + + if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { + DEBUG(LOG_ERR, "socket call failed: %m"); + return -1; + } + + memset(&dest, 0, sizeof(dest)); + memset(&packet, 0, sizeof(packet)); + + dest.sll_family = AF_PACKET; + dest.sll_protocol = htons(ETH_P_IP); + dest.sll_ifindex = ifindex; + dest.sll_halen = 6; + memcpy(dest.sll_addr, dest_arp, 6); + if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) { + DEBUG(LOG_ERR, "bind call failed: %m"); + close(fd); + return -1; + } + + packet.ip.protocol = IPPROTO_UDP; + packet.ip.saddr = source_ip; + packet.ip.daddr = dest_ip; + packet.udp.source = htons(source_port); + packet.udp.dest = htons(dest_port); + packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ + packet.ip.tot_len = packet.udp.len; + memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); + packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet)); + + packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); + packet.ip.ihl = sizeof(packet.ip) >> 2; + packet.ip.version = IPVERSION; + packet.ip.ttl = IPDEFTTL; + packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip)); + + result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest)); + if (result <= 0) { + DEBUG(LOG_ERR, "write on socket failed: %m"); + } + close(fd); + return result; +} + + +/* Let the kernel do all the work for packet generation */ +int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port) +{ + int n = 1; + int fd, result; + struct sockaddr_in client; + + if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + return -1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) + return -1; + + memset(&client, 0, sizeof(client)); + client.sin_family = AF_INET; + client.sin_port = htons(source_port); + client.sin_addr.s_addr = source_ip; + + if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) + return -1; + + memset(&client, 0, sizeof(client)); + client.sin_family = AF_INET; + client.sin_port = htons(dest_port); + client.sin_addr.s_addr = dest_ip; + + if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) + return -1; + + result = write(fd, payload, sizeof(struct dhcpMessage)); + close(fd); + return result; +} diff --git a/busybox/networking/udhcp/packet.h b/busybox/networking/udhcp/packet.h new file mode 100644 index 000000000..f5859e824 --- /dev/null +++ b/busybox/networking/udhcp/packet.h @@ -0,0 +1,41 @@ +#ifndef _PACKET_H +#define _PACKET_H + +#include +#include + +struct dhcpMessage { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + uint8_t chaddr[16]; + uint8_t sname[64]; + uint8_t file[128]; + uint32_t cookie; + uint8_t options[308]; /* 312 - cookie */ +}; + +struct udp_dhcp_packet { + struct iphdr ip; + struct udphdr udp; + struct dhcpMessage data; +}; + +void init_header(struct dhcpMessage *packet, char type); +int get_packet(struct dhcpMessage *packet, int fd); +uint16_t checksum(void *addr, int count); +int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex); +int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port, + uint32_t dest_ip, int dest_port); + + +#endif diff --git a/busybox/networking/udhcp/pidfile.c b/busybox/networking/udhcp/pidfile.c new file mode 100644 index 000000000..2e0c7533e --- /dev/null +++ b/busybox/networking/udhcp/pidfile.c @@ -0,0 +1,75 @@ +/* pidfile.c + * + * Functions to assist in the writing and removing of pidfiles. + * + * Russ Dill September 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "pidfile.h" +#include "common.h" + +static char *saved_pidfile; + +static void pidfile_delete(void) +{ + if (saved_pidfile) unlink(saved_pidfile); +} + + +int pidfile_acquire(const char *pidfile) +{ + int pid_fd; + if (!pidfile) return -1; + + pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644); + if (pid_fd < 0) { + LOG(LOG_ERR, "Unable to open pidfile %s: %m\n", pidfile); + } else { + lockf(pid_fd, F_LOCK, 0); + if (!saved_pidfile) + atexit(pidfile_delete); + saved_pidfile = (char *) pidfile; + } + + return pid_fd; +} + + +void pidfile_write_release(int pid_fd) +{ + FILE *out; + + if (pid_fd < 0) return; + + if ((out = fdopen(pid_fd, "w")) != NULL) { + fprintf(out, "%d\n", getpid()); + fclose(out); + } + lockf(pid_fd, F_UNLCK, 0); + close(pid_fd); +} + + + + diff --git a/busybox/networking/udhcp/pidfile.h b/busybox/networking/udhcp/pidfile.h new file mode 100644 index 000000000..ea97d1de5 --- /dev/null +++ b/busybox/networking/udhcp/pidfile.h @@ -0,0 +1,25 @@ +/* pidfile.h + * + * Functions to assist in the writing and removing of pidfiles. + * + * Russ Dill September 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +int pidfile_acquire(const char *pidfile); +void pidfile_write_release(int pid_fd); + diff --git a/busybox/networking/udhcp/script.c b/busybox/networking/udhcp/script.c new file mode 100644 index 000000000..820fbb064 --- /dev/null +++ b/busybox/networking/udhcp/script.c @@ -0,0 +1,233 @@ +/* script.c + * + * Functions to call the DHCP client notification scripts + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "options.h" +#include "dhcpd.h" +#include "dhcpc.h" +#include "common.h" + +/* get a rough idea of how long an option will be (rounding up...) */ +static const int max_option_length[] = { + [OPTION_IP] = sizeof("255.255.255.255 "), + [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, + [OPTION_STRING] = 1, + [OPTION_BOOLEAN] = sizeof("yes "), + [OPTION_U8] = sizeof("255 "), + [OPTION_U16] = sizeof("65535 "), + [OPTION_S16] = sizeof("-32768 "), + [OPTION_U32] = sizeof("4294967295 "), + [OPTION_S32] = sizeof("-2147483684 "), +}; + + +static inline int upper_length(int length, int opt_index) +{ + return max_option_length[opt_index] * + (length / option_lengths[opt_index]); +} + + +static int sprintip(char *dest, char *pre, uint8_t *ip) +{ + return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]); +} + + +/* really simple implementation, just count the bits */ +static int mton(struct in_addr *mask) +{ + int i; + unsigned long bits = ntohl(mask->s_addr); + /* too bad one can't check the carry bit, etc in c bit + * shifting */ + for (i = 0; i < 32 && !((bits >> i) & 1); i++); + return 32 - i; +} + + +/* Fill dest with the text of option 'option'. */ +static void fill_options(char *dest, uint8_t *option, struct dhcp_option *type_p) +{ + int type, optlen; + uint16_t val_u16; + int16_t val_s16; + uint32_t val_u32; + int32_t val_s32; + int len = option[OPT_LEN - 2]; + + dest += sprintf(dest, "%s=", type_p->name); + + type = type_p->flags & TYPE_MASK; + optlen = option_lengths[type]; + for(;;) { + switch (type) { + case OPTION_IP_PAIR: + dest += sprintip(dest, "", option); + *(dest++) = '/'; + option += 4; + optlen = 4; + case OPTION_IP: /* Works regardless of host byte order. */ + dest += sprintip(dest, "", option); + break; + case OPTION_BOOLEAN: + dest += sprintf(dest, *option ? "yes" : "no"); + break; + case OPTION_U8: + dest += sprintf(dest, "%u", *option); + break; + case OPTION_U16: + memcpy(&val_u16, option, 2); + dest += sprintf(dest, "%u", ntohs(val_u16)); + break; + case OPTION_S16: + memcpy(&val_s16, option, 2); + dest += sprintf(dest, "%d", ntohs(val_s16)); + break; + case OPTION_U32: + memcpy(&val_u32, option, 4); + dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32)); + break; + case OPTION_S32: + memcpy(&val_s32, option, 4); + dest += sprintf(dest, "%ld", (long) ntohl(val_s32)); + break; + case OPTION_STRING: + memcpy(dest, option, len); + dest[len] = '\0'; + return; /* Short circuit this case */ + } + option += optlen; + len -= optlen; + if (len <= 0) break; + dest += sprintf(dest, " "); + } +} + + +/* put all the parameters into an environment */ +static char **fill_envp(struct dhcpMessage *packet) +{ + int num_options = 0; + int i, j; + char **envp; + uint8_t *temp; + struct in_addr subnet; + char over = 0; + + if (packet == NULL) + num_options = 0; + else { + for (i = 0; dhcp_options[i].code; i++) + if (get_option(packet, dhcp_options[i].code)) { + num_options++; + if (dhcp_options[i].code == DHCP_SUBNET) + num_options++; /* for mton */ + } + if (packet->siaddr) num_options++; + if ((temp = get_option(packet, DHCP_OPTION_OVER))) + over = *temp; + if (!(over & FILE_FIELD) && packet->file[0]) num_options++; + if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; + } + + envp = xcalloc(sizeof(char *), num_options + 5); + j = 0; + asprintf(&envp[j++], "interface=%s", client_config.interface); + asprintf(&envp[j++], "%s=%s", "PATH", + getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin"); + asprintf(&envp[j++], "%s=%s", "HOME", getenv("HOME") ? : "/"); + + if (packet == NULL) return envp; + + envp[j] = xmalloc(sizeof("ip=255.255.255.255")); + sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr); + + + for (i = 0; dhcp_options[i].code; i++) { + if (!(temp = get_option(packet, dhcp_options[i].code))) + continue; + envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], + dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2); + fill_options(envp[j++], temp, &dhcp_options[i]); + + /* Fill in a subnet bits option for things like /24 */ + if (dhcp_options[i].code == DHCP_SUBNET) { + memcpy(&subnet, temp, 4); + asprintf(&envp[j++], "mask=%d", mton(&subnet)); + } + } + if (packet->siaddr) { + envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); + sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr); + } + if (!(over & FILE_FIELD) && packet->file[0]) { + /* watch out for invalid packets */ + packet->file[sizeof(packet->file) - 1] = '\0'; + asprintf(&envp[j++], "boot_file=%s", packet->file); + } + if (!(over & SNAME_FIELD) && packet->sname[0]) { + /* watch out for invalid packets */ + packet->sname[sizeof(packet->sname) - 1] = '\0'; + asprintf(&envp[j++], "sname=%s", packet->sname); + } + return envp; +} + + +/* Call a script with a par file and env vars */ +void run_script(struct dhcpMessage *packet, const char *name) +{ + int pid; + char **envp, **curr; + + if (client_config.script == NULL) + return; + + DEBUG(LOG_INFO, "vforking and execle'ing %s", client_config.script); + + envp = fill_envp(packet); + /* call script */ + pid = vfork(); + if (pid) { + waitpid(pid, NULL, 0); + for (curr = envp; *curr; curr++) free(*curr); + free(envp); + return; + } else if (pid == 0) { + /* close fd's? */ + + /* exec script */ + execle(client_config.script, client_config.script, + name, NULL, envp); + LOG(LOG_ERR, "script %s failed: %m", client_config.script); + exit(1); + } +} diff --git a/busybox/networking/udhcp/script.h b/busybox/networking/udhcp/script.h new file mode 100644 index 000000000..87a20cc17 --- /dev/null +++ b/busybox/networking/udhcp/script.h @@ -0,0 +1,6 @@ +#ifndef _SCRIPT_H +#define _SCRIPT_H + +void run_script(struct dhcpMessage *packet, const char *name); + +#endif diff --git a/busybox/networking/udhcp/serverpacket.c b/busybox/networking/udhcp/serverpacket.c new file mode 100644 index 000000000..75d55bd92 --- /dev/null +++ b/busybox/networking/udhcp/serverpacket.c @@ -0,0 +1,275 @@ +/* serverpacket.c + * + * Construct and send DHCP server packets + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "serverpacket.h" +#include "dhcpd.h" +#include "options.h" +#include "common.h" +#include "static_leases.h" + +/* send a packet to giaddr using the kernel ip stack */ +static int send_packet_to_relay(struct dhcpMessage *payload) +{ + DEBUG(LOG_INFO, "Forwarding packet to relay"); + + return kernel_packet(payload, server_config.server, SERVER_PORT, + payload->giaddr, SERVER_PORT); +} + + +/* send a packet to a specific arp address and ip address by creating our own ip packet */ +static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast) +{ + uint8_t *chaddr; + uint32_t ciaddr; + + if (force_broadcast) { + DEBUG(LOG_INFO, "broadcasting packet to client (NAK)"); + ciaddr = INADDR_BROADCAST; + chaddr = MAC_BCAST_ADDR; + } else if (payload->ciaddr) { + DEBUG(LOG_INFO, "unicasting packet to client ciaddr"); + ciaddr = payload->ciaddr; + chaddr = payload->chaddr; + } else if (ntohs(payload->flags) & BROADCAST_FLAG) { + DEBUG(LOG_INFO, "broadcasting packet to client (requested)"); + ciaddr = INADDR_BROADCAST; + chaddr = MAC_BCAST_ADDR; + } else { + DEBUG(LOG_INFO, "unicasting packet to client yiaddr"); + ciaddr = payload->yiaddr; + chaddr = payload->chaddr; + } + return raw_packet(payload, server_config.server, SERVER_PORT, + ciaddr, CLIENT_PORT, chaddr, server_config.ifindex); +} + + +/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */ +static int send_packet(struct dhcpMessage *payload, int force_broadcast) +{ + int ret; + + if (payload->giaddr) + ret = send_packet_to_relay(payload); + else ret = send_packet_to_client(payload, force_broadcast); + return ret; +} + + +static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type) +{ + init_header(packet, type); + packet->xid = oldpacket->xid; + memcpy(packet->chaddr, oldpacket->chaddr, 16); + packet->flags = oldpacket->flags; + packet->giaddr = oldpacket->giaddr; + packet->ciaddr = oldpacket->ciaddr; + add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server); +} + + +/* add in the bootp options */ +static void add_bootp_options(struct dhcpMessage *packet) +{ + packet->siaddr = server_config.siaddr; + if (server_config.sname) + strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1); + if (server_config.boot_file) + strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1); +} + + +/* send a DHCP OFFER to a DHCP DISCOVER */ +int sendOffer(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + struct dhcpOfferedAddr *lease = NULL; + uint32_t req_align, lease_time_align = server_config.lease; + uint8_t *req, *lease_time; + struct option_set *curr; + struct in_addr addr; + + uint32_t static_lease_ip; + + init_packet(&packet, oldpacket, DHCPOFFER); + + static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr); + + /* ADDME: if static, short circuit */ + if(!static_lease_ip) + { + /* the client is in our lease/offered table */ + if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) { + if (!lease_expired(lease)) + lease_time_align = lease->expires - time(0); + packet.yiaddr = lease->yiaddr; + + /* Or the client has a requested ip */ + } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) && + + /* Don't look here (ugly hackish thing to do) */ + memcpy(&req_align, req, 4) && + + /* and the ip is in the lease range */ + ntohl(req_align) >= ntohl(server_config.start) && + ntohl(req_align) <= ntohl(server_config.end) && + + !static_lease_ip && /* Check that its not a static lease */ + /* and is not already taken/offered */ + ((!(lease = find_lease_by_yiaddr(req_align)) || + + /* or its taken, but expired */ /* ADDME: or maybe in here */ + lease_expired(lease)))) { + packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */ + + /* otherwise, find a free IP */ + } else { + /* Is it a static lease? (No, because find_address skips static lease) */ + packet.yiaddr = find_address(0); + + /* try for an expired lease */ + if (!packet.yiaddr) packet.yiaddr = find_address(1); + } + + if(!packet.yiaddr) { + LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned"); + return -1; + } + + if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) { + LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned"); + return -1; + } + + if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { + memcpy(&lease_time_align, lease_time, 4); + lease_time_align = ntohl(lease_time_align); + if (lease_time_align > server_config.lease) + lease_time_align = server_config.lease; + } + + /* Make sure we aren't just using the lease time from the previous offer */ + if (lease_time_align < server_config.min_lease) + lease_time_align = server_config.lease; + } + /* ADDME: end of short circuit */ + else + { + /* It is a static lease... use it */ + packet.yiaddr = static_lease_ip; + } + + add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + addr.s_addr = packet.yiaddr; + LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr)); + return send_packet(&packet, 0); +} + + +int sendNAK(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + + init_packet(&packet, oldpacket, DHCPNAK); + + DEBUG(LOG_INFO, "sending NAK"); + return send_packet(&packet, 1); +} + + +int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr) +{ + struct dhcpMessage packet; + struct option_set *curr; + uint8_t *lease_time; + uint32_t lease_time_align = server_config.lease; + struct in_addr addr; + + init_packet(&packet, oldpacket, DHCPACK); + packet.yiaddr = yiaddr; + + if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { + memcpy(&lease_time_align, lease_time, 4); + lease_time_align = ntohl(lease_time_align); + if (lease_time_align > server_config.lease) + lease_time_align = server_config.lease; + else if (lease_time_align < server_config.min_lease) + lease_time_align = server_config.lease; + } + + add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + addr.s_addr = packet.yiaddr; + LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr)); + + if (send_packet(&packet, 0) < 0) + return -1; + + add_lease(packet.chaddr, packet.yiaddr, lease_time_align); + + return 0; +} + + +int send_inform(struct dhcpMessage *oldpacket) +{ + struct dhcpMessage packet; + struct option_set *curr; + + init_packet(&packet, oldpacket, DHCPACK); + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) + add_option_string(packet.options, curr->data); + curr = curr->next; + } + + add_bootp_options(&packet); + + return send_packet(&packet, 0); +} diff --git a/busybox/networking/udhcp/serverpacket.h b/busybox/networking/udhcp/serverpacket.h new file mode 100644 index 000000000..902492895 --- /dev/null +++ b/busybox/networking/udhcp/serverpacket.h @@ -0,0 +1,12 @@ +#ifndef _SERVERPACKET_H +#define _SERVERPACKET_H + +#include "packet.h" + +int sendOffer(struct dhcpMessage *oldpacket); +int sendNAK(struct dhcpMessage *oldpacket); +int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr); +int send_inform(struct dhcpMessage *oldpacket); + + +#endif diff --git a/busybox/networking/udhcp/signalpipe.c b/busybox/networking/udhcp/signalpipe.c new file mode 100644 index 000000000..074c8a603 --- /dev/null +++ b/busybox/networking/udhcp/signalpipe.c @@ -0,0 +1,78 @@ +/* signalpipe.c + * + * Signal pipe infrastructure. A reliable way of delivering signals. + * + * Russ Dill December 2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + + +#include "signalpipe.h" +#include "common.h" + +static int signal_pipe[2]; + +static void signal_handler(int sig) +{ + if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) + DEBUG(LOG_ERR, "Could not send signal: %m"); +} + + +/* Call this before doing anything else. Sets up the socket pair + * and installs the signal handler */ +void udhcp_sp_setup(void) +{ + socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); + signal(SIGUSR1, signal_handler); + signal(SIGUSR2, signal_handler); + signal(SIGTERM, signal_handler); +} + + +/* Quick little function to setup the rfds. Will return the + * max_fd for use with select. Limited in that you can only pass + * one extra fd */ +int udhcp_sp_fd_set(fd_set *rfds, int extra_fd) +{ + FD_ZERO(rfds); + FD_SET(signal_pipe[0], rfds); + if (extra_fd >= 0) FD_SET(extra_fd, rfds); + return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd; +} + + +/* Read a signal from the signal pipe. Returns 0 if there is + * no signal, -1 on error (and sets errno appropriately), and + * your signal on success */ +int udhcp_sp_read(fd_set *rfds) +{ + int sig; + + if (!FD_ISSET(signal_pipe[0], rfds)) + return 0; + + if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) + return -1; + + return sig; +} diff --git a/busybox/networking/udhcp/signalpipe.h b/busybox/networking/udhcp/signalpipe.h new file mode 100644 index 000000000..70c1e5715 --- /dev/null +++ b/busybox/networking/udhcp/signalpipe.h @@ -0,0 +1,22 @@ +/* signalpipe.h + * + * Russ Dill December 2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +void udhcp_sp_setup(void); +int udhcp_sp_fd_set(fd_set *rfds, int extra_fd); +int udhcp_sp_read(fd_set *rfds); diff --git a/busybox/networking/udhcp/socket.c b/busybox/networking/udhcp/socket.c new file mode 100644 index 000000000..7b057523a --- /dev/null +++ b/busybox/networking/udhcp/socket.c @@ -0,0 +1,132 @@ +/* + * socket.c -- DHCP server client/server socket creation + * + * udhcp client/server + * Copyright (C) 1999 Matthew Ramsay + * Chris Trew + * + * Rewrite by Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#include +#else +#include +#include +#include +#endif + +#include "socket.h" +#include "common.h" + +int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp) +{ + int fd; + struct ifreq ifr; + struct sockaddr_in *our_ip; + + memset(&ifr, 0, sizeof(struct ifreq)); + if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) { + ifr.ifr_addr.sa_family = AF_INET; + strcpy(ifr.ifr_name, interface); + + if (addr) { + if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { + our_ip = (struct sockaddr_in *) &ifr.ifr_addr; + *addr = our_ip->sin_addr.s_addr; + DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr)); + } else { + LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %m"); + return -1; + } + } + + if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) { + DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex); + *ifindex = ifr.ifr_ifindex; + } else { + LOG(LOG_ERR, "SIOCGIFINDEX failed!: %m"); + return -1; + } + if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) { + memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); + DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", + arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); + } else { + LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %m"); + return -1; + } + } else { + LOG(LOG_ERR, "socket failed!: %m"); + return -1; + } + close(fd); + return 0; +} + + +int listen_socket(uint32_t ip, int port, char *inf) +{ + struct ifreq interface; + int fd; + struct sockaddr_in addr; + int n = 1; + + DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s", ip, port, inf); + if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + DEBUG(LOG_ERR, "socket call failed: %m"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) { + close(fd); + return -1; + } + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) { + close(fd); + return -1; + } + + strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) { + close(fd); + return -1; + } + + if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) { + close(fd); + return -1; + } + + return fd; +} diff --git a/busybox/networking/udhcp/socket.h b/busybox/networking/udhcp/socket.h new file mode 100644 index 000000000..66179d4f5 --- /dev/null +++ b/busybox/networking/udhcp/socket.h @@ -0,0 +1,8 @@ +/* socket.h */ +#ifndef _SOCKET_H +#define _SOCKET_H + +int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp); +int listen_socket(uint32_t ip, int port, char *inf); + +#endif diff --git a/busybox/networking/udhcp/static_leases.c b/busybox/networking/udhcp/static_leases.c new file mode 100644 index 000000000..1124d39de --- /dev/null +++ b/busybox/networking/udhcp/static_leases.c @@ -0,0 +1,119 @@ +/* + * static_leases.c -- Couple of functions to assist with storing and + * retrieving data for static leases + * + * Wade Berrier September 2004 + * + */ + + +#include +#include +#include + +#include "static_leases.h" +#include "dhcpd.h" + +/* Takes the address of the pointer to the static_leases linked list, + * Address to a 6 byte mac address + * Address to a 4 byte ip address */ +int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip) +{ + + struct static_lease *cur; + struct static_lease *new_static_lease; + + /* Build new node */ + new_static_lease = xmalloc(sizeof(struct static_lease)); + new_static_lease->mac = mac; + new_static_lease->ip = ip; + new_static_lease->next = NULL; + + /* If it's the first node to be added... */ + if(*lease_struct == NULL) + { + *lease_struct = new_static_lease; + } + else + { + cur = *lease_struct; + while(cur->next != NULL) + { + cur = cur->next; + } + + cur->next = new_static_lease; + } + + return 1; + +} + +/* Check to see if a mac has an associated static lease */ +uint32_t getIpByMac(struct static_lease *lease_struct, void *arg) +{ + uint32_t return_ip; + struct static_lease *cur = lease_struct; + uint8_t *mac = arg; + + return_ip = 0; + + while(cur != NULL) + { + /* If the client has the correct mac */ + if(memcmp(cur->mac, mac, 6) == 0) + { + return_ip = *(cur->ip); + } + + cur = cur->next; + } + + return return_ip; + +} + +/* Check to see if an ip is reserved as a static ip */ +uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip) +{ + struct static_lease *cur = lease_struct; + + uint32_t return_val = 0; + + while(cur != NULL) + { + /* If the client has the correct ip */ + if(*cur->ip == ip) + return_val = 1; + + cur = cur->next; + } + + return return_val; + +} + +#ifdef UDHCP_DEBUG +/* Print out static leases just to check what's going on */ +/* Takes the address of the pointer to the static_leases linked list */ +void printStaticLeases(struct static_lease **arg) +{ + /* Get a pointer to the linked list */ + struct static_lease *cur = *arg; + + while(cur != NULL) + { + /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */ + printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac)); + /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */ + printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip)); + + cur = cur->next; + } + + +} +#endif + + + diff --git a/busybox/networking/udhcp/static_leases.h b/busybox/networking/udhcp/static_leases.h new file mode 100644 index 000000000..d06520b23 --- /dev/null +++ b/busybox/networking/udhcp/static_leases.h @@ -0,0 +1,25 @@ +/* static_leases.h */ +#ifndef _STATIC_LEASES_H +#define _STATIC_LEASES_H + +#include "dhcpd.h" + +/* Config file will pass static lease info to this function which will add it + * to a data structure that can be searched later */ +int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip); + +/* Check to see if a mac has an associated static lease */ +uint32_t getIpByMac(struct static_lease *lease_struct, void *arg); + +/* Check to see if an ip is reserved as a static ip */ +uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip); + +#ifdef UDHCP_DEBUG +/* Print out static leases just to check what's going on */ +void printStaticLeases(struct static_lease **lease_struct); +#endif + +#endif + + + diff --git a/busybox/networking/udhcp/version.h b/busybox/networking/udhcp/version.h new file mode 100644 index 000000000..3862539f5 --- /dev/null +++ b/busybox/networking/udhcp/version.h @@ -0,0 +1,6 @@ +#ifndef _UDHCP_VERSION_H +#define _UDHCP_VERSION_H + +#define VERSION "0.9.9-pre" + +#endif diff --git a/busybox/networking/vconfig.c b/busybox/networking/vconfig.c new file mode 100644 index 000000000..bbd29873c --- /dev/null +++ b/busybox/networking/vconfig.c @@ -0,0 +1,184 @@ +/* vi: set sw=4 ts=4: */ +/* + * vconfig implementation for busybox + * + * Copyright (C) 2001 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* BB_AUDIT SUSv3 N/A */ + +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* Stuff from linux/if_vlan.h, kernel version 2.4.23 */ +enum vlan_ioctl_cmds { + ADD_VLAN_CMD, + DEL_VLAN_CMD, + SET_VLAN_INGRESS_PRIORITY_CMD, + SET_VLAN_EGRESS_PRIORITY_CMD, + GET_VLAN_INGRESS_PRIORITY_CMD, + GET_VLAN_EGRESS_PRIORITY_CMD, + SET_VLAN_NAME_TYPE_CMD, + SET_VLAN_FLAG_CMD +}; +enum vlan_name_types { + VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */ + VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */ + VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */ + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */ + VLAN_NAME_TYPE_HIGHEST +}; + +struct vlan_ioctl_args { + int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */ + char device1[24]; + + union { + char device2[24]; + int VID; + unsigned int skb_priority; + unsigned int name_type; + unsigned int bind_type; + unsigned int flag; /* Matches vlan_dev_info flags */ + } u; + + short vlan_qos; +}; + +#define VLAN_GROUP_ARRAY_LEN 4096 +#define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */ + +/* On entry, table points to the length of the current string plus + * nul terminator plus data length for the subsequent entry. The + * return value is the last data entry for the matching string. */ +static const char *xfind_str(const char *table, const char *str) +{ + while (strcasecmp(str, table+1) != 0) { + if (!*(table += table[0])) { + bb_show_usage(); + } + } + return table - 1; +} + +static const char cmds[] = { + 4, ADD_VLAN_CMD, 7, + 'a', 'd', 'd', 0, + 3, DEL_VLAN_CMD, 7, + 'r', 'e', 'm', 0, + 3, SET_VLAN_NAME_TYPE_CMD, 17, + 's', 'e', 't', '_', + 'n', 'a', 'm', 'e', '_', + 't', 'y', 'p', 'e', 0, + 4, SET_VLAN_FLAG_CMD, 12, + 's', 'e', 't', '_', + 'f', 'l', 'a', 'g', 0, + 5, SET_VLAN_EGRESS_PRIORITY_CMD, 18, + 's', 'e', 't', '_', + 'e', 'g', 'r', 'e', 's', 's', '_', + 'm', 'a', 'p', 0, + 5, SET_VLAN_INGRESS_PRIORITY_CMD, 16, + 's', 'e', 't', '_', + 'i', 'n', 'g', 'r', 'e', 's', 's', '_', + 'm', 'a', 'p', 0, +}; + +static const char name_types[] = { + VLAN_NAME_TYPE_PLUS_VID, 16, + 'V', 'L', 'A', 'N', + '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', + 0, + VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22, + 'V', 'L', 'A', 'N', + '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', + '_', 'N', 'O', '_', 'P', 'A', 'D', 0, + VLAN_NAME_TYPE_RAW_PLUS_VID, 15, + 'D', 'E', 'V', + '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', + 0, + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20, + 'D', 'E', 'V', + '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', + '_', 'N', 'O', '_', 'P', 'A', 'D', 0, +}; + +static const char conf_file_name[] = "/proc/net/vlan/config"; + +int vconfig_main(int argc, char **argv) +{ + struct vlan_ioctl_args ifr; + const char *p; + int fd; + + if (argc < 3) { + bb_show_usage(); + } + + /* Don't bother closing the filedes. It will be closed on cleanup. */ + if (open(conf_file_name, O_RDONLY) < 0) { /* Is 802.1q is present? */ + bb_perror_msg_and_die("open %s", conf_file_name); + } + + memset(&ifr, 0, sizeof(struct vlan_ioctl_args)); + + ++argv; + p = xfind_str(cmds+2, *argv); + ifr.cmd = *p; + if (argc != p[-1]) { + bb_show_usage(); + } + + if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */ + ifr.u.name_type = *xfind_str(name_types+1, argv[1]); + } else { + if (strlen(argv[1]) >= IF_NAMESIZE) { + bb_error_msg_and_die("if_name >= %d chars\n", IF_NAMESIZE); + } + strcpy(ifr.device1, argv[1]); + p = argv[2]; + + /* I suppose one could try to combine some of the function calls below, + * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized + * (unsigned) int members of a unions. But because of the range checking, + * doing so wouldn't save that much space and would also make maintainence + * more of a pain. */ + if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */ + ifr.u.flag = bb_xgetularg10_bnd(p, 0, 1); + } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */ + ifr.u.VID = bb_xgetularg10_bnd(p, 0, VLAN_GROUP_ARRAY_LEN-1); + } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */ + ifr.u.skb_priority = bb_xgetularg10_bnd(p, 0, ULONG_MAX); + ifr.vlan_qos = bb_xgetularg10_bnd(argv[3], 0, 7); + } + } + + if (((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + || (ioctl(fd, SIOCSIFVLAN, &ifr) < 0) + ) { + bb_perror_msg_and_die("socket or ioctl error for %s", *argv); + } + + return 0; +} + diff --git a/busybox/networking/wget.c b/busybox/networking/wget.c new file mode 100644 index 000000000..45acef4d6 --- /dev/null +++ b/busybox/networking/wget.c @@ -0,0 +1,868 @@ +/* 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" + +struct host_info { + char *host; + int port; + char *path; + int is_ftp; + char *user; +}; + +static void parse_url(char *url, struct host_info *h); +static FILE *open_socket(struct sockaddr_in *s_in); +static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc); +static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf); + +/* Globals (can be accessed from signal handlers */ +static off_t filesize = 0; /* content-length of the file */ +static int chunked = 0; /* chunked transfer encoding */ +#ifdef CONFIG_FEATURE_WGET_STATUSBAR +static void progressmeter(int flag); +static char *curfile; /* Name of current file being transferred. */ +static struct timeval start; /* Time a transfer started. */ +static volatile unsigned long statbytes = 0; /* Number of bytes transferred so far. */ +/* For progressmeter() -- number of seconds before xfer considered "stalled" */ +static const int STALLTIME = 5; +#endif + +static void close_and_delete_outfile(FILE* output, char *fname_out, int do_continue) +{ + if (output != stdout && do_continue==0) { + fclose(output); + unlink(fname_out); + } +} + +/* Read NMEMB elements of SIZE bytes into PTR from STREAM. Returns the + * number of elements read, and a short count if an eof or non-interrupt + * error is encountered. */ +static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = 0; + + do { + clearerr(stream); + ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream); + } while (ret < nmemb && ferror(stream) && errno == EINTR); + + return ret; +} + +/* Write NMEMB elements of SIZE bytes from PTR to STREAM. Returns the + * number of elements written, and a short count if an eof or non-interrupt + * error is encountered. */ +static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = 0; + + do { + clearerr(stream); + ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream); + } while (ret < nmemb && ferror(stream) && errno == EINTR); + + return ret; +} + +/* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM. + * Returns S, or NULL if an eof or non-interrupt error is encountered. */ +static char *safe_fgets(char *s, int size, FILE *stream) +{ + char *ret; + + do { + clearerr(stream); + ret = fgets(s, size, stream); + } while (ret == NULL && ferror(stream) && errno == EINTR); + + return ret; +} + +#define close_delete_and_die(s...) { \ + close_and_delete_outfile(output, fname_out, do_continue); \ + bb_error_msg_and_die(s); } + + +#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION +/* + * Base64-encode character string + * oops... isn't something similar in uuencode.c? + * It would be better to use already existing code + */ +char *base64enc(unsigned char *p, char *buf, int len) { + + char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + char *s = buf; + + while(*p) { + if (s >= buf+len-4) + bb_error_msg_and_die("buffer overflow"); + *(s++) = al[(*p >> 2) & 0x3F]; + *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)]; + *s = *(s+1) = '='; + *(s+2) = 0; + if (! *(++p)) break; + *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)]; + if (! *(++p)) break; + *(s++) = al[*(p++) & 0x3F]; + } + + return buf; +} +#endif + +#define WGET_OPT_CONTINUE 1 +#define WGET_OPT_QUIET 2 +#define WGET_OPT_PASSIVE 4 +#define WGET_OPT_OUTNAME 8 +#define WGET_OPT_HEADER 16 +#define WGET_OPT_PREFIX 32 +#define WGET_OPT_PROXY 64 + +static const struct option wget_long_options[] = { + { "continue", 0, NULL, 'c' }, + { "quiet", 0, NULL, 'q' }, + { "passive-ftp", 0, NULL, 139 }, + { "output-document", 1, NULL, 'O' }, + { "header", 1, NULL, 131 }, + { "directory-prefix",1, NULL, 'P' }, + { "proxy", 1, NULL, 'Y' }, + { 0, 0, 0, 0 } +}; + +int wget_main(int argc, char **argv) +{ + int n, try=5, status; + unsigned long opt; + int port; + char *proxy = 0; + char *dir_prefix=NULL; + char *s, buf[512]; + struct stat sbuf; + char extra_headers[1024]; + char *extra_headers_ptr = extra_headers; + int extra_headers_left = sizeof(extra_headers); + struct host_info server, target; + struct sockaddr_in s_in; + llist_t *headers_llist = NULL; + + FILE *sfp = NULL; /* socket to web/ftp server */ + FILE *dfp = NULL; /* socket to ftp server (data) */ + char *fname_out = NULL; /* where to direct output (-O) */ + int do_continue = 0; /* continue a prev transfer (-c) */ + long beg_range = 0L; /* range at which continue begins */ + int got_clen = 0; /* got content-length: from server */ + FILE *output; /* socket to web server */ + int quiet_flag = FALSE; /* Be verry, verry quiet... */ + int use_proxy = 1; /* Use proxies if env vars are set */ + char *proxy_flag = "on"; /* Use proxies if env vars are set */ + + /* + * Crack command line. + */ + bb_opt_complementaly = "\203*"; + bb_applet_long_options = wget_long_options; + opt = bb_getopt_ulflags(argc, argv, "cq\213O:\203:P:Y:", &fname_out, &headers_llist, &dir_prefix, &proxy_flag); + if (opt & WGET_OPT_CONTINUE) { + ++do_continue; + } + if (opt & WGET_OPT_QUIET) { + quiet_flag = TRUE; + } + if (strcmp(proxy_flag, "off") == 0) { + /* Use the proxy if necessary. */ + use_proxy = 0; + } + if (opt & WGET_OPT_HEADER) { + while (headers_llist) { + int arglen = strlen(headers_llist->data); + if (extra_headers_left - arglen - 2 <= 0) + bb_error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen); + strcpy(extra_headers_ptr, headers_llist->data); + extra_headers_ptr += arglen; + extra_headers_left -= ( arglen + 2 ); + *extra_headers_ptr++ = '\r'; + *extra_headers_ptr++ = '\n'; + *(extra_headers_ptr + 1) = 0; + headers_llist = headers_llist->link; + } + } + if (argc - optind != 1) + bb_show_usage(); + + parse_url(argv[optind], &target); + server.host = target.host; + server.port = target.port; + + /* + * Use the proxy if necessary. + */ + if (use_proxy) { + proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); + if (proxy && *proxy) { + parse_url(bb_xstrdup(proxy), &server); + } else { + use_proxy = 0; + } + } + + /* Guess an output filename */ + if (!fname_out) { + // Dirty hack. Needed because bb_get_last_path_component + // will destroy trailing / by storing '\0' in last byte! + if(target.path[strlen(target.path)-1]!='/') { + fname_out = +#ifdef CONFIG_FEATURE_WGET_STATUSBAR + curfile = +#endif + bb_get_last_path_component(target.path); + } + if (fname_out==NULL || strlen(fname_out)<1) { + fname_out = +#ifdef CONFIG_FEATURE_WGET_STATUSBAR + curfile = +#endif + "index.html"; + } + if (dir_prefix != NULL) + fname_out = concat_path_file(dir_prefix, fname_out); +#ifdef CONFIG_FEATURE_WGET_STATUSBAR + } else { + curfile = bb_get_last_path_component(fname_out); +#endif + } + if (do_continue && !fname_out) + bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); + + + /* + * Open the output file stream. + */ + if (strcmp(fname_out, "-") == 0) { + output = stdout; + quiet_flag = TRUE; + } else { + output = bb_xfopen(fname_out, (do_continue ? "a" : "w")); + } + + /* + * Determine where to start transfer. + */ + if (do_continue) { + if (fstat(fileno(output), &sbuf) < 0) + bb_perror_msg_and_die("fstat()"); + if (sbuf.st_size > 0) + beg_range = sbuf.st_size; + else + do_continue = 0; + } + + /* We want to do exactly _one_ DNS lookup, since some + * sites (i.e. ftp.us.debian.org) use round-robin DNS + * and we want to connect to only one IP... */ + bb_lookup_host(&s_in, server.host); + s_in.sin_port = server.port; + if (quiet_flag==FALSE) { + fprintf(stdout, "Connecting to %s[%s]:%d\n", + server.host, inet_ntoa(s_in.sin_addr), ntohs(server.port)); + } + + if (use_proxy || !target.is_ftp) { + /* + * HTTP session + */ + do { + got_clen = chunked = 0; + + if (! --try) + close_delete_and_die("too many redirections"); + + /* + * Open socket to http server + */ + if (sfp) fclose(sfp); + sfp = open_socket(&s_in); + + /* + * Send HTTP request. + */ + if (use_proxy) { + const char *format = "GET %stp://%s:%d/%s HTTP/1.1\r\n"; +#ifdef CONFIG_FEATURE_WGET_IP6_LITERAL + if (strchr (target.host, ':')) + format = "GET %stp://[%s]:%d/%s HTTP/1.1\r\n"; +#endif + fprintf(sfp, format, + target.is_ftp ? "f" : "ht", target.host, + ntohs(target.port), target.path); + } else { + fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path); + } + + fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host); + +#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION + if (target.user) { + fprintf(sfp, "Authorization: Basic %s\r\n", + base64enc(target.user, buf, sizeof(buf))); + } + if (use_proxy && server.user) { + fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", + base64enc(server.user, buf, sizeof(buf))); + } +#endif + + if (do_continue) + fprintf(sfp, "Range: bytes=%ld-\r\n", beg_range); + if(extra_headers_left < sizeof(extra_headers)) + fputs(extra_headers,sfp); + fprintf(sfp,"Connection: close\r\n\r\n"); + + /* + * Retrieve HTTP response line and check for "200" status code. + */ +read_response: + if (fgets(buf, sizeof(buf), sfp) == NULL) + close_delete_and_die("no response from server"); + + for (s = buf ; *s != '\0' && !isspace(*s) ; ++s) + ; + for ( ; isspace(*s) ; ++s) + ; + switch (status = atoi(s)) { + case 0: + case 100: + while (gethdr(buf, sizeof(buf), sfp, &n) != NULL); + goto read_response; + case 200: + if (do_continue && output != stdout) + output = freopen(fname_out, "w", output); + do_continue = 0; + break; + case 300: /* redirection */ + case 301: + case 302: + case 303: + break; + case 206: + if (do_continue) + break; + /*FALLTHRU*/ + default: + chomp(buf); + close_delete_and_die("server returned error %d: %s", atoi(s), buf); + } + + /* + * Retrieve HTTP headers. + */ + while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) { + if (strcasecmp(buf, "content-length") == 0) { + unsigned long value; + if (safe_strtoul(s, &value)) { + close_delete_and_die("content-length %s is garbage", s); + } + filesize = value; + got_clen = 1; + continue; + } + if (strcasecmp(buf, "transfer-encoding") == 0) { + if (strcasecmp(s, "chunked") == 0) { + chunked = got_clen = 1; + } else { + close_delete_and_die("server wants to do %s transfer encoding", s); + } + } + if (strcasecmp(buf, "location") == 0) { + if (s[0] == '/') + target.path = bb_xstrdup(s+1); + else { + parse_url(bb_xstrdup(s), &target); + if (use_proxy == 0) { + server.host = target.host; + server.port = target.port; + } + bb_lookup_host(&s_in, server.host); + s_in.sin_port = server.port; + break; + } + } + } + } while(status >= 300); + + dfp = sfp; + } + else + { + /* + * FTP session + */ + if (! target.user) + target.user = bb_xstrdup("anonymous:busybox@"); + + sfp = open_socket(&s_in); + if (ftpcmd(NULL, NULL, sfp, buf) != 220) + close_delete_and_die("%s", buf+4); + + /* + * Splitting username:password pair, + * trying to log in + */ + s = strchr(target.user, ':'); + if (s) + *(s++) = '\0'; + switch(ftpcmd("USER ", target.user, sfp, buf)) { + case 230: + break; + case 331: + if (ftpcmd("PASS ", s, sfp, buf) == 230) + break; + /* FALLTHRU (failed login) */ + default: + close_delete_and_die("ftp login: %s", buf+4); + } + + ftpcmd("CDUP", NULL, sfp, buf); + ftpcmd("TYPE I", NULL, sfp, buf); + + /* + * Querying file size + */ + if (ftpcmd("SIZE /", target.path, sfp, buf) == 213) { + unsigned long value; + if (safe_strtoul(buf+4, &value)) { + close_delete_and_die("SIZE value is garbage"); + } + filesize = value; + got_clen = 1; + } + + /* + * Entering passive mode + */ + if (ftpcmd("PASV", NULL, sfp, buf) != 227) + close_delete_and_die("PASV: %s", buf+4); + s = strrchr(buf, ','); + *s = 0; + port = atoi(s+1); + s = strrchr(buf, ','); + port += atoi(s+1) * 256; + s_in.sin_port = htons(port); + dfp = open_socket(&s_in); + + if (do_continue) { + sprintf(buf, "REST %ld", beg_range); + if (ftpcmd(buf, NULL, sfp, buf) != 350) { + if (output != stdout) + output = freopen(fname_out, "w", output); + do_continue = 0; + } else + filesize -= beg_range; + } + + if (ftpcmd("RETR /", target.path, sfp, buf) > 150) + close_delete_and_die("RETR: %s", buf+4); + + } + + + /* + * Retrieve file + */ + if (chunked) { + fgets(buf, sizeof(buf), dfp); + filesize = strtol(buf, (char **) NULL, 16); + } +#ifdef CONFIG_FEATURE_WGET_STATUSBAR + if (quiet_flag==FALSE) + progressmeter(-1); +#endif + do { + while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, ((chunked || got_clen) && (filesize < sizeof(buf)) ? filesize : sizeof(buf)), dfp)) > 0) { + if (safe_fwrite(buf, 1, n, output) != n) { + bb_perror_msg_and_die("write error"); + } +#ifdef CONFIG_FEATURE_WGET_STATUSBAR + statbytes+=n; +#endif + if (got_clen) { + filesize -= n; + } + } + + if (chunked) { + safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ + safe_fgets(buf, sizeof(buf), dfp); + filesize = strtol(buf, (char **) NULL, 16); + if (filesize==0) { + chunked = 0; /* all done! */ + } + } + + if (n == 0 && ferror(dfp)) { + bb_perror_msg_and_die("network read error"); + } + } while (chunked); +#ifdef CONFIG_FEATURE_WGET_STATUSBAR + if (quiet_flag==FALSE) + progressmeter(1); +#endif + if ((use_proxy == 0) && target.is_ftp) { + fclose(dfp); + if (ftpcmd(NULL, NULL, sfp, buf) != 226) + bb_error_msg_and_die("ftp error: %s", buf+4); + ftpcmd("QUIT", NULL, sfp, buf); + } + exit(EXIT_SUCCESS); +} + + +void parse_url(char *url, struct host_info *h) +{ + char *cp, *sp, *up, *pp; + + if (strncmp(url, "http://", 7) == 0) { + h->port = bb_lookup_port("http", "tcp", 80); + h->host = url + 7; + h->is_ftp = 0; + } else if (strncmp(url, "ftp://", 6) == 0) { + h->port = bb_lookup_port("ftp", "tfp", 21); + h->host = url + 6; + h->is_ftp = 1; + } else + bb_error_msg_and_die("not an http or ftp url: %s", url); + + sp = strchr(h->host, '/'); + if (sp) { + *sp++ = '\0'; + h->path = sp; + } else + h->path = bb_xstrdup(""); + + up = strrchr(h->host, '@'); + if (up != NULL) { + h->user = h->host; + *up++ = '\0'; + h->host = up; + } else + h->user = NULL; + + pp = h->host; + +#ifdef CONFIG_FEATURE_WGET_IP6_LITERAL + if (h->host[0] == '[') { + char *ep; + + ep = h->host + 1; + while (*ep == ':' || isxdigit (*ep)) + ep++; + if (*ep == ']') { + h->host++; + *ep = '\0'; + pp = ep + 1; + } + } +#endif + + cp = strchr(pp, ':'); + if (cp != NULL) { + *cp++ = '\0'; + h->port = htons(atoi(cp)); + } +} + + +FILE *open_socket(struct sockaddr_in *s_in) +{ + FILE *fp; + + fp = fdopen(xconnect(s_in), "r+"); + if (fp == NULL) + bb_perror_msg_and_die("fdopen()"); + + return fp; +} + + +char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc) +{ + char *s, *hdrval; + int c; + + *istrunc = 0; + + /* retrieve header line */ + if (fgets(buf, bufsiz, fp) == NULL) + return NULL; + + /* see if we are at the end of the headers */ + for (s = buf ; *s == '\r' ; ++s) + ; + if (s[0] == '\n') + return NULL; + + /* convert the header name to lower case */ + for (s = buf ; isalnum(*s) || *s == '-' ; ++s) + *s = tolower(*s); + + /* verify we are at the end of the header name */ + if (*s != ':') + bb_error_msg_and_die("bad header line: %s", buf); + + /* locate the start of the header value */ + for (*s++ = '\0' ; *s == ' ' || *s == '\t' ; ++s) + ; + hdrval = s; + + /* locate the end of header */ + while (*s != '\0' && *s != '\r' && *s != '\n') + ++s; + + /* end of header found */ + if (*s != '\0') { + *s = '\0'; + return hdrval; + } + + /* Rats! The buffer isn't big enough to hold the entire header value. */ + while (c = getc(fp), c != EOF && c != '\n') + ; + *istrunc = 1; + return hdrval; +} + +static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf) +{ + if (s1) { + if (!s2) s2=""; + fprintf(fp, "%s%s\r\n", s1, s2); + fflush(fp); + } + + do { + char *buf_ptr; + + if (fgets(buf, 510, fp) == NULL) { + bb_perror_msg_and_die("fgets()"); + } + buf_ptr = strstr(buf, "\r\n"); + if (buf_ptr) { + *buf_ptr = '\0'; + } + } while (! isdigit(buf[0]) || buf[3] != ' '); + + return atoi(buf); +} + +#ifdef CONFIG_FEATURE_WGET_STATUSBAR +/* Stuff below is from BSD rcp util.c, as added to openshh. + * Original copyright notice is retained at the end of this file. + * + */ + + +static int +getttywidth(void) +{ + int width=0; + get_terminal_width_height(0, &width, NULL); + return (width); +} + +static void +updateprogressmeter(int ignore) +{ + int save_errno = errno; + + progressmeter(0); + errno = save_errno; +} + +static void +alarmtimer(int wait) +{ + struct itimerval itv; + + itv.it_value.tv_sec = wait; + itv.it_value.tv_usec = 0; + itv.it_interval = itv.it_value; + setitimer(ITIMER_REAL, &itv, NULL); +} + + +static void +progressmeter(int flag) +{ + static const char prefixes[] = " KMGTP"; + static struct timeval lastupdate; + static off_t lastsize, totalsize; + struct timeval now, td, wait; + off_t cursize, abbrevsize; + double elapsed; + int ratio, barlength, i, remaining; + char buf[256]; + + if (flag == -1) { + (void) gettimeofday(&start, (struct timezone *) 0); + lastupdate = start; + lastsize = 0; + totalsize = filesize; /* as filesize changes.. */ + } + + (void) gettimeofday(&now, (struct timezone *) 0); + cursize = statbytes; + if (totalsize != 0 && !chunked) { + ratio = 100.0 * cursize / totalsize; + ratio = MAX(ratio, 0); + ratio = MIN(ratio, 100); + } else + ratio = 100; + + snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); + + barlength = getttywidth() - 51; + if (barlength > 0) { + i = barlength * ratio / 100; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "|%.*s%*s|", i, + "*****************************************************************************" + "*****************************************************************************", + barlength - i, ""); + } + i = 0; + abbrevsize = cursize; + while (abbrevsize >= 100000 && i < sizeof(prefixes)) { + i++; + abbrevsize >>= 10; + } + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5d %c%c ", + (int) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : + 'B'); + + timersub(&now, &lastupdate, &wait); + if (cursize > lastsize) { + lastupdate = now; + lastsize = cursize; + if (wait.tv_sec >= STALLTIME) { + start.tv_sec += wait.tv_sec; + start.tv_usec += wait.tv_usec; + } + wait.tv_sec = 0; + } + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + + if (wait.tv_sec >= STALLTIME) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " - stalled -"); + } else if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalsize || chunked) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " --:-- ETA"); + } else { + remaining = (int) (totalsize / (statbytes / elapsed) - elapsed); + i = remaining / 3600; + if (i) + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%2d:", i); + else + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " "); + i = remaining % 3600; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%02d:%02d ETA", i / 60, i % 60); + } + write(STDERR_FILENO, buf, strlen(buf)); + + if (flag == -1) { + struct sigaction sa; + sa.sa_handler = updateprogressmeter; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + alarmtimer(1); + } else if (flag == 1) { + alarmtimer(0); + statbytes = 0; + putc('\n', stderr); + } +} +#endif + +/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff, + * much of which was blatantly stolen from openssh. */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: wget.c,v 1.75 2004/10/08 08:27:40 andersen Exp $ + */ + + + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/patches/cmp_n.diff b/busybox/patches/cmp_n.diff new file mode 100644 index 000000000..fc4661cf5 --- /dev/null +++ b/busybox/patches/cmp_n.diff @@ -0,0 +1,377 @@ +Index: coreutils/Config.in +=================================================================== +RCS file: /var/cvs/busybox/coreutils/Config.in,v +retrieving revision 1.24 +diff -u -r1.24 Config.in +--- a/coreutils/Config.in 15 Mar 2004 08:28:19 -0000 1.24 ++++ b/coreutils/Config.in 31 Mar 2004 11:51:17 -0000 +@@ -59,6 +59,21 @@ + cmp is used to compare two files and returns the result + to standard output. + ++config CONFIG_FEATURE_CMP_SKIP ++ bool " Enable optional arguments SKIP1 and SKIP2" ++ default n ++ depends on CONFIG_CMP ++ help ++ SKIP1 and SKIP2 specify how many bytes to ignore ++ at the start of each file. ++ ++config CONFIG_FEATURE_CMP_LIMIT ++ bool " Enable limit of inputs" ++ default n ++ depends on CONFIG_CMP ++ help ++ Enable cmp option (-n). ++ + config CONFIG_CP + bool "cp" + default n +Index: coreutils/cmp.c +=================================================================== +RCS file: /var/cvs/busybox/coreutils/cmp.c,v +retrieving revision 1.9 +diff -u -r1.9 cmp.c +--- a/coreutils/cmp.c 19 Mar 2003 09:11:32 -0000 1.9 ++++ b/coreutils/cmp.c 31 Mar 2004 11:51:17 -0000 +@@ -39,6 +39,12 @@ + #include + #include "busybox.h" + ++#ifdef CONFIG_FEATURE_CMP_SKIP ++#define MAX_OPTIONAL_ARGS 3 ++#else ++#define MAX_OPTIONAL_ARGS 1 ++#endif ++ + static FILE *cmp_xfopen_input(const char *filename) + { + FILE *fp; +@@ -58,12 +64,57 @@ + static const char fmt_l_opt[] = "%.0s%.0s%d %3o %3o\n"; /* nicer gnu format */ + #endif + +-static const char opt_chars[] = "sl"; ++#ifdef CONFIG_FEATURE_CMP_LIMIT ++#define OPTCHR_LIMIT "n:" ++#define OPTARG_LIMIT ,&limit_str ++#else ++#define OPTCHR_LIMIT ++#define OPTARG_LIMIT ++#endif ++ ++static const char opt_chars[] = "sl" OPTCHR_LIMIT; + + enum { + OPT_s = 1, +- OPT_l = 2 ++ OPT_l = 2, ++ OPT_n = 4 ++}; ++ ++#ifdef CONFIG_LFS ++#define SUFFIX_STRUCT suffix_mult64 ++#define PARSE_FUNC bb_xgetllarg10_sfx ++#else ++#define SUFFIX_STRUCT suffix_mult ++#define PARSE_FUNC bb_xgetlarg10_sfx ++#endif ++ ++#if defined(CONFIG_FEATURE_CMP_SKIP) || defined(CONFIG_FEATURE_CMP_LIMIT) ++static const struct SUFFIX_STRUCT suffixes[] = { ++ { "k", 1UL << 10 }, ++ { "M", 1UL << 20 }, ++ { "G", 1UL << 30 }, ++#ifdef CONFIG_LFS ++ { "T", 1ULL << 40 }, ++ { "P", 1ULL << 50 }, ++ { "E", 1ULL << 60 }, ++#endif ++ { NULL, 0 } + }; ++#endif ++ ++#ifdef CONFIG_FEATURE_CMP_SKIP ++static void skip_input(FILE *fp, off_t skip, const char *filename) ++{ ++ if (skip > 0 && fseeko(fp, skip, SEEK_CUR) != 0) { ++ while (skip-- > 0) { ++ if (getc(fp) == EOF) { ++ bb_xferror(fp, filename); ++ break; ++ } ++ } ++ } ++} ++#endif + + int cmp_main(int argc, char **argv) + { +@@ -73,12 +124,26 @@ + int c1, c2, char_pos, line_pos; + int opt_flags; + int exit_val = 0; ++#ifdef CONFIG_FEATURE_CMP_SKIP ++ off_t skip1 = 0, skip2 = 0; ++#endif ++#ifdef CONFIG_FEATURE_CMP_LIMIT ++ off_t limit = -1; ++ char *limit_str; ++#endif + + bb_default_error_retval = 2; /* 1 is returned if files are different. */ + +- opt_flags = bb_getopt_ulflags(argc, argv, opt_chars); ++ opt_flags = bb_getopt_ulflags(argc, argv, opt_chars OPTARG_LIMIT); + +- if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > 1)) { ++#ifdef CONFIG_FEATURE_CMP_LIMIT ++ if (opt_flags & OPT_n) { ++ limit = PARSE_FUNC(limit_str, suffixes); ++ opt_flags &= 3; ++ } ++#endif ++ ++ if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > MAX_OPTIONAL_ARGS)) { + bb_show_usage(); + } + +@@ -87,6 +152,13 @@ + filename2 = "-"; + if (*++argv) { + filename2 = *argv; ++#ifdef CONFIG_FEATURE_CMP_SKIP ++ if (*++argv) { ++ skip1 = PARSE_FUNC(*argv, suffixes); ++ if (*++argv) ++ skip2 = PARSE_FUNC(*argv, suffixes); ++ } ++#endif + } + fp2 = cmp_xfopen_input(filename2); + +@@ -98,6 +170,11 @@ + return 0; + } + ++#ifdef CONFIG_FEATURE_CMP_SKIP ++ skip_input(fp1, skip1, filename1); ++ skip_input(fp2, skip2, filename2); ++#endif ++ + fmt = fmt_differ; + if (opt_flags == OPT_l) { + fmt = fmt_l_opt; +@@ -106,6 +183,10 @@ + char_pos = 0; + line_pos = 1; + do { ++#ifdef CONFIG_FEATURE_CMP_LIMIT ++ if (limit-- == 0) ++ break; ++#endif + c1 = getc(fp1); + c2 = getc(fp2); + ++char_pos; +Index: include/usage.h +=================================================================== +RCS file: /var/cvs/busybox/include/usage.h,v +retrieving revision 1.198 +diff -u -r1.198 usage.h +--- a/include/usage.h 29 Mar 2004 08:20:08 -0000 1.198 ++++ b/include/usage.h 31 Mar 2004 11:51:17 -0000 +@@ -186,14 +186,29 @@ + #define clear_full_usage \ + "Clear screen." + ++#ifdef CONFIG_FEATURE_CMP_SKIP ++#define USAGE_CMP_SKIP(a) a ++#else ++#define USAGE_CMP_SKIP(a) ++#endif ++ ++#ifdef CONFIG_FEATURE_CMP_LIMIT ++#define USAGE_CMP_LIMIT(a) a ++#else ++#define USAGE_CMP_LIMIT(a) ++#endif ++ + #define cmp_trivial_usage \ +- "[OPTION]... FILE1 [FILE2]" ++ "[OPTION]... FILE1 [FILE2" USAGE_CMP_SKIP(" [SKIP1 [SKIP2]]") "]" + #define cmp_full_usage \ +- "Compare files.\n\n" \ ++ "Compare files.\n" \ ++ USAGE_CMP_SKIP("SKIP1 and SKIP2 are the number of bytes to skip in each file.\n") \ ++ "\n" \ + "Options:\n" \ +- "\t-l\tWrite the byte numbers (decimal) and values (octal)\n" \ +- "\t\t for all differing bytes.\n" \ +- "\t-s\tquiet mode - do not print" ++ "\t-l\t\tWrite the byte numbers (decimal) and values (octal)\n" \ ++ "\t\t\t for all differing bytes.\n" \ ++ USAGE_CMP_LIMIT("\t-n LIMIT\tCompare at most LIMIT bytes.\n") \ ++ "\t-s\t\tquiet mode - do not print" + + #define cp_trivial_usage \ + "[OPTION]... SOURCE DEST" +Index: include/libbb.h +=================================================================== +RCS file: /var/cvs/busybox/include/libbb.h,v +retrieving revision 1.129 +diff -u -r1.129 libbb.h +--- a/include/libbb.h 15 Mar 2004 08:28:38 -0000 1.129 ++++ b/include/libbb.h 31 Mar 2004 11:51:17 -0000 +@@ -217,6 +217,21 @@ + const struct suffix_mult *suffixes); + extern long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes); + ++struct suffix_mult64 { ++ const char *suffix; ++ unsigned long long mult; ++}; ++ ++extern unsigned long long bb_xgetullarg_bnd_sfx(const char *arg, int base, ++ unsigned long long lower, ++ unsigned long long upper, ++ const struct suffix_mult64 *suffixes); ++ ++extern long long bb_xgetllarg_bnd_sfx(const char *arg, int base, ++ long long lower, ++ long long upper, ++ const struct suffix_mult64 *suffixes); ++extern long long bb_xgetllarg10_sfx(const char *arg, const struct suffix_mult64 *suffixes); + + //#warning pitchable now? + extern unsigned long bb_xparse_number(const char *numstr, +Index: libbb/Makefile.in +=================================================================== +RCS file: /var/cvs/busybox/libbb/Makefile.in,v +retrieving revision 1.34 +diff -u -r1.34 Makefile.in +--- a/libbb/Makefile.in 6 Mar 2004 22:11:45 -0000 1.34 ++++ b/libbb/Makefile.in 31 Mar 2004 11:51:17 -0000 +@@ -70,7 +70,8 @@ + + LIBBB_MSRC3:=$(LIBBB_DIR)xgetularg.c + LIBBB_MOBJ3:=xgetularg_bnd_sfx.o xgetlarg_bnd_sfx.o getlarg10_sfx.o \ +- xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o ++ xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o \ ++ xgetullarg_bnd_sfx.o xgetllarg_bnd_sfx.o xgetllarg10_sfx.o + + LIBBB_MSRC4:=$(LIBBB_DIR)/safe_strtol.c + LIBBB_MOBJ4:=safe_strtoi.o safe_strtod.o safe_strtol.o safe_strtoul.o +Index: libbb/xgetularg.c +=================================================================== +RCS file: /var/cvs/busybox/libbb/xgetularg.c,v +retrieving revision 1.2 +diff -u -r1.2 xgetularg.c +--- a/libbb/xgetularg.c 15 Mar 2004 08:28:44 -0000 1.2 ++++ b/libbb/xgetularg.c 31 Mar 2004 11:51:17 -0000 +@@ -158,3 +158,106 @@ + return bb_xgetularg10_bnd(arg, 0, ULONG_MAX); + } + #endif ++ ++#ifdef L_xgetullarg_bnd_sfx ++extern ++unsigned long long bb_xgetullarg_bnd_sfx(const char *arg, int base, ++ unsigned long long lower, ++ unsigned long long upper, ++ const struct suffix_mult64 *suffixes) ++{ ++ unsigned long long r; ++ int old_errno; ++ char *e; ++ ++ assert(arg); ++ ++ /* Disallow '-' and any leading whitespace. Speed isn't critical here ++ * since we're parsing commandline args. So make sure we get the ++ * actual isspace function rather than a larger macro implementaion. */ ++ if ((*arg == '-') || (isspace)(*arg)) { ++ bb_show_usage(); ++ } ++ ++ /* Since this is a lib function, we're not allowed to reset errno to 0. ++ * Doing so could break an app that is deferring checking of errno. ++ * So, save the old value so that we can restore it if successful. */ ++ old_errno = errno; ++ errno = 0; ++ r = strtoull(arg, &e, base); ++ /* Do the initial validity check. Note: The standards do not ++ * guarantee that errno is set if no digits were found. So we ++ * must test for this explicitly. */ ++ if (errno || (arg == e)) { /* error or no digits */ ++ bb_show_usage(); ++ } ++ errno = old_errno; /* Ok. So restore errno. */ ++ ++ /* Do optional suffix parsing. Allow 'empty' suffix tables. ++ * Note that we also all nul suffixes with associated multipliers, ++ * to allow for scaling of the arg by some default multiplier. */ ++ ++ if (suffixes) { ++ while (suffixes->suffix) { ++ if (strcmp(suffixes->suffix, e) == 0) { ++ if (ULONG_LONG_MAX / suffixes->mult < r) { /* Overflow! */ ++ bb_show_usage(); ++ } ++ ++e; ++ r *= suffixes->mult; ++ break; ++ } ++ ++suffixes; ++ } ++ } ++ ++ /* Finally, check for illegal trailing chars and range limits. */ ++ /* Note: although we allow leading space (via stroul), trailing space ++ * is an error. It would be easy enough to allow though if desired. */ ++ if (*e || (r < lower) || (r > upper)) { ++ bb_show_usage(); ++ } ++ ++ return r; ++} ++#endif ++ ++#ifdef L_xgetllarg_bnd_sfx ++extern ++long long bb_xgetllarg_bnd_sfx(const char *arg, int base, ++ long long lower, ++ long long upper, ++ const struct suffix_mult64 *suffixes) ++{ ++ unsigned long long u = LONG_LONG_MAX; ++ long long r; ++ const char *p = arg; ++ ++ if ((*p == '-') && (p[1] != '+')) { ++ ++p; ++#if LONG_LONG_MAX == (-(LONG_LONG_MIN + 1)) ++ ++u; /* two's complement */ ++#endif ++ } ++ ++ r = bb_xgetullarg_bnd_sfx(p, base, 0, u, suffixes); ++ ++ if (*arg == '-') { ++ r = -r; ++ } ++ ++ if ((r < lower) || (r > upper)) { ++ bb_show_usage(); ++ } ++ ++ return r; ++} ++#endif ++ ++#ifdef L_xgetllarg10_sfx ++extern ++long long bb_xgetllarg10_sfx(const char *arg, const struct suffix_mult64 *suffixes) ++{ ++ return bb_xgetllarg_bnd_sfx(arg, 10, LONG_LONG_MIN, LONG_LONG_MAX, suffixes); ++} ++#endif diff --git a/busybox/patches/dd_ibs_and_obs.diff b/busybox/patches/dd_ibs_and_obs.diff new file mode 100644 index 000000000..3bbf4b989 --- /dev/null +++ b/busybox/patches/dd_ibs_and_obs.diff @@ -0,0 +1,252 @@ +This patch adds support of ibs= and obs= to dd. +---- +Hideki IWAMOTO h-iwamoto@kit.hi-ho.ne.jp + + +--- a/include/usage.h 29 Mar 2004 08:20:08 -0000 1.198 ++++ b/include/usage.h 4 Apr 2004 07:15:21 -0000 +@@ -309,13 +309,15 @@ + "64\n" + + #define dd_trivial_usage \ +- "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \ +- "\t [seek=N] [conv=notrunc|noerror|sync]" ++ "[if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N]\n" \ ++ "\t [skip=N] [seek=N] [conv=notrunc|noerror|sync]" + #define dd_full_usage \ + "Copy a file, converting and formatting according to options\n\n" \ + "\tif=FILE\t\tread from FILE instead of stdin\n" \ + "\tof=FILE\t\twrite to FILE instead of stdout\n" \ +- "\tbs=N\t\tread and write N bytes at a time\n" \ ++ "\tibs=N\t\tread N bytes at a time\n" \ ++ "\tobs=N\t\twrite N bytes at a time\n" \ ++ "\tbs=N\t\tforce ibs=N and obs=N\n" \ + "\tcount=N\t\tcopy only N input blocks\n" \ + "\tskip=N\t\tskip N input blocks\n" \ + "\tseek=N\t\tskip N output blocks\n" \ +@@ -323,6 +325,8 @@ + "\tconv=noerror\tcontinue after read errors\n" \ + "\tconv=sync\tpad blocks with zeros\n" \ + "\n" \ ++ "If the bs= expr operand is not specified, the input is processed and collected\n" \ ++ "into full-sized output blocks until the end of the input is reached.\n" \ + "Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\n" \ + "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)." + #define dd_example_usage \ +--- a/coreutils/dd.c 30 Jan 2004 22:24:32 -0000 1.55 ++++ b/coreutils/dd.c 4 Apr 2004 07:15:21 -0000 +@@ -30,6 +30,10 @@ + #include + #include "busybox.h" + ++#define C_NOERROR 0x0001 ++#define C_TRUNC 0x0002 ++#define C_SYNC 0x0004 ++#define C_TWOBUFS 0x0008 + + static const struct suffix_mult dd_suffixes[] = { + { "c", 1 }, +@@ -51,13 +55,13 @@ + size_t in_full = 0; + size_t in_part = 0; + size_t count = -1; +- size_t bs = 512; ++ size_t ibs = 512; ++ size_t obs = 512; ++ size_t oc = 0; + ssize_t n; + off_t seek = 0; + off_t skip = 0; +- int sync_flag = FALSE; +- int noerror = FALSE; +- int trunc_flag = TRUE; ++ unsigned int dd_flags = C_TWOBUFS | C_TRUNC; + int oflag; + int ifd; + int ofd; +@@ -65,11 +69,18 @@ + const char *infile = NULL; + const char *outfile = NULL; + char *buf; ++ char *ibuf; ++ char *obuf; + + for (i = 1; i < argc; i++) { +- if (strncmp("bs=", argv[i], 3) == 0) +- bs = bb_xparse_number(argv[i]+3, dd_suffixes); +- else if (strncmp("count=", argv[i], 6) == 0) ++ if (strncmp("ibs=", argv[i], 4) == 0) ++ ibs = bb_xparse_number(argv[i]+4, dd_suffixes); ++ else if (strncmp("obs=", argv[i], 4) == 0) ++ obs = bb_xparse_number(argv[i]+4, dd_suffixes); ++ else if (strncmp("bs=", argv[i], 3) == 0) { ++ ibs = obs = bb_xparse_number(argv[i]+3, dd_suffixes); ++ dd_flags &= ~C_TWOBUFS; ++ } else if (strncmp("count=", argv[i], 6) == 0) + count = bb_xparse_number(argv[i]+6, dd_suffixes); + else if (strncmp("seek=", argv[i], 5) == 0) + seek = bb_xparse_number(argv[i]+5, dd_suffixes); +@@ -83,13 +94,13 @@ + buf = argv[i]+5; + while (1) { + if (strncmp("notrunc", buf, 7) == 0) { +- trunc_flag = FALSE; ++ dd_flags &= ~C_TRUNC; + buf += 7; + } else if (strncmp("sync", buf, 4) == 0) { +- sync_flag = TRUE; ++ dd_flags |= C_SYNC; + buf += 4; + } else if (strncmp("noerror", buf, 7) == 0) { +- noerror = TRUE; ++ dd_flags |= C_NOERROR; + buf += 7; + } else { + bb_error_msg_and_die("invalid conversion `%s'", argv[i]+5); +@@ -103,7 +114,12 @@ + bb_show_usage(); + } + +- buf = xmalloc(bs); ++ ibuf = xmalloc(ibs); ++ ++ if (dd_flags & C_TWOBUFS) ++ obuf = xmalloc(obs); ++ else ++ obuf = ibuf; + + if (infile != NULL) { + ifd = bb_xopen(infile, O_RDONLY); +@@ -115,7 +131,7 @@ + if (outfile != NULL) { + oflag = O_WRONLY | O_CREAT; + +- if (!seek && trunc_flag) { ++ if (!seek && (dd_flags & C_TRUNC)) { + oflag |= O_TRUNC; + } + +@@ -123,8 +139,8 @@ + bb_perror_msg_and_die("%s", outfile); + } + +- if (seek && trunc_flag) { +- if (ftruncate(ofd, seek * bs) < 0) { ++ if (seek && (dd_flags & C_TRUNC)) { ++ if (ftruncate(ofd, seek * obs) < 0) { + struct stat st; + + if (fstat (ofd, &st) < 0 || S_ISREG (st.st_mode) || +@@ -139,52 +155,88 @@ + } + + if (skip) { +- if (lseek(ifd, skip * bs, SEEK_CUR) < 0) { +- bb_perror_msg_and_die("%s", infile); ++ if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) { ++ while (skip-- > 0) { ++ n = safe_read(ifd, ibuf, ibs); ++ if (n < 0) ++ bb_perror_msg_and_die("%s", infile); ++ if (n == 0) ++ break; ++ } + } + } + + if (seek) { +- if (lseek(ofd, seek * bs, SEEK_CUR) < 0) { ++ if (lseek(ofd, seek * obs, SEEK_CUR) < 0) { + bb_perror_msg_and_die("%s", outfile); + } + } + + while (in_full + in_part != count) { +- if (noerror) { ++ if (dd_flags & C_NOERROR) { + /* Pre-zero the buffer when doing the noerror thing */ +- memset(buf, '\0', bs); ++ memset(ibuf, '\0', ibs); ++ } ++ ++ n = safe_read(ifd, ibuf, ibs); ++ if (n == 0) { ++ break; + } +- n = safe_read(ifd, buf, bs); + if (n < 0) { +- if (noerror) { +- n = bs; ++ if (dd_flags & C_NOERROR) { ++ n = ibs; + bb_perror_msg("%s", infile); + } else { + bb_perror_msg_and_die("%s", infile); + } + } +- if (n == 0) { +- break; +- } +- if (n == bs) { ++ if (n == ibs) { + in_full++; + } else { + in_part++; ++ if (dd_flags & C_SYNC) { ++ memset(ibuf + n, '\0', ibs - n); ++ n = ibs; ++ } + } +- if (sync_flag) { +- memset(buf + n, '\0', bs - n); +- n = bs; ++ ++ if (dd_flags & C_TWOBUFS) { ++ size_t d; ++ char *tmp = ibuf; ++ ++ while (n) { ++ d = obs - oc; ++ if (d > n) ++ d = n; ++ memcpy(obuf + oc, tmp, d); ++ n -= d; ++ tmp += d; ++ oc += d; ++ if (oc == obs) { ++ if (bb_full_write(ofd, obuf, obs) < 0) { ++ bb_perror_msg_and_die("%s", outfile); ++ } ++ out_full++; ++ oc = 0; ++ } ++ } ++ } else { ++ if (bb_full_write(ofd, ibuf, n) < 0) { ++ bb_perror_msg_and_die("%s", outfile); ++ } ++ if (n == ibs) { ++ out_full++; ++ } else { ++ out_part++; ++ } + } +- n = bb_full_write(ofd, buf, n); +- if (n < 0) { ++ } ++ ++ if (oc) { ++ if (bb_full_write(ofd, obuf, oc) < 0) { + bb_perror_msg_and_die("%s", outfile); + } +- if (n == bs) { +- out_full++; +- } else { +- out_part++; +- } ++ out_part++; + } + + if (close (ifd) < 0) { + + diff --git a/busybox/patches/eject.diff b/busybox/patches/eject.diff new file mode 100644 index 000000000..fcc234d02 --- /dev/null +++ b/busybox/patches/eject.diff @@ -0,0 +1,164 @@ +Index: AUTHORS +=================================================================== +RCS file: /var/cvs/busybox/AUTHORS,v +retrieving revision 1.40 +diff -u -r1.40 AUTHORS +--- a/AUTHORS 9 Oct 2003 21:19:21 -0000 1.40 ++++ b/AUTHORS 5 Mar 2004 07:23:17 -0000 +@@ -8,6 +8,9 @@ + + ----------- + ++Peter Willis ++ eject ++ + Emanuele Aina + run-parts + +Index: coreutils/Config.in +=================================================================== +RCS file: /var/cvs/busybox/coreutils/Config.in,v +retrieving revision 1.23 +diff -u -r1.23 Config.in +--- a/coreutils/Config.in 5 Mar 2004 06:47:25 -0000 1.23 ++++ b/coreutils/Config.in 5 Mar 2004 07:23:18 -0000 +@@ -164,6 +164,13 @@ + a command; without options it displays the current + environment. + ++config CONFIG_EJECT ++ bool "eject" ++ default n ++ help ++ ejects a cdrom drive. ++ defaults to /dev/cdrom ++ + config CONFIG_EXPR + bool "expr" + default n +Index: coreutils/Makefile.in +=================================================================== +RCS file: /var/cvs/busybox/coreutils/Makefile.in,v +retrieving revision 1.8 +diff -u -r1.8 Makefile.in +--- a/coreutils/Makefile.in 27 Jan 2004 09:22:20 -0000 1.8 ++++ b/coreutils/Makefile.in 5 Mar 2004 07:23:18 -0000 +@@ -41,6 +41,7 @@ + COREUTILS-$(CONFIG_DU) += du.o + COREUTILS-$(CONFIG_ECHO) += echo.o + COREUTILS-$(CONFIG_ENV) += env.o ++COREUTILS-$(CONFIG_EJECT) += eject.o + COREUTILS-$(CONFIG_EXPR) += expr.o + COREUTILS-$(CONFIG_FALSE) += false.o + COREUTILS-$(CONFIG_FOLD) += fold.o +Index: coreutils/eject.c +=================================================================== +RCS file: coreutils/eject.c +diff -N coreutils/eject.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ b/coreutils/eject.c 5 Mar 2004 07:23:21 -0000 +@@ -0,0 +1,66 @@ ++/* ++ * eject implementation for busybox ++ * ++ * Copyright (C) 2004 Peter Willis ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++/* ++ * This is a simple hack of eject based on something Erik posted in #uclibc. ++ * Most of the dirty work blatantly ripped off from cat.c =) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "busybox.h" ++#include // needs to be after busybox.h or compile problems arise ++ ++#define DEFAULT_CDROM "/dev/cdrom" ++ ++extern int eject_main(int argc, char **argv) ++{ ++ int fd; ++ int flag = CDROMEJECT; ++ int i = 1; ++ char *device = NULL; ++ ++ /* ++ * i'm too lazy to learn bb_getopt_ulflags and this is obscenely large ++ * for just some argument parsing so mjn3 can clean it up later. ++ * sorry, but PlumpOS 7.0-pre2 needs this asap :-/ ++ */ ++ while (++i <= argc) { ++ if ( (! strncmp(argv[i-1],"-t",2)) || (! strncmp(argv[i-1],"--trayclose",11)) ) { ++ flag = CDROMCLOSETRAY; ++ } else { ++ device = argv[i-1]; ++ } ++ } ++ if ( (fd = open(device == NULL ? DEFAULT_CDROM : device, O_RDONLY | O_NONBLOCK) ) < 0 ) { ++ perror("eject: Can't open device"); ++ return(EXIT_FAILURE); ++ } ++ if (ioctl(fd, flag)) { ++ perror("eject: Can't eject cdrom"); ++ return(EXIT_FAILURE); ++ } ++ return EXIT_SUCCESS; ++} +Index: include/applets.h +=================================================================== +RCS file: /var/cvs/busybox/include/applets.h,v +retrieving revision 1.111 +diff -u -r1.111 applets.h +--- a/include/applets.h 27 Jan 2004 09:22:20 -0000 1.111 ++++ b/include/applets.h 5 Mar 2004 07:23:21 -0000 +@@ -178,6 +178,9 @@ + #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS) + APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER) + #endif ++#ifdef CONFIG_EJECT ++ APPLET(eject, eject_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) ++#endif + #ifdef CONFIG_ENV + APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) + #endif +Index: include/usage.h +=================================================================== +RCS file: /var/cvs/busybox/include/usage.h,v +retrieving revision 1.191 +diff -u -r1.191 usage.h +--- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191 ++++ b/include/usage.h 5 Mar 2004 07:23:29 -0000 +@@ -537,6 +537,13 @@ + "\t-, -i\tstart with an empty environment\n" \ + "\t-u\tremove variable from the environment\n" + ++#define eject_trivial_usage \ ++ "[-t] [FILE]" ++#define eject_full_usage \ ++ "Ejects the specified FILE or /dev/cdrom if FILE is unspecified.\n\n" \ ++ "Options:\n" \ ++ "\t-t, --trayclose \tclose tray\n" ++ + #define expr_trivial_usage \ + "EXPRESSION" + #define expr_full_usage \ diff --git a/busybox/patches/makdevs_table.diff b/busybox/patches/makdevs_table.diff new file mode 100644 index 000000000..1041608b7 --- /dev/null +++ b/busybox/patches/makdevs_table.diff @@ -0,0 +1,294 @@ +Index: include/usage.h +=================================================================== +RCS file: /var/cvs/busybox/include/usage.h,v +retrieving revision 1.211 +diff -u -r1.211 usage.h +--- a/include/usage.h 26 May 2004 22:09:37 -0000 1.211 ++++ b/include/usage.h 5 Jun 2004 07:51:26 -0000 +@@ -1536,6 +1536,7 @@ + #define lsmod_full_usage \ + "List the currently loaded kernel modules." + ++#ifdef CONFIG_FEATURE_MAKEDEVS_LEAF + #define makedevs_trivial_usage \ + "NAME TYPE MAJOR MINOR FIRST LAST [s]" + #define makedevs_full_usage \ +@@ -1555,6 +1556,18 @@ + "[creates ttyS2-ttyS63]\n" \ + "# makedevs /dev/hda b 3 0 0 8 s\n" \ + "[creates hda,hda1-hda8]\n" ++#endif ++ ++#ifdef CONFIG_FEATURE_MAKEDEVS_TABLE ++#define makedevs_trivial_usage \ ++ "[-r rootdir] [device_table]" ++#define makedevs_full_usage \ ++ "Creates a batch of special files as specified in a device table\n" \ ++ "The device table has one line per device group, each group is of\n" \ ++ "the format\n" \ ++ "\ttype mode user group major minor start increment count\n" \ ++ "a '-' may be used for blank entries\n" ++#endif + + #ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK + #define USAGE_MD5_SHA1_SUM_CHECK(a) a +Index: miscutils/Config.in +=================================================================== +RCS file: /var/cvs/busybox/miscutils/Config.in,v +retrieving revision 1.14 +diff -u -r1.14 Config.in +--- a/miscutils/Config.in 15 Mar 2004 08:28:46 -0000 1.14 ++++ b/miscutils/Config.in 5 Jun 2004 07:51:26 -0000 +@@ -143,10 +143,32 @@ + bool "makedevs" + default n + help +- 'makedevs' is a utility used and created by the Linux Router Project. +- It creates a large number of device special files (/dev devices) +- rather quickly, and can be considerably faster then running mknod a +- zillion times. ++ 'makedevs' is a utility used to create a batch of devices with ++ one command. ++ . ++ There are two choices for command line behaviour, the interface ++ as used by LEAF/Linux Router Project, or a device table file. ++ . ++ 'leaf' is traditionally what busybox follows, it allows multiple ++ devices of a particluar type to be created per command. ++ e.g. /dev/hda[0-9] ++ Device properties are passed as command line arguments. ++ . ++ 'table' reads device properties from a file or stdin, allowing ++ a batch of unrelated devices to be makde with one command. ++ User/group names are allowed as an alternative to uid/gid. ++ ++choice ++ prompt "Choose makedevs behaviour" ++ default CONFIG_FEATURE_MAKDEVS_TABLE ++ ++config CONFIG_FEATURE_MAKEDEVS_LEAF ++ bool "leaf" ++ ++config CONFIG_FEATURE_MAKEDEVS_TABLE ++ bool "table" ++ ++endchoice + + config CONFIG_MT + bool "mt" +Index: miscutils/makedevs.c +=================================================================== +RCS file: /var/cvs/busybox/miscutils/makedevs.c,v +retrieving revision 1.16 +diff -u -r1.16 makedevs.c +--- a/miscutils/makedevs.c 15 Mar 2004 08:28:46 -0000 1.16 ++++ b/miscutils/makedevs.c 5 Jun 2004 07:51:26 -0000 +@@ -1,20 +1,27 @@ + /* vi: set sw=4 ts=4: */ +-/* +- * public domain -- Dave 'Kill a Cop' Cinege +- * +- * makedevs +- * Make ranges of device files quickly. +- * known bugs: can't deal with alpha ranges +- */ + ++#include ++ ++#include ++#include + #include + #include + #include +-#include ++#include + #include +-#include ++ + #include "busybox.h" + ++#ifdef CONFIG_FEATURE_MAKEDEVS_LEAF ++ ++/* ++ * public domain -- Dave 'Kill a Cop' Cinege ++ * ++ * makedevs ++ * Make ranges of device files quickly. ++ * known bugs: can't deal with alpha ranges ++ */ ++ + int makedevs_main(int argc, char **argv) + { + mode_t mode; +@@ -69,24 +76,153 @@ + return 0; + } + ++#elif defined CONFIG_FEATURE_MAKEDEVS_TABLE ++ + /* +-And this is what this program replaces. The shell is too slow! ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++static const struct option makedevs_long_options[] = { ++ {"root", 1, NULL, 'r'}, ++ {0, 0, 0, 0} ++}; + +-makedev () { +-local basedev=$1; local S=$2; local E=$3 +-local major=$4; local Sminor=$5; local type=$6 +-local sbase=$7 +- +- if [ ! "$sbase" = "" ]; then +- mknod "$basedev" $type $major $Sminor +- S=`expr $S + 1` +- Sminor=`expr $Sminor + 1` +- fi +- +- while [ $S -le $E ]; do +- mknod "$basedev$S" $type $major $Sminor +- S=`expr $S + 1` +- Sminor=`expr $Sminor + 1` +- done ++extern int makedevs_main(int argc, char **argv) ++{ ++ FILE *table; ++ int opt; ++ char *rootdir = "./"; ++ char *line; ++ int ret = EXIT_SUCCESS; ++ ++ bb_opt_complementaly = "d~r"; ++ bb_applet_long_options = makedevs_long_options; ++ opt = bb_getopt_ulflags(argc, argv, "d:r:", &rootdir, &rootdir); ++ ++ if (optind + 1 == argc) { ++ table = bb_xfopen(argv[optind], "r"); ++ } else { ++ table = stdin; ++ } ++ ++ if (chdir(rootdir) == -1) { ++ bb_perror_msg_and_die("Couldnt chdor to %s", rootdir); ++ } ++ ++ umask(0); ++ ++ while ((line = bb_get_chomped_line_from_file(table))) { ++ char type; ++ unsigned int mode = 0755; ++ unsigned int major = 0; ++ unsigned int minor = 0; ++ unsigned int count = 0; ++ unsigned int increment = 0; ++ unsigned int start = 0; ++ char name[41]; ++ char user[41]; ++ char group[41]; ++ char *full_name; ++ uid_t uid; ++ gid_t gid; ++ ++ if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", name, ++ &type, &mode, user, group, &major, ++ &minor, &start, &increment, &count)) || ++ ((major | minor | start | count | increment) > 255)) { ++ bb_error_msg("Ignoring invalid line\n%s\n", line); ++ ret = EXIT_FAILURE; ++ continue; ++ } ++ if (name[0] == '#') { ++ continue; ++ } ++ if (group) { ++ gid = get_ug_id(group, my_getgrnam); ++ } else { ++ gid = getgid(); ++ } ++ if (user) { ++ uid = get_ug_id(user, my_getpwnam); ++ } else { ++ uid = getuid(); ++ } ++ full_name = concat_path_file(rootdir, name); ++ ++ if (type == 'd') { ++ bb_make_directory(full_name, mode | S_IFDIR, 0); ++ if (chown(full_name, uid, gid) == -1) { ++ bb_perror_msg("chown failed for %s", full_name); ++ ret = EXIT_FAILURE; ++ goto loop; ++ } ++ } else { ++ dev_t rdev; ++ ++ if (type == 'p') { ++ mode |= S_IFIFO; ++ } ++ else if (type == 'c') { ++ mode |= S_IFCHR; ++ } ++ else if (type == 'b') { ++ mode |= S_IFBLK; ++ } else { ++ bb_error_msg("Unsupported file type %c", type); ++ ret = EXIT_FAILURE; ++ goto loop; ++ } ++ ++ if (count > 0) { ++ int i; ++ char *full_name_inc; ++ ++ full_name_inc = xmalloc(strlen(full_name) + 4); ++ for (i = start; i < count; i++) { ++ sprintf(full_name_inc, "%s%d", full_name, i); ++ rdev = (major << 8) + minor + (i * increment - start); ++ if (mknod(full_name_inc, mode, rdev) == -1) { ++ bb_perror_msg("Couldnt create node %s", full_name_inc); ++ ret = EXIT_FAILURE; ++ } ++ else if (chown(full_name_inc, uid, gid) == -1) { ++ bb_perror_msg("chown failed for %s", full_name_inc); ++ ret = EXIT_FAILURE; ++ } ++ } ++ free(full_name_inc); ++ } else { ++ rdev = (major << 8) + minor; ++ if (mknod(full_name, mode, rdev) == -1) { ++ bb_perror_msg("Couldnt create node %s", full_name); ++ ret = EXIT_FAILURE; ++ } ++ else if (chown(full_name, uid, gid) == -1) { ++ bb_perror_msg("chown failed for %s", full_name); ++ ret = EXIT_FAILURE; ++ } ++ } ++ } ++loop: ++ free(line); ++ free(full_name); ++ } ++ fclose(table); ++ ++ return 0; + } +-*/ ++#else ++# error makdedevs configuration error, either leaf or table must be selected ++#endif diff --git a/busybox/patches/rpm2cpio_bzip2.patch b/busybox/patches/rpm2cpio_bzip2.patch new file mode 100644 index 000000000..151dd9fb4 --- /dev/null +++ b/busybox/patches/rpm2cpio_bzip2.patch @@ -0,0 +1,63 @@ +diff -ur busybox/archival/Config.in busybox/archival/Config.in +--- busybox/archival/Config.in Sun May 23 09:15:37 2004 ++++ busybox/archival/Config.in Sun May 23 09:15:58 2004 +@@ -127,6 +127,14 @@ + help + Converts an RPM file into a CPIO archive. + ++config CONFIG_FEATURE_RPM2CPIO_BZIP2 ++ bool " Support bzip2 decompression" ++ default n ++ depends on CONFIG_RPM2CPIO ++ help ++ If you enable this option you'll be able to extract ++ rpms compressed with bzip2. ++ + config CONFIG_RPM + bool "rpm" + default n +diff -ur busybox/archival/libunarchive/Makefile.in busybox/archival/libunarchive/Makefile.in +--- busybox/archival/libunarchive/Makefile.in Sun May 23 09:15:04 2004 ++++ busybox/archival/libunarchive/Makefile.in Sun May 23 09:16:42 2004 +@@ -65,6 +65,7 @@ + LIBUNARCHIVE-$(CONFIG_GUNZIP) += $(GUNZIP_FILES) + LIBUNARCHIVE-$(CONFIG_FEATURE_GUNZIP_UNCOMPRESS) += decompress_uncompress.o + LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += $(GUNZIP_FILES) get_header_cpio.o ++LIBUNARCHIVE-$(CONFIG_FEATURE_RPM2CPIO_BZIP2) += decompress_bunzip2.o + LIBUNARCHIVE-$(CONFIG_RPM) += $(GUNZIP_FILES) get_header_cpio.o + LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o + LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_BZIP2) += decompress_bunzip2.o get_header_tar_bz2.o +diff -ur busybox/archival/rpm2cpio.c busybox/archival/rpm2cpio.c +--- busybox/archival/rpm2cpio.c Sun May 23 09:15:04 2004 ++++ busybox/archival/rpm2cpio.c Sun May 23 09:19:03 2004 +@@ -91,14 +91,26 @@ + skip_header(rpm_fd); + + bb_xread_all(rpm_fd, &magic, 2); +- if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) { +- bb_error_msg_and_die("Invalid gzip magic"); ++ if ((magic[0] == 0x1f) || (magic[1] == 0x8b)) { ++ check_header_gzip(rpm_fd); ++ if (inflate_gunzip(rpm_fd, fileno(stdout)) != 0) ++ bb_error_msg("Error inflating (gzip)"); + } + +- check_header_gzip(rpm_fd); +- if (inflate_gunzip(rpm_fd, STDOUT_FILENO) != 0) { +- bb_error_msg("Error inflating"); ++ if ((magic[0] == 'B') && (magic[1] == 'Z')) { ++#ifdef CONFIG_FEATURE_RPM2CPIO_BZIP2 ++ /* return to position before magic (eek..!) */ ++ lseek(rpm_fd, -2, SEEK_CUR); ++ if(uncompressStream(rpm_fd, fileno(stdout)) != 0) ++ bb_error_msg("Error inflating (bzip2)"); ++#else ++ bb_error_msg_and_die("bzip2 not supported"); ++#endif + } ++ ++ else ++ bb_error_msg_and_die("not gzip or bzip2 compressed"); ++ + + close(rpm_fd); diff --git a/busybox/patches/tftp_timeout_multicast.diff b/busybox/patches/tftp_timeout_multicast.diff new file mode 100644 index 000000000..ca431fc60 --- /dev/null +++ b/busybox/patches/tftp_timeout_multicast.diff @@ -0,0 +1,1053 @@ +Index: AUTHORS +=================================================================== +RCS file: /var/cvs/busybox/AUTHORS,v +retrieving revision 1.40 +diff -u -r1.40 AUTHORS +--- a/AUTHORS 9 Oct 2003 21:19:21 -0000 1.40 ++++ b/AUTHORS 5 Mar 2004 15:45:47 -0000 +@@ -92,6 +92,9 @@ + Original author of BusyBox in 1995, 1996. Some of his code can + still be found hiding here and there... + ++John Powers ++ Added multicast option (rfc2090) and timeout option (rfc2349) to tftp. ++ + Tim Riker + bug fixes, member of fan club + +Index: include/usage.h +=================================================================== +RCS file: /var/cvs/busybox/include/usage.h,v +retrieving revision 1.191 +diff -u -r1.191 usage.h +--- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191 ++++ b/include/usage.h 5 Mar 2004 15:45:59 -0000 +@@ -2492,6 +2492,21 @@ + #else + #define USAGE_TFTP_BS(a) + #endif ++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT ++ #define USAGE_TFTP_TIMEOUT(a) a ++#else ++ #define USAGE_TFTP_TIMEOUT(a) ++#endif ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ #define USAGE_TFTP_MULTICAST(a) a ++#else ++ #define USAGE_TFTP_MULTICAST(a) ++#endif ++#ifdef CONFIG_FEATURE_TFTP_DEBUG ++ #define USAGE_TFTP_DEBUG(a) a ++#else ++ #define USAGE_TFTP_DEBUG(a) ++#endif + + #define tftp_trivial_usage \ + "[OPTION]... HOST [PORT]" +@@ -2508,6 +2523,16 @@ + ) \ + USAGE_TFTP_BS( \ + "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \ ++ ) \ ++ USAGE_TFTP_TIMEOUT( \ ++ "\t-T SEC\tClient timeout SEC seconds (default: 5).\n" \ ++ "\t-t SEC\tServer timeout SEC seconds\n" \ ++ ) \ ++ USAGE_TFTP_MULTICAST( \ ++ "\t-m\tMulticast get file.\n" \ ++ ) \ ++ USAGE_TFTP_DEBUG( \ ++ "\t-D\tPrint debug messages.\n" \ + ) + #define time_trivial_usage \ + "[OPTION]... COMMAND [ARGS...]" +Index: networking/Config.in +=================================================================== +RCS file: /var/cvs/busybox/networking/Config.in,v +retrieving revision 1.27 +diff -u -r1.27 Config.in +--- a/networking/Config.in 22 Feb 2004 12:25:47 -0000 1.27 ++++ b/networking/Config.in 5 Mar 2004 15:45:59 -0000 +@@ -522,6 +522,13 @@ + Add support for the GET command within the TFTP client. This allows + a client to retrieve a file from a TFTP server. + ++config CONFIG_FEATURE_TFTP_MULTICAST ++ bool " Enable \"multicast\" option" ++ default n ++ depends on CONFIG_FEATURE_TFTP_GET ++ help ++ Allow the client to receive multicast file transfers. ++ + config CONFIG_FEATURE_TFTP_PUT + bool " Enable \"put\" command" + default y +@@ -531,12 +538,19 @@ + a client to transfer a file to a TFTP server. + + config CONFIG_FEATURE_TFTP_BLOCKSIZE +- bool " Enable \"blocksize\" command" ++ bool " Enable \"blksize\" option" + default n + depends on CONFIG_TFTP + help + Allow the client to specify the desired block size for transfers. + ++config CONFIG_FEATURE_TFTP_TIMEOUT ++ bool " Enable \"timeout\" option" ++ default n ++ depends on CONFIG_TFTP ++ help ++ Allow the client to negotiate timeout option with server. ++ + config CONFIG_FEATURE_TFTP_DEBUG + bool " Enable debug" + default n +Index: networking/tftp.c +=================================================================== +RCS file: /var/cvs/busybox/networking/tftp.c,v +retrieving revision 1.25 +diff -u -r1.25 tftp.c +--- a/networking/tftp.c 5 Mar 2004 13:04:39 -0000 1.25 ++++ b/networking/tftp.c 5 Mar 2004 15:46:00 -0000 +@@ -1,11 +1,26 @@ ++/* vi: set sw=4 ts=4: */ + /* ------------------------------------------------------------------------- */ + /* tftp.c */ ++/* Copyright (c) 2003, 2004 Texas Instruments */ ++/* */ ++/* This package is free software; you can redistribute it and/or */ ++/* modify it under the terms of the license found in the file */ ++/* named COPYING that should have accompanied this file. */ ++/* */ ++/* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ ++/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ ++/* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ + /* */ + /* A simple tftp client for busybox. */ + /* Tries to follow RFC1350. */ + /* Only "octet" mode supported. */ + /* Optional blocksize negotiation (RFC2347 + RFC2348) */ + /* */ ++/* New features added at Texas Instruments, October 2003 */ ++/* Author: John Powers */ ++/* Multicast option: rfc2090 */ ++/* Timeout option: rfc2349 */ ++/* */ + /* Copyright (C) 2001 Magnus Damm */ + /* */ + /* Parts of the code based on: */ +@@ -46,8 +61,20 @@ + + #include "busybox.h" + ++#if defined(CONFIG_FEATURE_TFTP_BLOCKSIZE) || defined(CONFIG_FEATURE_TFTP_MULTICAST) || defined(CONFIG_FEATURE_TFTP_TIMEOUT) ++ #define TFTP_OPTIONS ++#endif ++ + //#define CONFIG_FEATURE_TFTP_DEBUG + ++#ifdef CONFIG_FEATURE_TFTP_DEBUG ++ static void printtime(void); ++ #define dprintf(fmt...) if (debug) {printtime(); printf(fmt);} ++ int debug = 0; ++#else ++ #define dprintf(fmt...) ++#endif ++ + #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ + #define TFTP_TIMEOUT 5 /* seconds */ + +@@ -68,12 +95,24 @@ + "Illegal TFTP operation", + "Unknown transfer ID", + "File already exists", +- "No such user" ++ "No such user", ++#ifdef TFTP_OPTIONS ++ "Unsupported option", ++#endif + }; + + const int tftp_cmd_get = 1; + const int tftp_cmd_put = 2; + ++ ++struct tftp_option { ++ int multicast; ++ int blksize; ++ int client_timeout; ++ int server_timeout; ++}; ++ ++ + #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + + static int tftp_blocksize_check(int blocksize, int bufsize) +@@ -93,16 +132,158 @@ + return blocksize; + } + ++#endif ++ ++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT ++ ++static int ++tftp_timeout_check(int timeout) ++{ ++ /* Check if timeout seconds is valid: ++ * RFC2349 says between 1 and 255. ++ */ ++ ++ if (timeout < 1 || timeout > 255) { ++ bb_error_msg("bad timeout value"); ++ return 0; ++ } ++ return timeout; ++} ++ ++#endif ++ ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++static int ++tftp_multicast_check(const char *opt, char **phost, unsigned short *pport, int *pactive) ++{ ++ /* Option string contains comma delimited addr,port,active. ++ * addr = multicast IP address ++ * port = port number ++ * active = 1 if active client ++ * 0 if passive client ++ * ++ * Addr and port will be empty fields when the server notifies a ++ * passive client that it is now the active client. ++ * ++ * The host address string must be freed by the caller. Neither host ++ * nor port will be set/changed if the input fields are empty. ++ * ++ * If any tokenization errors occur in the opt string, the host ++ * address string is automatically freed. ++ * ++ * Return 0 if any tokenization error, 1 if all parameters are good. ++ */ ++ ++ char *token = NULL; ++ char *parse_buf = NULL; ++ char *tokenv = NULL; ++ char *host = NULL; ++ int port; ++ int active; ++ ++ parse_buf = bb_xstrdup(opt); ++ ++ dprintf("multicast option=%s\n", opt); ++ ++ /* IP address */ ++ if ((token = strtok_r(parse_buf, ",", &tokenv)) == NULL) { ++ dprintf("tftp_multicast_check: cannot parse IP address from %s\n", parse_buf); ++ free(parse_buf); ++ return 0; ++ } ++ if (strlen(token) > 0) ++ *phost = host = bb_xstrdup(token); ++ ++ /* Port */ ++ if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) { ++ dprintf("tftp_multicast_check: cannot parse port number from %s\n", tokenv); ++ goto token_error; ++ } ++ if (strlen(token) > 0) { ++ port = atoi(token); ++ if (port < 0 || port > 0xFFFF) { ++ dprintf("tftp_multicast_check: bad port number (%d)\n", port); ++ goto token_error; ++ } ++ *pport = htons(port); ++ } ++ ++ /* Active/passive */ ++ if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) { ++ dprintf("tftp_multicast_check: cannot parse active/passive from %s\n", tokenv); ++ goto token_error; ++ } ++ active = atoi(token); ++ if (active != 0 && active != 1) { ++ dprintf("tftp_multicast_check: bad active/passive flag (%d)\n", active); ++ goto token_error; ++ } ++ *pactive = active; ++ ++ free(parse_buf); ++ return 1; ++ ++token_error: ++ free(parse_buf); ++ if (host != NULL) ++ free(host); ++ *phost = NULL; ++ return 0; ++ ++} ++ ++#define VECTOR_QUANTUM_WIDTH 8 ++#define VECTOR_QUANTUM_ALL_ONES ((1< 0) { + ++ while (len > 0) { + /* Make sure the options are terminated correctly */ +- + for (k = 0; k < len; k++) { + if (buf[k] == '\0') { + break; +@@ -117,9 +298,8 @@ + if (strcasecmp(buf, option) == 0) { + opt_found = 1; + } +- } +- else { +- if (opt_found) { ++ } else { ++ if (opt_found) { + return buf; + } + } +@@ -138,7 +318,8 @@ + #endif + + static inline int tftp(const int cmd, const struct hostent *host, +- const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize) ++ const char *remotefile, int localfd, const unsigned short port, ++ struct tftp_option *option) + { + const int cmd_get = cmd & tftp_cmd_get; + const int cmd_put = cmd & tftp_cmd_put; +@@ -155,18 +336,29 @@ + int len; + int opcode = 0; + int finished = 0; +- int timeout = bb_tftp_num_retries; ++ int retry = bb_tftp_num_retries; + unsigned short block_nr = 1; + +-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE +- int want_option_ack = 0; ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ struct hostent *mchost; ++ struct sockaddr_in mcsa; ++ char *mchostname; ++ unsigned short mcport; ++ unsigned char *mcblockmap = NULL; ++ int master_client = 1; ++ int mcfd = -1; ++ int mcmaxblock = 0x10000; ++ int ack_oack = 0; ++#else ++ #define master_client 1 ++ #define ack_oack 0 + #endif + + /* Can't use RESERVE_CONFIG_BUFFER here since the allocation + * size varies meaning BUFFERS_GO_ON_STACK would fail */ +- char *buf=xmalloc(tftp_bufsize + 4); ++ char *buf=xmalloc(option->blksize + 4); + +- tftp_bufsize += 4; ++ int tftp_bufsize = option->blksize + 4; + + if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg("socket"); +@@ -183,15 +375,21 @@ + memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, + sizeof(sa.sin_addr)); + +- /* build opcode */ +- +- if (cmd_get) { +- opcode = TFTP_RRQ; ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ if (option->multicast) { ++ const int bmsize = 0x10000 / VECTOR_QUANTUM_WIDTH; ++ if ((mcfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { ++ bb_perror_msg("multicast socket"); ++ return EXIT_FAILURE; ++ } ++ mcblockmap = xmalloc(bmsize+1); ++ memset(mcblockmap, 0, bmsize+1); + } ++#endif + +- if (cmd_put) { +- opcode = TFTP_WRQ; +- } ++ /* build opcode */ ++ ++ opcode = cmd_get ? TFTP_RRQ : TFTP_WRQ; + + while (1) { + +@@ -203,7 +401,7 @@ + + cp += 2; + +- /* add filename and mode */ ++ /* First packet of file transfer includes file name, mode, and options */ + + if ((cmd_get && (opcode == TFTP_RRQ)) || + (cmd_put && (opcode == TFTP_WRQ))) { +@@ -223,7 +421,7 @@ + } + + if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) { +- bb_error_msg("too long remote-filename"); ++ bb_error_msg("too long: remote filename"); + break; + } + +@@ -238,8 +436,8 @@ + + if (len != TFTP_BLOCKSIZE_DEFAULT) { + +- if ((&buf[tftp_bufsize - 1] - cp) < 15) { +- bb_error_msg("too long remote-filename"); ++ if ((&buf[tftp_bufsize - 1] - cp) < 15) { ++ bb_error_msg("buffer too small for blksize option"); + break; + } + +@@ -249,16 +447,65 @@ + cp += 8; + + cp += snprintf(cp, 6, "%d", len) + 1; ++ } ++#endif ++ ++ ++ ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ ++ if (option->multicast) { ++ if ((&buf[tftp_bufsize - 1] - cp) < 12) { ++ bb_error_msg("buffer too small for multicast option"); ++ break; ++ } ++ ++ /* add "multicast" option */ + +- want_option_ack = 1; ++ memcpy(cp, "multicast\0", 11); ++ cp += 11; ++ ++ option->multicast = 0; /* turn back on when server accepts option */ ++ ack_oack = 1; /* acknowledge OACK */ + } ++ + #endif ++ ++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT ++ ++ if (option->server_timeout != TFTP_TIMEOUT) { ++ if ((&buf[tftp_bufsize - 1] - cp) < 12) { ++ bb_error_msg("buffer too small for timeout option"); ++ break; ++ } ++ ++ /* add "timeout" option */ ++ ++ memcpy(cp, "timeout", 8); ++ cp += 8; ++ ++ cp += snprintf(cp, 4, "%d", option->server_timeout) + 1; ++ } ++#endif ++ + } + + /* add ack and data */ + +- if ((cmd_get && (opcode == TFTP_ACK)) || +- (cmd_put && (opcode == TFTP_DATA))) { ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ else if (option->multicast && opcode == TFTP_ACK) { ++ if (master_client || ack_oack) { ++ int blocknum = bit_lmz(mcblockmap); ++ *((unsigned short *) cp) = htons(blocknum); ++ cp += 2; ++ if (blocknum >= mcmaxblock) ++ finished = 1; ++ dprintf("ack block %d/%d %s\n", blocknum, mcmaxblock, finished? "finished": ""); ++ } ++ } ++#endif ++ else if ((cmd_get && opcode == TFTP_ACK) || ++ (cmd_put && opcode == TFTP_DATA)) { + + *((unsigned short *) cp) = htons(block_nr); + +@@ -275,7 +522,7 @@ + } + + if (len != (tftp_bufsize - 4)) { +- finished++; ++ finished = 1; + } + + cp += len; +@@ -283,82 +530,119 @@ + } + + +- /* send packet */ ++ /* send packet and receive reply */ + + +- timeout = bb_tftp_num_retries; /* re-initialize */ ++ retry = bb_tftp_num_retries; /* re-initialize */ + do { +- ++ int selectrc; + len = cp - buf; + +-#ifdef CONFIG_FEATURE_TFTP_DEBUG +- fprintf(stderr, "sending %u bytes\n", len); +- for (cp = buf; cp < &buf[len]; cp++) +- fprintf(stderr, "%02x ", (unsigned char)*cp); +- fprintf(stderr, "\n"); +-#endif +- if (sendto(socketfd, buf, len, 0, +- (struct sockaddr *) &sa, sizeof(sa)) < 0) { +- bb_perror_msg("send"); +- len = -1; +- break; +- } +- ++ /* send packet */ ++ if ((len > 2) && (! option->multicast || master_client || ack_oack)) { + +- if (finished && (opcode == TFTP_ACK)) { +- break; ++#ifdef CONFIG_FEATURE_TFTP_DEBUG ++ dprintf("sending %u bytes\n", len); ++ for (cp = buf; cp < &buf[len]; cp++) ++ if (debug) ++ printf("%02x ", *(unsigned char *)cp); ++ if (debug) ++ printf("\n"); ++#endif ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ ack_oack = 0; ++#endif ++ if (sendto(socketfd, buf, len, 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) { ++ bb_perror_msg("send"); ++ len = -1; ++ break; ++ } ++ if (finished && opcode == TFTP_ACK) { ++ break; ++ } + } + +- /* receive packet */ ++ /* receive reply packet */ + + memset(&from, 0, sizeof(from)); + fromlen = sizeof(from); + +- tv.tv_sec = TFTP_TIMEOUT; ++ tv.tv_sec = option->client_timeout; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(socketfd, &rfds); ++ dprintf("set to receive from socketfd (%d)\n", socketfd); ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ if (option->multicast) { ++ FD_SET(mcfd, &rfds); ++ dprintf("set to receive from mcfd (%d)\n", mcfd); ++ } ++#endif + +- switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) { +- case 1: +- len = recvfrom(socketfd, buf, tftp_bufsize, 0, +- (struct sockaddr *) &from, &fromlen); +- +- if (len < 0) { +- bb_perror_msg("recvfrom"); +- break; ++ dprintf("select\n"); ++ selectrc = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); ++ if (selectrc > 0) { ++ /* A packet was received */ ++ if (FD_ISSET(socketfd, &rfds)) { /* Unicast packet */ ++ dprintf("from socketfd\n"); ++ len = recvfrom(socketfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen); ++ ++ if (len < 0) { ++ bb_perror_msg("recvfrom"); ++ } else { ++ if (sa.sin_port == port) { ++ sa.sin_port = from.sin_port; ++ } ++ if (sa.sin_port == from.sin_port) { ++ retry = 0; ++ } else { ++ /* bad packet */ ++ /* discard the packet - treat as timeout */ ++ retry = bb_tftp_num_retries; ++ bb_error_msg("timeout"); ++ } ++ } + } + +- timeout = 0; +- +- if (sa.sin_port == port) { +- sa.sin_port = from.sin_port; ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ else if (option->multicast && FD_ISSET(mcfd, &rfds)) { /* Multicast packet */ ++ dprintf("from mcfd\n"); ++ len = recvfrom(mcfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen); ++ if (len < 0) { ++ bb_perror_msg("multicast recvfrom"); ++ } else { ++ if (mcsa.sin_port == mcport) { ++ mcsa.sin_port = from.sin_port; ++ } ++ if (mcsa.sin_port == from.sin_port) { ++ retry = 0; ++ } else { ++ retry = bb_tftp_num_retries; ++ bb_error_msg("multicast timeout"); ++ } ++ } + } +- if (sa.sin_port == from.sin_port) { +- break; +- } +- +- /* fall-through for bad packets! */ +- /* discard the packet - treat as timeout */ +- timeout = bb_tftp_num_retries; ++#endif + +- case 0: ++ } else if (selectrc == 0) { ++ /* Time out */ ++ dprintf("timeout\n"); + bb_error_msg("timeout"); + +- timeout--; +- if (timeout == 0) { ++ retry--; ++ if (retry == 0) { + len = -1; + bb_error_msg("last timeout"); + } +- break; +- +- default: ++ } else { ++ /* Error condition */ ++ dprintf("error\n"); + bb_perror_msg("select"); + len = -1; + } + +- } while (timeout && (len >= 0)); ++ } while (retry && len >= 0); + + if ((finished) || (len < 0)) { + break; +@@ -370,9 +654,8 @@ + opcode = ntohs(*((unsigned short *) buf)); + tmp = ntohs(*((unsigned short *) &buf[2])); + +-#ifdef CONFIG_FEATURE_TFTP_DEBUG +- fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp); +-#endif ++ dprintf("received %d bytes: %04x %04x\n", len, opcode, tmp); ++ dprintf("master_client=%d\n", master_client); + + if (opcode == TFTP_ERROR) { + char *msg = NULL; +@@ -393,55 +676,116 @@ + break; + } + +-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE +- if (want_option_ack) { ++#ifdef TFTP_OPTIONS + +- want_option_ack = 0; ++ if (opcode == TFTP_OACK) { + +- if (opcode == TFTP_OACK) { ++ /* server seems to support options */ + +- /* server seems to support options */ ++ char *res; ++ ++ block_nr = 0; /* acknowledge option packet with block number 0 */ ++ opcode = cmd_put ? TFTP_DATA : TFTP_ACK; + +- char *res; + +- res = tftp_option_get(&buf[2], len-2, +- "blksize"); ++#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE ++ res = tftp_option_get(&buf[2], len-2, "blksize"); + +- if (res) { +- int blksize = atoi(res); +- +- if (tftp_blocksize_check(blksize, +- tftp_bufsize - 4)) { ++ if (res) { ++ int blksize = atoi(res); + +- if (cmd_put) { +- opcode = TFTP_DATA; +- } +- else { +- opcode = TFTP_ACK; +- } +-#ifdef CONFIG_FEATURE_TFTP_DEBUG +- fprintf(stderr, "using blksize %u\n", blksize); ++ if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) { ++ dprintf("using blksize %d\n", blksize); ++ tftp_bufsize = blksize + 4; ++ free(buf); ++ buf = xmalloc(tftp_bufsize); ++ } else { ++ bb_error_msg("bad blksize %d", blksize); ++ break; ++ } ++ } + #endif +- tftp_bufsize = blksize + 4; +- block_nr = 0; +- continue; +- } +- } +- /* FIXME: +- * we should send ERROR 8 */ +- bb_error_msg("bad server option"); +- break; +- } + +- bb_error_msg("warning: blksize not supported by server" +- " - reverting to 512"); + +- tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4; ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ res = tftp_option_get(&buf[2], len-2, "multicast"); ++ ++ if (res) { ++ ack_oack = 1; ++ if (tftp_multicast_check(res, &mchostname, &mcport, &master_client)) { ++ struct ip_mreq mreq; ++ struct in_addr mcaddr; ++ ++ dprintf("using multicast\n"); ++ ++ mchost = xgethostbyname(mchostname); ++ if (mchost) { ++ memcpy(&mcaddr, mchost->h_addr, mchost->h_length); ++ if (! IN_MULTICAST(ntohl(mcaddr.s_addr))) { ++ bb_error_msg("bad multicast address: %s", mchostname); ++ break; ++ } ++ } else { ++ bb_error_msg("bad multicast address: %s", mchostname); ++ break; ++ } ++ ++ memset(&mcsa, 0, sizeof(mcsa)); ++ mcsa.sin_family = AF_INET; ++ mcsa.sin_addr.s_addr = htonl(INADDR_ANY); ++ mcsa.sin_port = mcport; ++ ++ bind(mcfd, (struct sockaddr *)&mcsa, sizeof(mcsa)); ++ ++ mreq.imr_multiaddr.s_addr = mcaddr.s_addr; ++ mreq.imr_interface.s_addr = htonl(INADDR_ANY); ++ ++ if (setsockopt(mcfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) ++ { ++ bb_error_msg("setsockopt"); ++ break; ++ } ++ ++ option->multicast = 1; ++ } else { ++ bb_error_msg("bad multicast option value: %s", res); ++ break; ++ } ++ } ++#endif ++ + } ++ else + #endif + + if (cmd_get && (opcode == TFTP_DATA)) { + ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ if (option->multicast) { ++ int bn = tmp - 1; ++ /* Do I need this block? */ ++ if (! bit_isset(bn, mcblockmap)) { ++ lseek(localfd, bn*(tftp_bufsize-4), SEEK_SET); ++ len = write(localfd, &buf[4], len-4); ++ if (len < 0) { ++ bb_perror_msg("write"); ++ break; ++ } ++ bit_set(bn, mcblockmap); ++ if (len != (tftp_bufsize-4)) { ++ mcmaxblock = tmp; ++ dprintf("mcmaxblock=%d, (len(%d) != tftp_bufsize-4(%d))\n", mcmaxblock, len, tftp_bufsize-4); ++ } ++ opcode = TFTP_ACK; ++ } ++ /* Do not acknowledge block if I already have a copy of the block. A situation can arise when the server ++ * and client timeout nearly simultaneously. The server retransmits the block at the same time the client ++ * re-requests the block. From then on out, each block is transmitted twice--not a good use of bandwidth. ++ */ ++ } ++ else ++#endif ++ + if (tmp == block_nr) { + + len = write(localfd, &buf[4], len - 4); +@@ -452,15 +796,14 @@ + } + + if (len != (tftp_bufsize - 4)) { +- finished++; ++ finished = 1; + } + + opcode = TFTP_ACK; +- continue; + } + } + +- if (cmd_put && (opcode == TFTP_ACK)) { ++ else if (cmd_put && opcode == TFTP_ACK) { + + if (tmp == (unsigned short)(block_nr - 1)) { + if (finished) { +@@ -468,15 +811,19 @@ + } + + opcode = TFTP_DATA; +- continue; + } + } + } + + #ifdef CONFIG_FEATURE_CLEAN_UP + close(socketfd); ++ free(buf); ++ ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ if (mcblockmap != NULL) ++ free(mcblockmap); ++#endif + +- free(buf); + #endif + + return finished ? EXIT_SUCCESS : EXIT_FAILURE; +@@ -487,13 +834,18 @@ + struct hostent *host = NULL; + char *localfile = NULL; + char *remotefile = NULL; +- int port; ++ unsigned short port; + int cmd = 0; + int fd = -1; + int flags = 0; + int opt; + int result; +- int blocksize = TFTP_BLOCKSIZE_DEFAULT; ++ struct tftp_option option = { ++ .multicast = 0, ++ .blksize = TFTP_BLOCKSIZE_DEFAULT, ++ .client_timeout = TFTP_TIMEOUT, ++ .server_timeout = TFTP_TIMEOUT, ++ }; + + /* figure out what to pass to getopt */ + +@@ -515,13 +867,45 @@ + #define PUT + #endif + +- while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) { ++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT ++#define TO "T:t:" ++#else ++#define TO ++#endif ++ ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++#define MC "m" ++#else ++#define MC ++#endif ++ ++#ifdef CONFIG_FEATURE_TFTP_DEBUG ++#define DB "D" ++#else ++#define DB ++#endif ++ ++ while ((opt = getopt(argc, argv, BS GET PUT TO MC DB "l:r:")) != -1) { + switch (opt) { + #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE + case 'b': +- blocksize = atoi(optarg); +- if (!tftp_blocksize_check(blocksize, 0)) { +- return EXIT_FAILURE; ++ option.blksize = atoi(optarg); ++ if (!tftp_blocksize_check(option.blksize, 0)) { ++ return EXIT_FAILURE; ++ } ++ break; ++#endif ++#ifdef CONFIG_FEATURE_TFTP_TIMEOUT ++ case 'T': ++ option.client_timeout = atoi(optarg); ++ if (!tftp_timeout_check(option.client_timeout)) { ++ return EXIT_FAILURE; ++ } ++ break; ++ case 't': ++ option.server_timeout = atoi(optarg); ++ if (!tftp_timeout_check(option.server_timeout)) { ++ return EXIT_FAILURE; + } + break; + #endif +@@ -537,18 +921,34 @@ + flags = O_RDONLY; + break; + #endif ++#ifdef CONFIG_FEATURE_TFTP_MULTICAST ++ case 'm': ++ option.multicast = 1; /* receive multicast file */ ++ break; ++#endif ++#ifdef CONFIG_FEATURE_TFTP_DEBUG ++ case 'D': ++ debug = 1; ++ break; ++#endif + case 'l': + localfile = bb_xstrdup(optarg); + break; + case 'r': + remotefile = bb_xstrdup(optarg); + break; ++ default: ++ bb_show_usage(); + } + } + + if ((cmd == 0) || (optind == argc)) { + bb_show_usage(); + } ++ if (cmd == tftp_cmd_put && option.multicast) { ++ fprintf(stderr, "Multicast (-m) invalid option with put (-p) command\n"); ++ exit(EXIT_FAILURE); ++ } + if(localfile && strcmp(localfile, "-") == 0) { + fd = fileno((cmd==tftp_cmd_get)? stdout : stdin); + } +@@ -566,14 +966,12 @@ + host = xgethostbyname(argv[optind]); + port = bb_lookup_port(argv[optind + 1], "udp", 69); + +-#ifdef CONFIG_FEATURE_TFTP_DEBUG +- fprintf(stderr, "using server \"%s\", remotefile \"%s\", " ++ dprintf("using server \"%s\", remotefile \"%s\", " + "localfile \"%s\".\n", + inet_ntoa(*((struct in_addr *) host->h_addr)), + remotefile, localfile); +-#endif + +- result = tftp(cmd, host, remotefile, fd, port, blocksize); ++ result = tftp(cmd, host, remotefile, fd, port, &option); + + #ifdef CONFIG_FEATURE_CLEAN_UP + if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) { +@@ -582,3 +980,18 @@ + #endif + return(result); + } ++ ++ ++#ifdef CONFIG_FEATURE_TFTP_DEBUG ++ ++#include ++ ++static void ++printtime(void) ++{ ++ struct timeval tv; ++ gettimeofday(&tv, NULL); ++ printf("%11lu.%06lu ", tv.tv_sec, tv.tv_usec); ++} ++ ++#endif diff --git a/busybox/patches/top_system_cpu.diff b/busybox/patches/top_system_cpu.diff new file mode 100644 index 000000000..5d213e76a --- /dev/null +++ b/busybox/patches/top_system_cpu.diff @@ -0,0 +1,51 @@ +diff -purN busybox.ori/include/libbb.h busybox/include/libbb.h +--- busybox.ori/include/libbb.h 2004-03-21 14:39:35.000000000 +0100 ++++ busybox-1.0/include/libbb.h 2004-03-21 14:45:35.000000000 +0100 +@@ -447,6 +447,7 @@ typedef struct { + int ppid; + #ifdef FEATURE_CPU_USAGE_PERCENTAGE + unsigned pcpu; ++ unsigned pscpu; + unsigned long stime, utime; + #endif + char *cmd; +diff -purN busybox.ori/procps/top.c busybox/procps/top.c +--- busybox.ori/procps/top.c 2004-03-21 14:40:09.000000000 +0100 ++++ busybox-1.0/procps/top.c 2004-03-21 17:27:52.961951448 +0100 +@@ -289,6 +289,15 @@ static void do_stats(void) + i = 999; + cur->pcpu = i; + ++ /* ++ * Calculate percent of system time from cpu time ++ */ ++ if (systime != 0) { ++ cur->pscpu = 100 * total_time / systime; ++ } else { ++ cur->pscpu = 0; ++ } ++ + } + + /* +@@ -393,7 +402,7 @@ static void display_status(int count, in + + #ifdef FEATURE_CPU_USAGE_PERCENTAGE + /* what info of the processes is shown */ +- printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%MEM COMMAND\e[0m\n"); ++ printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%SCPU %%MEM COMMAND\e[0m\n"); + #else + printf("\n\e[7m PID USER STATUS RSS PPID %%MEM COMMAND\e[0m\n"); + #endif +@@ -410,9 +419,9 @@ static void display_status(int count, in + else + sprintf(rss_str_buf, "%7ld", s->rss); + #ifdef FEATURE_CPU_USAGE_PERCENTAGE +- printf("%5d %-8s %s %s %5d %2d.%d %2u.%u ", ++ printf("%5d %-8s %s %s %5d %2d.%d %2d.%d %2u.%u ", + s->pid, s->user, s->state, rss_str_buf, s->ppid, +- s->pcpu/10, s->pcpu%10, pmem/10, pmem%10); ++ s->pcpu/10, s->pcpu%10,s->pscpu/10, s->pscpu%10, pmem/10, pmem%10); + #else + printf("%5d %-8s %s %s %5d %2u.%u ", + s->pid, s->user, s->state, rss_str_buf, s->ppid, diff --git a/busybox/patches/udhcp_additional_items.diff b/busybox/patches/udhcp_additional_items.diff new file mode 100644 index 000000000..933be2ad4 --- /dev/null +++ b/busybox/patches/udhcp_additional_items.diff @@ -0,0 +1,126 @@ +Index: include/usage.h +=================================================================== +RCS file: /var/cvs/busybox/include/usage.h,v +retrieving revision 1.191 +diff -u -r1.191 usage.h +--- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191 ++++ b/include/usage.h 5 Mar 2004 14:32:45 -0000 +@@ -2606,6 +2606,7 @@ + "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \ + "\t-q,\t--quit\tQuit after obtaining lease\n" \ + "\t-r,\t--request=IP\tIP address to request (default: none)\n" \ ++ "\t-R,\t--require=NAME\tAdd NAME to request\n" \ + "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \ + "\t-v,\t--version\tDisplay version" + +Index: networking/udhcp/README.udhcpc +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpc,v +retrieving revision 1.3 +diff -u -r1.3 README.udhcpc +--- a/networking/udhcp/README.udhcpc 11 Dec 2002 21:12:44 -0000 1.3 ++++ b/networking/udhcp/README.udhcpc 5 Mar 2004 14:32:46 -0000 +@@ -22,6 +22,7 @@ + -p, --pidfile=file Store process ID of daemon in file + -q, --quit Quit after obtaining lease + -r, --request=IP IP address to request (default: none) ++-R, --require=NAME Add NAME to request + -s, --script=file Run file at dhcp events (default: + /usr/share/udhcpc/default.script) + -v, --version Display version +@@ -101,6 +102,8 @@ + + additional options are easily added in options.c. + ++By default, only a few basic items are requested. To request additional ++items use the -R option. Example: "-R rootpath" + + note on udhcpc's random seed + --------------------------- +Index: networking/udhcp/dhcpc.c +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/dhcpc.c,v +retrieving revision 1.16 +diff -u -r1.16 dhcpc.c +--- a/networking/udhcp/dhcpc.c 30 Jan 2004 23:45:12 -0000 1.16 ++++ b/networking/udhcp/dhcpc.c 5 Mar 2004 14:32:46 -0000 +@@ -88,6 +88,7 @@ + " -p, --pidfile=file Store process ID of daemon in file\n" + " -q, --quit Quit after obtaining lease\n" + " -r, --request=IP IP address to request (default: none)\n" ++" -R, --require=NAME Add NAME to the request\n" + " -s, --script=file Run file at dhcp events (default:\n" + " " DEFAULT_SCRIPT ")\n" + " -v, --version Display version\n" +@@ -203,6 +204,7 @@ + {"pidfile", required_argument, 0, 'p'}, + {"quit", no_argument, 0, 'q'}, + {"request", required_argument, 0, 'r'}, ++ {"require", required_argument, 0, 'R'}, + {"script", required_argument, 0, 's'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} +@@ -211,7 +213,7 @@ + /* get options */ + while (1) { + int option_index = 0; +- c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index); ++ c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:R:s:v", arg_options, &option_index); + if (c == -1) break; + + switch (c) { +@@ -254,6 +256,11 @@ + case 'r': + requested_ip = inet_addr(optarg); + break; ++ case 'R': ++ if (require_option(optarg)) { ++ fprintf(stderr,"WARNING: %s unknown/not-supported (Ignored)\n", optarg ); ++ } ++ break; + case 's': + client_config.script = optarg; + break; +Index: networking/udhcp/options.c +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/options.c,v +retrieving revision 1.7 +diff -u -r1.7 options.c +--- a/networking/udhcp/options.c 30 Jan 2004 23:45:12 -0000 1.7 ++++ b/networking/udhcp/options.c 5 Mar 2004 14:32:46 -0000 +@@ -57,7 +57,19 @@ + [OPTION_S32] = 4 + }; + +- ++/* find and mark requested item as required */ ++int require_option(char *name) ++{ ++ int i; ++ for (i = 0; dhcp_options[i].code; i++) { ++ if (strcmp(name, dhcp_options[i].name) == 0 ){ ++ dhcp_options[i].flags |= OPTION_REQ; ++ return 0; ++ } ++ } ++ return 1; ++} ++ + /* get an option with bounds checking (warning, not aligned). */ + uint8_t *get_option(struct dhcpMessage *packet, int code) + { +Index: networking/udhcp/options.h +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/options.h,v +retrieving revision 1.5 +diff -u -r1.5 options.h +--- a/networking/udhcp/options.h 30 Jan 2004 23:45:12 -0000 1.5 ++++ b/networking/udhcp/options.h 5 Mar 2004 14:32:46 -0000 +@@ -30,6 +30,7 @@ + extern struct dhcp_option dhcp_options[]; + extern int option_lengths[]; + ++int require_option(char *name); + uint8_t *get_option(struct dhcpMessage *packet, int code); + int end_option(uint8_t *optionptr); + int add_option_string(uint8_t *optionptr, uint8_t *string); diff --git a/busybox/patches/udhcp_config_paths.diff b/busybox/patches/udhcp_config_paths.diff new file mode 100644 index 000000000..1d3a6b4b0 --- /dev/null +++ b/busybox/patches/udhcp_config_paths.diff @@ -0,0 +1,333 @@ +Index: include/usage.h +=================================================================== +RCS file: /var/cvs/busybox/include/usage.h,v +retrieving revision 1.191 +diff -u -r1.191 usage.h +--- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191 ++++ b/include/usage.h 5 Mar 2004 13:20:11 -0000 +@@ -2606,7 +2606,8 @@ + "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \ + "\t-q,\t--quit\tQuit after obtaining lease\n" \ + "\t-r,\t--request=IP\tIP address to request (default: none)\n" \ +- "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \ ++ "\t-s,\t--script=file\tRun file at dhcp events (default: " \ ++ CONFIG_UDHCPC_SCRIPT_PATH ")\n" \ + "\t-v,\t--version\tDisplay version" + + #define udhcpd_trivial_usage \ +Index: networking/udhcp/AUTHORS +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/AUTHORS,v +retrieving revision 1.3 +diff -u -r1.3 AUTHORS +--- a/networking/udhcp/AUTHORS 18 Dec 2003 22:25:38 -0000 1.3 ++++ b/networking/udhcp/AUTHORS 5 Mar 2004 13:20:11 -0000 +@@ -10,5 +10,5 @@ + Moreton Bay (http://www.moretonbay.com/) + Vladimir Oleynik Size optimizations + +- ++Tony J. White additional busybox build options + +Index: networking/udhcp/Config.in +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/Config.in,v +retrieving revision 1.5 +diff -u -r1.5 Config.in +--- a/networking/udhcp/Config.in 22 Oct 2003 09:58:38 -0000 1.5 ++++ b/networking/udhcp/Config.in 5 Mar 2004 13:20:11 -0000 +@@ -58,5 +58,62 @@ + + See http://udhcp.busybox.net for further details. + ++menu "udhcpd Configuration Options" ++ depends on CONFIG_UDHCPD ++ ++config CONFIG_UDHCPD_CONF_PATH ++ string "Path to default udhcpd.conf" ++ default "/etc/udhcpd.conf" ++ depends on CONFIG_UDHCPD ++ help ++ The full path to udhcpd's default configuration file. ++ (default is: /etc/udhcpd.conf) ++ ++config CONFIG_UDHCPD_LEASE_PATH ++ string "Path to default udhcpd.leases" ++ default "/var/lib/misc/udhcpd.leases" ++ depends on CONFIG_UDHCPD ++ help ++ The full path to udhcpd's default leases file. ++ (default is: /var/lib/misc/udhcpd.leases) ++ ++config CONFIG_UDHCPD_PID_PATH ++ string "Path to default udhcpd PID file" ++ default "/var/run/udhcpd.pid" ++ depends on CONFIG_UDHCPD ++ help ++ The full path to udhcpd's default pid file. ++ (default is: /var/run/udhcpd.pid) ++ ++endmenu ++ ++menu "udhcpc Configuration Options" ++ depends on CONFIG_UDHCPC ++ ++config CONFIG_UDHCPC_SCRIPT_PATH ++ string "Path to default udhcpc event script" ++ depends on CONFIG_UDHCPC ++ help ++ The full path to udhcpc's default event script file. ++ (default is: /usr/share/udhcpc/default.script OR ++ /share/udhcpc/default.script if CONFIG_INSTALL_NO_USR is set) ++ ++ When udhcpc is started it executes this script to take care ++ of system tasks after it completes DHCP communication. Such ++ tasks include putting network interfaces up or down, setting ++ DNS info, adding routing information, etc. ++ ++if CONFIG_INSTALL_NO_USR ++config CONFIG_UDHCPC_SCRIPT_PATH ++ default "/share/udhcpc/default.script" ++endif ++ ++if !CONFIG_INSTALL_NO_USR ++config CONFIG_UDHCPC_SCRIPT_PATH ++ default "/usr/share/udhcpc/default.script" ++endif ++ ++endmenu ++ + endmenu + +Index: networking/udhcp/README +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/README,v +retrieving revision 1.3 +diff -u -r1.3 README +--- a/networking/udhcp/README 18 Dec 2003 22:25:38 -0000 1.3 ++++ b/networking/udhcp/README 5 Mar 2004 13:20:11 -0000 +@@ -9,27 +9,42 @@ + compile time options + ------------------- + +-The Makefile contains three of the compile time options: ++The following options can be adjusted when configuring busybox: + +- UDHCP_DEBUG: If UDHCP_DEBUG is defined, udhcpd will output extra +- debugging output, compile with -g, and not fork to the background when +- run. +- UDHCP_SYSLOG: If UDHCP_SYSLOG is defined, udhcpd will log all its +- messages syslog, otherwise, it will attempt to log them to stdout. +- +- COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd, +- is created. If called as udhcpd, the dhcp server will be started. +- If called as udhcpc, the dhcp client will be started. +- +-dhcpd.h contains the other three compile time options: +- +- LEASE_TIME: The default lease time if not specified in the config +- file. ++ CONFIG_FEATURE_UDHCP_DEBUG: ++ If this is defined, udhcpd will output extra debugging output, ++ compile with -g, and not fork to the background when run. + +- LEASES_FILE: The default file for storing leases. +- +- DHCPD_CONFIG_FILE: The defualt config file to use. ++ CONFIG_FEATURE_UDHCP_SYSLOG: ++ If this is defined, udhcpd will log all its messages syslog, ++ otherwise, it will attempt to log them to stdout. ++ ++ CONFIG_UDHCPD_CONF_PATH: ++ The full path to udhcpd's default configuration file. ++ ++ CONFIG_UDHCPD_LEASE_PATH: ++ The full path to udhcpd's default leases file. ++ ++ CONFIG_UDHCPD_PID_PATH: ++ The full path to udhcpd's default pid file. ++ ++ CONFIG_UDHCPC_SCRIPT_PATH: ++ The full path to udhcpc's default event script file. ++ (default is: /usr/share/udhcpc/default.script) ++ ++ When udhcpc is started it executes this script to take care ++ of system tasks after it completes DHCP communication. Such ++ tasks include putting network interfaces up or down, setting ++ DNS info, adding routing information, etc. ++ ++ ++dhcpd.h contains the another compile time option: + ++ LEASE_TIME: ++ The default lease time if not specified in the config file. ++ This option can also be changed at runtime with the 'lease' ++ configuration option. ++ + options.c contains a set of dhcp options for the client: + + name[10]: The name of the option as it will appear in scripts +Index: networking/udhcp/README.udhcpc +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpc,v +retrieving revision 1.3 +diff -u -r1.3 README.udhcpc +--- a/networking/udhcp/README.udhcpc 11 Dec 2002 21:12:44 -0000 1.3 ++++ b/networking/udhcp/README.udhcpc 5 Mar 2004 13:20:11 -0000 +@@ -23,7 +23,8 @@ + -q, --quit Quit after obtaining lease + -r, --request=IP IP address to request (default: none) + -s, --script=file Run file at dhcp events (default: +- /usr/share/udhcpc/default.script) ++ /usr/share/udhcpc/default.script or ++ CONFIG_UDHCPC_SCRIPT_PATH at build time) + -v, --version Display version + + +Index: networking/udhcp/README.udhcpd +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpd,v +retrieving revision 1.1 +diff -u -r1.1 README.udhcpd +--- a/networking/udhcp/README.udhcpd 31 Oct 2002 19:21:27 -0000 1.1 ++++ b/networking/udhcp/README.udhcpd 5 Mar 2004 13:20:11 -0000 +@@ -50,10 +50,14 @@ + + compile time options + ------------------- ++ ++During busybox configuration, you can change the default paths for ++udhcpd.conf, udhcpd.leases, and udhcpd.pid files. See README for ++more details. + +-dhcpd.h contains the other two compile time options: ++dhcpd.h contains the compile time option: + + LEASE_TIME: The default lease time if not specified in the config + file. ++ + +- DHCPD_CONFIG_FILE: The defualt config file to use. +Index: networking/udhcp/dhcpc.h +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/dhcpc.h,v +retrieving revision 1.4 +diff -u -r1.4 dhcpc.h +--- a/networking/udhcp/dhcpc.h 30 Jan 2004 23:45:12 -0000 1.4 ++++ b/networking/udhcp/dhcpc.h 5 Mar 2004 13:20:11 -0000 +@@ -2,7 +2,11 @@ + #ifndef _DHCPC_H + #define _DHCPC_H + +-#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" ++#ifdef CONFIG_UDHCPC_SCRIPT_PATH ++ #define DEFAULT_SCRIPT CONFIG_UDHCPC_SCRIPT_PATH ++#else ++ #define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" ++#endif + + /* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */ + #include "libbb_udhcp.h" +Index: networking/udhcp/dhcpd.c +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.c,v +retrieving revision 1.5 +diff -u -r1.5 dhcpd.c +--- a/networking/udhcp/dhcpd.c 30 Jan 2004 23:45:12 -0000 1.5 ++++ b/networking/udhcp/dhcpd.c 5 Mar 2004 13:20:11 -0000 +@@ -70,6 +70,13 @@ + struct dhcpOfferedAddr *lease; + int max_sock; + unsigned long num_ips; ++ int daemonize = 1; ++ ++ while (strcmp(argv[1],"-f")==0 || strcmp(argv[1],"--foreground")==0) { ++ daemonize = 0; ++ argv++; ++ argc--; ++ } + + memset(&server_config, 0, sizeof(struct server_config_t)); + read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]); +@@ -99,9 +106,8 @@ + &server_config.server, server_config.arp) < 0) + return 1; + +-#ifndef UDHCP_DEBUG +- background(server_config.pidfile); /* hold lock during fork. */ +-#endif ++ if(daemonize) ++ background(server_config.pidfile); /* hold lock during fork. */ + + /* Setup the signal pipe */ + udhcp_sp_setup(); +Index: networking/udhcp/dhcpd.h +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.h,v +retrieving revision 1.5 +diff -u -r1.5 dhcpd.h +--- a/networking/udhcp/dhcpd.h 30 Jan 2004 23:45:12 -0000 1.5 ++++ b/networking/udhcp/dhcpd.h 5 Mar 2004 13:20:12 -0000 +@@ -15,11 +15,25 @@ + + /* the period of time the client is allowed to use that address */ + #define LEASE_TIME (60*60*24*10) /* 10 days of seconds */ +-#define LEASES_FILE "/var/lib/misc/udhcpd.leases" ++ ++#ifdef CONFIG_UDHCPD_LEASE_PATH ++ #define LEASES_FILE CONFIG_UDHCPD_LEASE_PATH ++#else ++ #define LEASES_FILE "/var/lib/misc/udhcpd.leases" ++#endif + + /* where to find the DHCP server configuration file */ +-#define DHCPD_CONF_FILE "/etc/udhcpd.conf" ++#ifdef CONFIG_UDHCPD_CONF_PATH ++ #define DHCPD_CONF_FILE CONFIG_UDHCPD_CONF_PATH ++#else ++ #define DHCPD_CONF_FILE "/etc/udhcpd.conf" ++#endif + ++#ifdef CONFIG_UDHCPD_PID_PATH ++ #define DHCPD_PID_FILE CONFIG_UDHCPD_PID_PATH ++#else ++ #define DHCPD_PID_FILE "/var/run/udhcpd.pid" ++#endif + /*****************************************************************/ + /* Do not modify below here unless you know what you are doing!! */ + /*****************************************************************/ +Index: networking/udhcp/files.c +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/files.c,v +retrieving revision 1.13 +diff -u -r1.13 files.c +--- a/networking/udhcp/files.c 30 Jan 2004 23:45:12 -0000 1.13 ++++ b/networking/udhcp/files.c 5 Mar 2004 13:20:13 -0000 +@@ -166,7 +166,7 @@ + {"offer_time", read_u32, &(server_config.offer_time), "60"}, + {"min_lease", read_u32, &(server_config.min_lease), "60"}, + {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE}, +- {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, ++ {"pidfile", read_str, &(server_config.pidfile), DHCPD_PID_FILE}, + {"notify_file", read_str, &(server_config.notify_file), ""}, + {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, + {"sname", read_str, &(server_config.sname), ""}, +Index: networking/udhcp/libbb_udhcp.h +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/libbb_udhcp.h,v +retrieving revision 1.5 +diff -u -r1.5 libbb_udhcp.h +--- a/networking/udhcp/libbb_udhcp.h 18 Dec 2003 22:25:38 -0000 1.5 ++++ b/networking/udhcp/libbb_udhcp.h 5 Mar 2004 13:20:13 -0000 +@@ -3,11 +3,6 @@ + /* bit of a hack, do this no matter what the order of the includes. + * (for busybox) */ + +-#ifdef CONFIG_INSTALL_NO_USR +-#undef DEFUALT_SCRIPT +-#define DEFAULT_SCRIPT "/share/udhcpc/default.script" +-#endif +- + #ifndef _LIBBB_UDHCP_H + #define _LIBBB_UDHCP_H + diff --git a/busybox/patches/udhcpd_foreground.diff b/busybox/patches/udhcpd_foreground.diff new file mode 100644 index 000000000..3b8c7eb0c --- /dev/null +++ b/busybox/patches/udhcpd_foreground.diff @@ -0,0 +1,33 @@ +Index: ./networking/udhcp/dhcpd.c +=================================================================== +RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.c,v +retrieving revision 1.5 +diff -u -r1.5 dhcpd.c +--- a/./networking/udhcp/dhcpd.c 30 Jan 2004 23:45:12 -0000 1.5 ++++ b/./networking/udhcp/dhcpd.c 5 Mar 2004 13:09:05 -0000 +@@ -70,6 +70,13 @@ + struct dhcpOfferedAddr *lease; + int max_sock; + unsigned long num_ips; ++ int daemonize = 1; ++ ++ while (strcmp(argv[1],"-f")==0 || strcmp(argv[1],"--foreground")==0) { ++ daemonize = 0; ++ argv++; ++ argc--; ++ } + + memset(&server_config, 0, sizeof(struct server_config_t)); + read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]); +@@ -99,9 +106,8 @@ + &server_config.server, server_config.arp) < 0) + return 1; + +-#ifndef UDHCP_DEBUG +- background(server_config.pidfile); /* hold lock during fork. */ +-#endif ++ if(daemonize) ++ background(server_config.pidfile); /* hold lock during fork. */ + + /* Setup the signal pipe */ + udhcp_sp_setup(); diff --git a/busybox/procps/Config.in b/busybox/procps/Config.in new file mode 100644 index 000000000..8d557972c --- /dev/null +++ b/busybox/procps/Config.in @@ -0,0 +1,82 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Process Utilities" + +config CONFIG_FREE + bool "free" + default n + help + free displays the total amount of free and used physical and swap + memory in the system, as well as the buffers used by the kernel. + The shared memory column should be ignored; it is obsolete. + +config CONFIG_KILL + bool "kill" + default n + help + The command kill sends the specified signal to the specified + process or process group. If no signal is specified, the TERM + signal is sent. + +config CONFIG_KILLALL + bool "killall" + default n + depends on CONFIG_KILL + help + killall sends a signal to all processes running any of the + specified commands. If no signal name is specified, SIGTERM is + sent. + +config CONFIG_PIDOF + bool "pidof" + default n + help + Pidof finds the process id's (pids) of the named programs. It prints + those id's on the standard output. + +config CONFIG_PS + bool "ps" + default n + help + ps gives a snapshot of the current processes. + +config CONFIG_RENICE + bool "renice" + default n + help + Renice alters the scheduling priority of one or more running + processes. + +config CONFIG_TOP + bool "top" + default n + help + The top program provides a dynamic real-time view of a running + system. + +config FEATURE_CPU_USAGE_PERCENTAGE + bool " Support showing CPU usage percentage (add 2k bytes)" + default y + depends on CONFIG_TOP + help + Make top display CPU usage. + +config CONFIG_UPTIME + bool "uptime" + default n + help + uptime gives a one line display of the current time, how long + the system has been running, how many users are currently logged + on, and the system load averages for the past 1, 5, and 15 minutes. + +config CONFIG_SYSCTL + bool "sysctl" + default n + help + sysctl - configure kernel parameters at runtime + +endmenu + diff --git a/busybox/procps/Makefile b/busybox/procps/Makefile new file mode 100644 index 000000000..1cc880462 --- /dev/null +++ b/busybox/procps/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/procps +PROCPS_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/procps/Makefile.in b/busybox/procps/Makefile.in new file mode 100644 index 000000000..ced29a198 --- /dev/null +++ b/busybox/procps/Makefile.in @@ -0,0 +1,43 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +PROCPS_AR:=procps.a +ifndef $(PROCPS_DIR) +PROCPS_DIR:=$(top_builddir)/procps/ +endif +srcdir=$(top_srcdir)/procps + +PROCPS-y:= +PROCPS-$(CONFIG_FREE) += free.o +PROCPS-$(CONFIG_KILL) += kill.o +PROCPS-$(CONFIG_PIDOF) += pidof.o +PROCPS-$(CONFIG_PS) += ps.o +PROCPS-$(CONFIG_RENICE) += renice.o +PROCPS-$(CONFIG_SYSCTL) += sysctl.o +PROCPS-$(CONFIG_TOP) += top.o +PROCPS-$(CONFIG_UPTIME) += uptime.o + +libraries-y+=$(PROCPS_DIR)$(PROCPS_AR) + +$(PROCPS_DIR)$(PROCPS_AR): $(patsubst %,$(PROCPS_DIR)%, $(PROCPS-y)) + $(AR) -ro $@ $(patsubst %,$(PROCPS_DIR)%, $(PROCPS-y)) + +$(PROCPS_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/procps/free.c b/busybox/procps/free.c new file mode 100644 index 000000000..4fb047d48 --- /dev/null +++ b/busybox/procps/free.c @@ -0,0 +1,84 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini free implementation for busybox + * + * Copyright (C) 1999-2004 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; + } + if ( info.mem_unit == 1 ) { + info.mem_unit=1024; + + /* TODO: Make all this stuff not overflow when mem >= 4 Gib */ + info.totalram/=info.mem_unit; + info.freeram/=info.mem_unit; +#ifndef __uClinux__ + info.totalswap/=info.mem_unit; + info.freeswap/=info.mem_unit; +#endif + info.sharedram/=info.mem_unit; + info.bufferram/=info.mem_unit; + } else { + info.mem_unit/=1024; + /* TODO: Make all this stuff not overflow when mem >= 4 Gib */ + info.totalram*=info.mem_unit; + info.freeram*=info.mem_unit; +#ifndef __uClinux__ + info.totalswap*=info.mem_unit; + info.freeswap*=info.mem_unit; +#endif + info.sharedram*=info.mem_unit; + info.bufferram*=info.mem_unit; + } + + if (argc > 1 && **(argv + 1) == '-') + bb_show_usage(); + + printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free", + "shared", "buffers"); + + printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram, + info.totalram-info.freeram, info.freeram, + info.sharedram, info.bufferram); + +#ifndef __uClinux__ + printf("%6s%13ld%13ld%13ld\n", "Swap:", info.totalswap, + info.totalswap-info.freeswap, info.freeswap); + + printf("%6s%13ld%13ld%13ld\n", "Total:", info.totalram+info.totalswap, + (info.totalram-info.freeram)+(info.totalswap-info.freeswap), + info.freeram+info.freeswap); +#endif + return EXIT_SUCCESS; +} + diff --git a/busybox/procps/kill.c b/busybox/procps/kill.c new file mode 100644 index 000000000..25a8d0124 --- /dev/null +++ b/busybox/procps/kill.c @@ -0,0 +1,156 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini kill/killall implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * Copyright (C) 1999-2004 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" + +#define KILL 0 +#define KILLALL 1 + +extern int kill_main(int argc, char **argv) +{ + int whichApp, signo = SIGTERM; + const char *name; + int errors = 0; + +#ifdef CONFIG_KILLALL + int quiet=0; + /* Figure out what we are trying to do here */ + whichApp = (strcmp(bb_applet_name, "killall") == 0)? KILLALL : KILL; +#else + whichApp = KILL; +#endif + + /* Parse any options */ + if (argc < 2) + bb_show_usage(); + + if(argv[1][0] != '-'){ + argv++; + argc--; + goto do_it_now; + } + + /* The -l option, which prints out signal names. */ + if(argv[1][1]=='l' && argv[1][2]=='\0'){ + if(argc==2) { + /* Print the whole signal list */ + int col = 0; + for(signo=1; signo < NSIG; signo++) { + name = u_signal_names(0, &signo, 1); + if(name==NULL) /* unnamed */ + continue; + col += printf("%2d) %-16s", signo, name); + if (col > 60) { + printf("\n"); + col = 0; + } + } + printf("\n"); + + } else { + for(argv++; *argv; argv++) { + name = u_signal_names(*argv, &signo, -1); + if(name!=NULL) + printf("%s\n", name); + } + } + /* If they specified -l, were all done */ + return EXIT_SUCCESS; + } + +#ifdef CONFIG_KILLALL + /* The -q quiet option */ + if(argv[1][1]=='q' && argv[1][2]=='\0'){ + quiet++; + argv++; + argc--; + if(argc<2 || argv[1][0] != '-'){ + goto do_it_now; + } + } +#endif + + if(!u_signal_names(argv[1]+1, &signo, 0)) + bb_error_msg_and_die( "bad signal name '%s'", argv[1]+1); + argv+=2; + argc-=2; + +do_it_now: + + if (whichApp == KILL) { + /* Looks like they want to do a kill. Do that */ + while (--argc >= 0) { + int pid; + + if (!isdigit(**argv)) + bb_error_msg_and_die( "Bad PID '%s'", *argv); + pid = strtol(*argv, NULL, 0); + if (kill(pid, signo) != 0) { + bb_perror_msg( "Could not kill pid '%d'", pid); + errors++; + } + argv++; + } + + } +#ifdef CONFIG_KILLALL + else { + pid_t myPid=getpid(); + /* Looks like they want to do a killall. Do that */ + while (--argc >= 0) { + long* pidList; + + pidList = find_pid_by_name(*argv); + if (!pidList || *pidList<=0) { + errors++; + if (quiet==0) + bb_error_msg( "%s: no process killed", *argv); + } else { + long *pl; + + for(pl = pidList; *pl !=0 ; pl++) { + if (*pl==myPid) + continue; + if (kill(*pl, signo) != 0) { + errors++; + if (quiet==0) + bb_perror_msg( "Could not kill pid '%ld'", *pl); + } + } + } + free(pidList); + argv++; + } + } +#endif + return errors; +} diff --git a/busybox/procps/pidof.c b/busybox/procps/pidof.c new file mode 100644 index 000000000..413864a37 --- /dev/null +++ b/busybox/procps/pidof.c @@ -0,0 +1,71 @@ +/* vi: set sw=4 ts=4: */ +/* + * pidof implementation for busybox + * + * Copyright (C) 1999-2004 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, n = 0; + int single_flag = 0; + int fail = 1; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "s")) > 0) { + switch (opt) { + case 's': + single_flag = 1; + break; + default: + bb_show_usage(); + } + } + + /* Looks like everything is set to go. */ + while(optind < argc) { + long *pidList; + long *pl; + + pidList = find_pid_by_name(argv[optind]); + for(pl = pidList; *pl > 0; pl++) { + printf("%s%ld", (n++ ? " " : ""), *pl); + fail = 0; + if (single_flag) + break; + } + free(pidList); + optind++; + + } + printf("\n"); + + return fail ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/busybox/procps/ps.c b/busybox/procps/ps.c new file mode 100644 index 000000000..0b603314d --- /dev/null +++ b/busybox/procps/ps.c @@ -0,0 +1,109 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ps implementation(s) for busybox + * + * Copyright (C) 1999-2004 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 "busybox.h" +#ifdef CONFIG_SELINUX +#include +#include +#include /* for is_flask_enabled() */ +#endif + +static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */ + + + +extern int ps_main(int argc, char **argv) +{ + procps_status_t * p; + int i, len; + int terminal_width = TERMINAL_WIDTH; + +#ifdef CONFIG_SELINUX + int use_selinux = 0; + security_id_t sid; + if(is_flask_enabled() && argv[1] && !strcmp(argv[1], "-c") ) + use_selinux = 1; +#endif + + get_terminal_width_height(0, &terminal_width, NULL); + /* Go one less... */ + terminal_width--; + +#ifdef CONFIG_SELINUX + if(use_selinux) + printf(" PID Context Stat Command\n"); + else +#endif + printf(" PID Uid VmSize Stat Command\n"); +#ifdef CONFIG_SELINUX + while ((p = procps_scan(1, use_selinux, &sid)) != 0) { +#else + while ((p = procps_scan(1)) != 0) { +#endif + char *namecmd = p->cmd; + +#ifdef CONFIG_SELINUX + if(use_selinux) + { + char sbuf[128]; + len = sizeof(sbuf); + if(security_sid_to_context(sid, (security_context_t)&sbuf, &len)) + strcpy(sbuf, "unknown"); + + len = printf("%5d %-32s %s ", p->pid, sbuf, p->state); + } + else +#endif + if(p->rss == 0) + len = printf("%5d %-8s %s ", p->pid, p->user, p->state); + else + len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state); + i = terminal_width-len; + + if(namecmd != 0 && namecmd[0] != 0) { + if(i < 0) + i = 0; + if(strlen(namecmd) > i) + namecmd[i] = 0; + printf("%s\n", namecmd); + } else { + namecmd = p->short_cmd; + if(i < 2) + i = 2; + if(strlen(namecmd) > (i-2)) + namecmd[i-2] = 0; + printf("[%s]\n", namecmd); + } + free(p->cmd); + } + return EXIT_SUCCESS; +} + diff --git a/busybox/procps/renice.c b/busybox/procps/renice.c new file mode 100644 index 000000000..a6f0820df --- /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) bb_show_usage(); + + prio = atoi(*++argv); + if (prio > 20) prio = 20; + if (prio < -20) prio = -20; + + while (*++argv) { + int ps = atoi(*argv); + int oldp = getpriority(PRIO_PROCESS, ps); + + if (setpriority(PRIO_PROCESS, ps, prio) == 0) { + printf("%d: old priority %d, new priority %d\n", ps, oldp, prio ); + } else { + bb_perror_msg("%d: setpriority", ps); + status = EXIT_FAILURE; + } + } + + return status; +} diff --git a/busybox/procps/sysctl.c b/busybox/procps/sysctl.c new file mode 100644 index 000000000..359dcc041 --- /dev/null +++ b/busybox/procps/sysctl.c @@ -0,0 +1,352 @@ + +/* + * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters + * + * + * "Copyright 1999 George Staikos + * This file may be used subject to the terms and conditions of the + * GNU General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details." + * + * Changelog: + * v1.01: + * - added -p to preload values from a file + * v1.01.1 + * - busybox applet aware by + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +/* + * Function Prototypes + */ +static int sysctl_read_setting(const char *setting, int output); +static int sysctl_write_setting(const char *setting, int output); +static int sysctl_preload_file(const char *filename, int output); +static int sysctl_display_all(const char *path, int output, int show_table); + +/* + * Globals... + */ +static const char PROC_PATH[] = "/proc/sys/"; +static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf"; + +/* error messages */ +static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter '%s'\n"; +static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting '%s'\n"; +static const char ERR_NO_EQUALS[] = + "error: '%s' must be of the form name=value\n"; +static const char ERR_INVALID_KEY[] = "error: '%s' is an unknown key\n"; +static const char ERR_UNKNOWN_WRITING[] = + "error: unknown error %d setting key '%s'\n"; +static const char ERR_UNKNOWN_READING[] = + "error: unknown error %d reading key '%s'\n"; +static const char ERR_PERMISSION_DENIED[] = + "error: permission denied on key '%s'\n"; +static const char ERR_OPENING_DIR[] = "error: unable to open directory '%s'\n"; +static const char ERR_PRELOAD_FILE[] = + "error: unable to open preload file '%s'\n"; +static const char WARN_BAD_LINE[] = + "warning: %s(%d): invalid syntax, continuing...\n"; + + +static void dwrite_str(int fd, const char *buf) +{ + write(fd, buf, strlen(buf)); +} + +/* + * sysctl_main()... + */ +int sysctl_main(int argc, char **argv) +{ + int retval = 0; + int output = 1; + int write_mode = 0; + int switches_allowed = 1; + + if (argc < 2) + bb_show_usage(); + + argv++; + + for (; argv && *argv && **argv; argv++) { + if (switches_allowed && **argv == '-') { /* we have a switch */ + switch ((*argv)[1]) { + case 'n': + output = 0; + break; + case 'w': + write_mode = 1; + switches_allowed = 0; + break; + case 'p': + argv++; + return + sysctl_preload_file(((argv && *argv + && **argv) ? *argv : + DEFAULT_PRELOAD), output); + case 'a': + case 'A': + switches_allowed = 0; + return sysctl_display_all(PROC_PATH, output, + ((*argv)[1] == 'a') ? 0 : 1); + case 'h': + case '?': + bb_show_usage(); + default: + bb_error_msg(ERR_UNKNOWN_PARAMETER, *argv); + bb_show_usage(); + } + } else { + switches_allowed = 0; + if (write_mode) + retval = sysctl_write_setting(*argv, output); + else + sysctl_read_setting(*argv, output); + } + } + return retval; +} /* end sysctl_main() */ + + + +/* + * sysctl_preload_file + * preload the sysctl's from a conf file + * - we parse the file and then reform it (strip out whitespace) + */ +#define PRELOAD_BUF 256 + +int sysctl_preload_file(const char *filename, int output) +{ + int lineno = 0; + char oneline[PRELOAD_BUF]; + char buffer[PRELOAD_BUF]; + char *name, *value, *ptr; + FILE *fp = NULL; + + if (!filename || ((fp = fopen(filename, "r")) == NULL)) { + bb_error_msg_and_die(ERR_PRELOAD_FILE, filename); + } + + while (fgets(oneline, sizeof(oneline) - 1, fp)) { + oneline[sizeof(oneline) - 1] = 0; + lineno++; + trim(oneline); + ptr = (char *) oneline; + + if (*ptr == '#' || *ptr == ';') + continue; + + if (bb_strlen(ptr) < 2) + continue; + + name = strtok(ptr, "="); + if (!name || !*name) { + bb_error_msg(WARN_BAD_LINE, filename, lineno); + continue; + } + + trim(name); + + value = strtok(NULL, "\n\r"); + if (!value || !*value) { + bb_error_msg(WARN_BAD_LINE, filename, lineno); + continue; + } + + while ((*value == ' ' || *value == '\t') && *value != 0) + value++; + strcpy(buffer, name); + strcat(buffer, "="); + strcat(buffer, value); + sysctl_write_setting(buffer, output); + } + fclose(fp); + return 0; +} /* end sysctl_preload_file() */ + + +/* + * Write a single sysctl setting + */ +int sysctl_write_setting(const char *setting, int output) +{ + int retval = 0; + const char *name = setting; + const char *value; + const char *equals; + char *tmpname, *outname, *cptr; + int fd = -1; + + if (!name) /* probably dont' want to display this err */ + return 0; + + if (!(equals = strchr(setting, '='))) { + bb_error_msg(ERR_NO_EQUALS, setting); + return -1; + } + + value = equals + sizeof(char); /* point to the value in name=value */ + + if (!*name || !*value || name == equals) { + bb_error_msg(ERR_MALFORMED_SETTING, setting); + return -2; + } + + bb_xasprintf(&tmpname, "%s%.*s", PROC_PATH, (equals - name), name); + outname = bb_xstrdup(tmpname + strlen(PROC_PATH)); + + while ((cptr = strchr(tmpname, '.')) != NULL) + *cptr = '/'; + + while ((cptr = strchr(outname, '/')) != NULL) + *cptr = '.'; + + if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { + switch (errno) { + case ENOENT: + bb_error_msg(ERR_INVALID_KEY, outname); + break; + case EACCES: + bb_perror_msg(ERR_PERMISSION_DENIED, outname); + break; + default: + bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname); + break; + } + retval = -1; + } else { + dwrite_str(fd, value); + close(fd); + if (output) { + dwrite_str(STDOUT_FILENO, outname); + dwrite_str(STDOUT_FILENO, " = "); + } + dwrite_str(STDOUT_FILENO, value); + dwrite_str(STDOUT_FILENO, "\n"); + } + + /* cleanup */ + free(tmpname); + free(outname); + return retval; +} /* end sysctl_write_setting() */ + + +/* + * Read a sysctl setting + * + */ +int sysctl_read_setting(const char *setting, int output) +{ + int retval = 0; + char *tmpname, *outname, *cptr; + char inbuf[1025]; + const char *name = setting; + FILE *fp; + + if (!setting || !*setting) + bb_error_msg(ERR_INVALID_KEY, setting); + + tmpname = concat_path_file(PROC_PATH, name); + outname = bb_xstrdup(tmpname + strlen(PROC_PATH)); + + while ((cptr = strchr(tmpname, '.')) != NULL) + *cptr = '/'; + while ((cptr = strchr(outname, '/')) != NULL) + *cptr = '.'; + + if ((fp = fopen(tmpname, "r")) == NULL) { + switch (errno) { + case ENOENT: + bb_error_msg(ERR_INVALID_KEY, outname); + break; + case EACCES: + bb_error_msg(ERR_PERMISSION_DENIED, outname); + break; + default: + bb_error_msg(ERR_UNKNOWN_READING, errno, outname); + break; + } + retval = -1; + } else { + while (fgets(inbuf, sizeof(inbuf) - 1, fp)) { + if (output) { + dwrite_str(STDOUT_FILENO, outname); + dwrite_str(STDOUT_FILENO, " = "); + } + dwrite_str(STDOUT_FILENO, inbuf); + } + fclose(fp); + } + + free(tmpname); + free(outname); + return retval; +} /* end sysctl_read_setting() */ + + + +/* + * Display all the sysctl settings + * + */ +int sysctl_display_all(const char *path, int output, int show_table) +{ + int retval = 0; + int retval2; + DIR *dp; + struct dirent *de; + char *tmpdir; + struct stat ts; + + if (!(dp = opendir(path))) { + bb_perror_msg(ERR_OPENING_DIR, path); + retval = -1; + } else { + while ((de = readdir(dp)) != NULL) { + tmpdir = concat_subpath_file(path, de->d_name); + if(tmpdir == NULL) + continue; + if ((retval2 = stat(tmpdir, &ts)) != 0) + bb_perror_msg(tmpdir); + else { + if (S_ISDIR(ts.st_mode)) { + sysctl_display_all(tmpdir, output, show_table); + } else + retval |= + sysctl_read_setting(tmpdir + bb_strlen(PROC_PATH), + output); + + } + free(tmpdir); + } /* end while */ + closedir(dp); + } + + return retval; +} /* end sysctl_display_all() */ + +#ifdef STANDALONE_SYSCTL +int main(int argc, char **argv) +{ + return sysctl_main(argc, argv); +} +const char *bb_applet_name = "sysctl"; +#endif diff --git a/busybox/procps/top.c b/busybox/procps/top.c new file mode 100644 index 000000000..5963c3554 --- /dev/null +++ b/busybox/procps/top.c @@ -0,0 +1,592 @@ +/* + * A tiny 'top' utility. + * + * This is written specifically for the linux /proc//stat(m) + * files format. + + * This reads the PIDs of all processes and their status and shows + * the status of processes (first ones that fit to screen) at given + * intervals. + * + * NOTES: + * - At startup this changes to /proc, all the reads are then + * relative to that. + * + * (C) Eero Tamminen + * + * Rewritten by Vladimir Oleynik (C) 2002 + */ + +/* Original code Copyrights */ +/* + * Copyright (c) 1992 Branko Lankester + * Copyright (c) 1992 Roger Binns + * Copyright (C) 1994-1996 Charles L. Blake. + * Copyright (C) 1992-1998 Michael K. Johnson + * May be distributed under the conditions of the + * GNU Library General Public License + */ + +#include +#include +#include +#include +#include +#include +/* get page info */ +#include +#include "busybox.h" + +//#define FEATURE_CPU_USAGE_PERCENTAGE /* + 2k */ + +#ifdef FEATURE_CPU_USAGE_PERCENTAGE +#include +#include +#include +#include /* htons */ +#endif + + +typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q); + +static procps_status_t *top; /* Hehe */ +static int ntop; + + +static int pid_sort (procps_status_t *P, procps_status_t *Q) +{ + return (Q->pid - P->pid); +} + +static int mem_sort (procps_status_t *P, procps_status_t *Q) +{ + return (int)(Q->rss - P->rss); +} + +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + +#define sort_depth 3 +static cmp_t sort_function[sort_depth]; + +static int pcpu_sort (procps_status_t *P, procps_status_t *Q) +{ + return (Q->pcpu - P->pcpu); +} + +static int time_sort (procps_status_t *P, procps_status_t *Q) +{ + return (int)((Q->stime + Q->utime) - (P->stime + P->utime)); +} + +int mult_lvl_cmp(void* a, void* b) { + int i, cmp_val; + + for(i = 0; i < sort_depth; i++) { + cmp_val = (*sort_function[i])(a, b); + if (cmp_val != 0) + return cmp_val; + } + return 0; +} + +/* This structure stores some critical information from one frame to + the next. mostly used for sorting. Added cumulative and resident fields. */ +struct save_hist { + int ticks; + int pid; + int utime; + int stime; +}; + +/* + * Calculates percent cpu usage for each task. + */ + +static struct save_hist *save_history; + +static unsigned long Hertz; + +/*********************************************************************** + * Some values in /proc are expressed in units of 1/HZ seconds, where HZ + * is the kernel clock tick rate. One of these units is called a jiffy. + * The HZ value used in the kernel may vary according to hacker desire. + * According to Linus Torvalds, this is not true. He considers the values + * in /proc as being in architecture-dependent units that have no relation + * to the kernel clock tick rate. Examination of the kernel source code + * reveals that opinion as wishful thinking. + * + * In any case, we need the HZ constant as used in /proc. (the real HZ value + * may differ, but we don't care) There are several ways we could get HZ: + * + * 1. Include the kernel header file. If it changes, recompile this library. + * 2. Use the sysconf() function. When HZ changes, recompile the C library! + * 3. Ask the kernel. This is obviously correct... + * + * Linus Torvalds won't let us ask the kernel, because he thinks we should + * not know the HZ value. Oh well, we don't have to listen to him. + * Someone smuggled out the HZ value. :-) + * + * This code should work fine, even if Linus fixes the kernel to match his + * stated behavior. The code only fails in case of a partial conversion. + * + */ + +#define FILE_TO_BUF(filename, fd) do{ \ + if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \ + bb_perror_msg_and_die("/proc not be mounted?"); \ + } \ + lseek(fd, 0L, SEEK_SET); \ + if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ + bb_perror_msg_and_die("%s", filename); \ + } \ + buf[local_n] = '\0'; \ +}while(0) + +#define FILE_TO_BUF2(filename, fd) do{ \ + lseek(fd, 0L, SEEK_SET); \ + if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ + bb_perror_msg_and_die("%s", filename); \ + } \ + buf[local_n] = '\0'; \ +}while(0) + +static void init_Hertz_value(void) { + unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */ + double up_1, up_2, seconds; + unsigned long jiffies, h; + char buf[80]; + int uptime_fd = -1; + int stat_fd = -1; + + long smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF); + + if(smp_num_cpus<1) smp_num_cpus=1; + do { + int local_n; + + FILE_TO_BUF("uptime", uptime_fd); + up_1 = strtod(buf, 0); + FILE_TO_BUF("stat", stat_fd); + sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j); + FILE_TO_BUF2("uptime", uptime_fd); + up_2 = strtod(buf, 0); + } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */ + + close(uptime_fd); + close(stat_fd); + + jiffies = user_j + nice_j + sys_j + other_j; + seconds = (up_1 + up_2) / 2; + h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus ); + /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */ + switch(h){ + case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */ + case 48 ... 52 : Hertz = 50; break; + case 58 ... 62 : Hertz = 60; break; + case 63 ... 65 : Hertz = 64; break; /* StrongARM /Shark */ + case 95 ... 105 : Hertz = 100; break; /* normal Linux */ + case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */ + case 195 ... 204 : Hertz = 200; break; /* normal << 1 */ + case 253 ... 260 : Hertz = 256; break; + case 295 ... 304 : Hertz = 300; break; /* 3 cpus */ + case 393 ... 408 : Hertz = 400; break; /* normal << 2 */ + case 495 ... 504 : Hertz = 500; break; /* 5 cpus */ + case 595 ... 604 : Hertz = 600; break; /* 6 cpus */ + case 695 ... 704 : Hertz = 700; break; /* 7 cpus */ + case 790 ... 808 : Hertz = 800; break; /* normal << 3 */ + case 895 ... 904 : Hertz = 900; break; /* 9 cpus */ + case 990 ... 1010 : Hertz = 1000; break; /* ARM */ + case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */ + case 1095 ... 1104 : Hertz = 1100; break; /* 11 cpus */ + case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */ + default: + /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */ + Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL; + } +} + +static void do_stats(void) +{ + struct timeval t; + static struct timeval oldtime; + struct timezone timez; + float elapsed_time; + + procps_status_t *cur; + int total_time, i, n; + static int prev_count; + int systime, usrtime, pid; + + struct save_hist *New_save_hist; + + /* + * Finds the current time (in microseconds) and calculates the time + * elapsed since the last update. + */ + gettimeofday(&t, &timez); + elapsed_time = (t.tv_sec - oldtime.tv_sec) + + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0; + oldtime.tv_sec = t.tv_sec; + oldtime.tv_usec = t.tv_usec; + + New_save_hist = alloca(sizeof(struct save_hist)*ntop); + /* + * Make a pass through the data to get stats. + */ + for(n = 0; n < ntop; n++) { + cur = top + n; + + /* + * Calculate time in cur process. Time is sum of user time + * (usrtime) plus system time (systime). + */ + systime = cur->stime; + usrtime = cur->utime; + pid = cur->pid; + total_time = systime + usrtime; + New_save_hist[n].ticks = total_time; + New_save_hist[n].pid = pid; + New_save_hist[n].stime = systime; + New_save_hist[n].utime = usrtime; + + /* find matching entry from previous pass */ + for (i = 0; i < prev_count; i++) { + if (save_history[i].pid == pid) { + total_time -= save_history[i].ticks; + systime -= save_history[i].stime; + usrtime -= save_history[i].utime; + break; + } + } + + /* + * Calculate percent cpu time for cur task. + */ + i = (total_time * 10 * 100/Hertz) / elapsed_time; + if (i > 999) + i = 999; + cur->pcpu = i; + + } + + /* + * Save cur frame's information. + */ + free(save_history); + save_history = memcpy(xmalloc(sizeof(struct save_hist)*n), New_save_hist, + sizeof(struct save_hist)*n); + prev_count = n; + qsort(top, n, sizeof(procps_status_t), (void*)mult_lvl_cmp); +} +#else +static cmp_t sort_function; +#endif /* FEATURE_CPU_USAGE_PERCENTAGE */ + +/* display generic info (meminfo / loadavg) */ +static unsigned long display_generic(void) +{ + FILE *fp; + char buf[80]; + float avg1, avg2, avg3; + unsigned long total, used, mfree, shared, buffers, cached; + unsigned int needs_conversion = 1; + + /* read memory info */ + fp = bb_xfopen("meminfo", "r"); + + /* + * Old kernels (such as 2.4.x) had a nice summary of memory info that + * we could parse, however this is gone entirely in 2.6. Try parsing + * the old way first, and if that fails, parse each field manually. + * + * First, we read in the first line. Old kernels will have bogus + * strings we don't care about, whereas new kernels will start right + * out with MemTotal: + * -- PFM. + */ + if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) { + fgets(buf, sizeof(buf), fp); /* skip first line */ + + fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu", + &total, &used, &mfree, &shared, &buffers, &cached); + } else { + /* + * Revert to manual parsing, which incidentally already has the + * sizes in kilobytes. This should be safe for both 2.4 and + * 2.6. + */ + needs_conversion = 0; + + fscanf(fp, "MemFree: %lu %s\n", &mfree, buf); + + /* + * MemShared: is no longer present in 2.6. Report this as 0, + * to maintain consistent behavior with normal procps. + */ + if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2) + shared = 0; + + fscanf(fp, "Buffers: %lu %s\n", &buffers, buf); + fscanf(fp, "Cached: %lu %s\n", &cached, buf); + + used = total - mfree; + } + fclose(fp); + + /* read load average */ + fp = bb_xfopen("loadavg", "r"); + if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) { + bb_error_msg_and_die("failed to read '%s'", "loadavg"); + } + fclose(fp); + + if (needs_conversion) { + /* convert to kilobytes */ + used /= 1024; + mfree /= 1024; + shared /= 1024; + buffers /= 1024; + cached /= 1024; + total /= 1024; + } + + /* output memory info and load average */ + /* clear screen & go to top */ + printf("\e[H\e[J" "Mem: " + "%ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n", + used, mfree, shared, buffers, cached); + printf("Load average: %.2f, %.2f, %.2f " + "(State: S=sleeping R=running, W=waiting)\n", + avg1, avg2, avg3); + return total; +} + + +/* display process statuses */ +static void display_status(int count, int col) +{ + procps_status_t *s = top; + char rss_str_buf[8]; + unsigned long total_memory = display_generic(); + +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + /* what info of the processes is shown */ + printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%MEM COMMAND\e[0m\n"); +#else + printf("\n\e[7m PID USER STATUS RSS PPID %%MEM COMMAND\e[0m\n"); +#endif + + while (count--) { + char *namecmd = s->short_cmd; + int pmem; + + pmem = 1000.0 * s->rss / total_memory; + if (pmem > 999) pmem = 999; + + if(s->rss > 10*1024) + sprintf(rss_str_buf, "%6ldM", s->rss/1024); + else + sprintf(rss_str_buf, "%7ld", s->rss); +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + printf("%5d %-8s %s %s %5d %2d.%d %2u.%u ", + s->pid, s->user, s->state, rss_str_buf, s->ppid, + s->pcpu/10, s->pcpu%10, pmem/10, pmem%10); +#else + printf("%5d %-8s %s %s %5d %2u.%u ", + s->pid, s->user, s->state, rss_str_buf, s->ppid, + pmem/10, pmem%10); +#endif + if(strlen(namecmd) > col) + namecmd[col] = 0; + printf("%s\n", namecmd); + s++; + } +} + +static void clearmems(void) +{ + free(top); + top = 0; + ntop = 0; +} + +#if defined CONFIG_FEATURE_USE_TERMIOS +#include +#include +#include + + +static struct termios initial_settings; + +static void reset_term(void) +{ + tcsetattr(0, TCSANOW, (void *) &initial_settings); +#ifdef CONFIG_FEATURE_CLEAN_UP + clearmems(); +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + free(save_history); +#endif +#endif /* CONFIG_FEATURE_CLEAN_UP */ +} + +static void sig_catcher (int sig) +{ + reset_term(); +} +#endif /* CONFIG_FEATURE_USE_TERMIOS */ + + +int top_main(int argc, char **argv) +{ + int opt, interval, lines, col; +#if defined CONFIG_FEATURE_USE_TERMIOS + struct termios new_settings; + struct timeval tv; + fd_set readfds; + unsigned char c; + struct sigaction sa; +#endif /* CONFIG_FEATURE_USE_TERMIOS */ + + /* Default update rate is 5 seconds */ + interval = 5; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "d:")) > 0) { + switch (opt) { + case 'd': + interval = atoi(optarg); + break; + default: + bb_show_usage(); + } + } + + /* Default to 25 lines - 5 lines for status */ + lines = 25 - 5; + /* Default CMD format size */ +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + col = 35 - 6; +#else + col = 35; +#endif + /* change to /proc */ + if (chdir("/proc") < 0) { + bb_perror_msg_and_die("chdir('/proc')"); + } +#if defined CONFIG_FEATURE_USE_TERMIOS + tcgetattr(0, (void *) &initial_settings); + memcpy(&new_settings, &initial_settings, sizeof(struct termios)); + new_settings.c_lflag &= ~(ISIG | ICANON); /* unbuffered input */ + /* Turn off echoing */ + new_settings.c_lflag &= ~(ECHO | ECHONL); + + signal (SIGTERM, sig_catcher); + sigaction (SIGTERM, (struct sigaction *) 0, &sa); + sa.sa_flags |= SA_RESTART; + sa.sa_flags &= ~SA_INTERRUPT; + sigaction (SIGTERM, &sa, (struct sigaction *) 0); + sigaction (SIGINT, &sa, (struct sigaction *) 0); + tcsetattr(0, TCSANOW, (void *) &new_settings); + atexit(reset_term); + + get_terminal_width_height(0, &col, &lines); + if (lines > 4) { + lines -= 5; +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + col = col - 80 + 35 - 6; +#else + col = col - 80 + 35; +#endif + } +#endif /* CONFIG_FEATURE_USE_TERMIOS */ +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + sort_function[0] = pcpu_sort; + sort_function[1] = mem_sort; + sort_function[2] = time_sort; +#else + sort_function = mem_sort; +#endif + while (1) { + /* read process IDs & status for all the processes */ + procps_status_t * p; + +#ifdef CONFIG_SELINUX + while ((p = procps_scan(0, 0, NULL) ) != 0) { +#else + while ((p = procps_scan(0)) != 0) { +#endif + int n = ntop; + + top = xrealloc(top, (++ntop)*sizeof(procps_status_t)); + memcpy(top + n, p, sizeof(procps_status_t)); + } + if (ntop == 0) { + bb_perror_msg_and_die("scandir('/proc')"); + } +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + if(!Hertz) { + init_Hertz_value(); + do_stats(); + sleep(1); + clearmems(); + continue; + } + do_stats(); +#else + qsort(top, ntop, sizeof(procps_status_t), (void*)sort_function); +#endif + opt = lines; + if (opt > ntop) { + opt = ntop; + } + /* show status for each of the processes */ + display_status(opt, col); +#if defined CONFIG_FEATURE_USE_TERMIOS + tv.tv_sec = interval; + tv.tv_usec = 0; + FD_ZERO (&readfds); + FD_SET (0, &readfds); + select (1, &readfds, NULL, NULL, &tv); + if (FD_ISSET (0, &readfds)) { + if (read (0, &c, 1) <= 0) { /* signal */ + return EXIT_FAILURE; + } + if(c == 'q' || c == initial_settings.c_cc[VINTR]) + return EXIT_SUCCESS; + if(c == 'M') { +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + sort_function[0] = mem_sort; + sort_function[1] = pcpu_sort; + sort_function[2] = time_sort; +#else + sort_function = mem_sort; +#endif + } +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + if(c == 'P') { + sort_function[0] = pcpu_sort; + sort_function[1] = mem_sort; + sort_function[2] = time_sort; + } + if(c == 'T') { + sort_function[0] = time_sort; + sort_function[1] = mem_sort; + sort_function[2] = pcpu_sort; + } +#endif + if(c == 'N') { +#ifdef FEATURE_CPU_USAGE_PERCENTAGE + sort_function[0] = pid_sort; +#else + sort_function = pid_sort; +#endif + } + } +#else + sleep(interval); +#endif /* CONFIG_FEATURE_USE_TERMIOS */ + clearmems(); + } + + return EXIT_SUCCESS; +} diff --git a/busybox/procps/uptime.c b/busybox/procps/uptime.c new file mode 100644 index 000000000..38d58af9c --- /dev/null +++ b/busybox/procps/uptime.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini uptime implementation for busybox + * + * Copyright (C) 1999-2004 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 CONFIG_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(" %02d:%02d:%02d up ", + current_time->tm_hour, current_time->tm_min, current_time->tm_sec); + updays = (int) info.uptime / (60*60*24); + if (updays) + printf("%d day%s, ", updays, (updays != 1) ? "s" : ""); + upminutes = (int) info.uptime / 60; + uphours = (upminutes / 60) % 24; + upminutes %= 60; + if(uphours) + printf("%2d:%02d, ", uphours, upminutes); + else + printf("%d min, ", upminutes); + + printf("load average: %ld.%02ld, %ld.%02ld, %ld.%02ld\n", + LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]), + LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]), + LOAD_INT(info.loads[2]), LOAD_FRAC(info.loads[2])); + + return EXIT_SUCCESS; +} diff --git a/busybox/scripts/.cvsignore b/busybox/scripts/.cvsignore new file mode 100644 index 000000000..07fa550f5 --- /dev/null +++ b/busybox/scripts/.cvsignore @@ -0,0 +1,2 @@ +mkdep +split-include diff --git a/busybox/scripts/config/.cvsignore b/busybox/scripts/config/.cvsignore new file mode 100644 index 000000000..e8bf7a75b --- /dev/null +++ b/busybox/scripts/config/.cvsignore @@ -0,0 +1,8 @@ +conf +mconf +lkc_defs.h +lex.zconf.c +zconf.tab.h +zconf.tab.c +lex.backup +zconf.output diff --git a/busybox/scripts/config/Kconfig-language.txt b/busybox/scripts/config/Kconfig-language.txt new file mode 100644 index 000000000..493749b32 --- /dev/null +++ b/busybox/scripts/config/Kconfig-language.txt @@ -0,0 +1,255 @@ +Introduction +------------ + +The configuration database is collection of configuration options +organized in a tree structure: + + +- Code maturity level options + | +- Prompt for development and/or incomplete code/drivers + +- General setup + | +- Networking support + | +- System V IPC + | +- BSD Process Accounting + | +- Sysctl support + +- Loadable module support + | +- Enable loadable module support + | +- Set version information on all module symbols + | +- Kernel module loader + +- ... + +Every entry has its own dependencies. These dependencies are used +to determine the visible of an entry. Any child entry is only +visible if its parent entry is also visible. + +Menu entries +------------ + +Most entries define a config option, all other entries help to organize +them. A single configuration option is defined like this: + +config MODVERSIONS + bool "Set version information on all module symbols" + depends MODULES + help + Usually, modules have to be recompiled whenever you switch to a new + kernel. ... + +Every line starts with a key word and can be followed by multiple +arguments. "config" starts a new config entry. The following lines +define attributes for this config option. Attributes can be the type of +the config option, input prompt, dependencies, help text and default +values. A config option can be defined multiple times with the same +name, but every definition can have only a single input prompt and the +type must not conflict. + +Menu attributes +--------------- + +A menu entry can have a number of attributes. Not all of them are +applicable everywhere (see syntax). + +- type definition: "bool"/"tristate"/"string"/"hex"/"integer" + Every config option must have a type. There are only two basic types: + tristate and string, the other types base on these two. The type + definition optionally accepts an input prompt, so these two examples + are equivalent: + + bool "Networking support" + and + bool + prompt "Networking support" + +- input prompt: "prompt" ["if" ] + Every menu entry can have at most one prompt, which is used to display + to the user. Optionally dependencies only for this prompt can be added + with "if". + +- default value: "default" ["if" ] + A config option can have any number of default values. If multiple + default values are visible, only the first defined one is active. + Default values are not limited to the menu entry, where they are + defined, this means the default can be defined somewhere else or be + overriden by an earlier definition. + The default value is only assigned to the config symbol if no other + value was set by the user (via the input prompt above). If an input + prompt is visible the default value is presented to the user and can + be overridden by him. + Optionally dependencies only for this default value can be added with + "if". + +- dependencies: "depends on"/"requires" + This defines a dependency for this menu entry. If multiple + dependencies are defined they are connected with '&&'. Dependencies + are applied to all other options within this menu entry (which also + accept "if" expression), so these two examples are equivalent: + + bool "foo" if BAR + default y if BAR + and + depends on BAR + bool "foo" + default y + +- help text: "help" + This defines a help text. The end of the help text is determined by + the level indentation, this means it ends at the first line which has + a smaller indentation than the first line of the help text. + + +Menu dependencies +----------------- + +Dependencies define the visibility of a menu entry and can also reduce +the input range of tristate symbols. The tristate logic used in the +expressions uses one more state than normal boolean logic to express the +module state. Dependency expressions have the following syntax: + + ::= (1) + '=' (2) + '!=' (3) + '(' ')' (4) + '!' (5) + '||' (6) + '&&' (7) + +Expressions are listed in decreasing order of precedence. + +(1) Convert the symbol into an expression. Boolean and tristate symbols + are simply converted into the respective expression values. All + other symbol types result in 'n'. +(2) If the values of both symbols are equal, it returns 'y', + otherwise 'n'. +(3) If the values of both symbols are equal, it returns 'n', + otherwise 'y'. +(4) Returns the value of the expression. Used to override precedence. +(5) Returns the result of (2-/expr/). +(6) Returns the result of min(/expr/, /expr/). +(7) Returns the result of max(/expr/, /expr/). + +An expression can have a value of 'n', 'm' or 'y' (or 0, 1, 2 +respectively for calculations). A menu entry becomes visible when it's +expression evaluates to 'm' or 'y'. + +There are two type of symbols: constant and nonconstant symbols. +Nonconstant symbols are the most common ones and are defined with the +'config' statement. Nonconstant symbols consist entirely of alphanumeric +characters or underscores. +Constant symbols are only part of expressions. Constant symbols are +always surrounded by single or double quotes. Within the quote any +other character is allowed and the quotes can be escaped using '\'. + +Menu structure +-------------- + +The position of a menu entry in the tree is determined in two ways. First +it can be specified explicitely: + +menu "Network device support" + depends NET + +config NETDEVICES + ... + +endmenu + +All entries within the "menu" ... "endmenu" block become a submenu of +"Network device support". All subentries inherit the dependencies from +the menu entry, e.g. this means the dependency "NET" is added to the +dependency list of the config option NETDEVICES. + +The other way to generate the menu structure is done by analyzing the +dependencies. If a menu entry somehow depends on the previous entry, it +can be made a submenu of it. First the the previous (parent) symbol must +be part of the dependency list and then one of these two condititions +must be true: +- the child entry must become invisible, if the parent is set to 'n' +- the child entry must only be visible, if the parent is visible + +config MODULES + bool "Enable loadable module support" + +config MODVERSIONS + bool "Set version information on all module symbols" + depends MODULES + +comment "module support disabled" + depends !MODULES + +MODVERSIONS directly depends on MODULES, this means it's only visible if +MODULES is different from 'n'. The comment on the other hand is always +visible when MODULES it's visible (the (empty) dependency of MODULES is +also part of the comment dependencies). + + +Kconfig syntax +-------------- + +The configuration file describes a series of menu entries, where every +line starts with a keyword (except help texts). The following keywords +end a menu entry: +- config +- choice/endchoice +- comment +- menu/endmenu +- if/endif +- source +The first four also start the definition of a menu entry. + +config: + + "config" + + +This defines a config symbol and accepts any of above +attributes as options. + +choices: + + "choice" + + + "endchoice" + +This defines a choice group and accepts any of above attributes as +options. A choice can only be of type bool or tristate, while a boolean +choice only allows a single config entry to be selected, a tristate +choice also allows any number of config entries to be set to 'm'. This +can be used if multiple drivers for a single hardware exists and only a +single driver can be compiled/loaded into the kernel, but all drivers +can be compiled as modules. +A choice accepts another option "optional", which allows to set the +choice to 'n' and no entry needs to be selected. + +comment: + + "comment" + + +This defines a comment which is displayed to the user during the +configuration process and is also echoed to the output files. The only +possible options are dependencies. + +menu: + + "menu" + + + "endmenu" + +This defines a menu block, see "Menu structure" above for more +information. The only possible options are dependencies. + +if: + + "if" + + "endif" + +This defines an if block. The dependency expression is appended +to all enclosed menu entries. + +source: + + "source" + +This reads the specified configuration file. This file is always parsed. diff --git a/busybox/scripts/config/Makefile b/busybox/scripts/config/Makefile new file mode 100644 index 000000000..c0b5b9d35 --- /dev/null +++ b/busybox/scripts/config/Makefile @@ -0,0 +1,111 @@ +# Makefile for BusyBox +# +# Copyright (C) 2002 Erik Andersen + +top_srcdir=../.. +top_builddir=../.. +srcdir=$(top_srcdir)/scripts/config +include $(top_builddir)/Rules.mak + +all: ncurses conf mconf + +LIBS = -lncurses +ifeq (/usr/include/ncurses/ncurses.h, $(wildcard /usr/include/ncurses/ncurses.h)) + HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="" +else +ifeq (/usr/include/ncurses/curses.h, $(wildcard /usr/include/ncurses/curses.h)) + HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="" +else +ifeq (/usr/local/include/ncurses/ncurses.h, $(wildcard /usr/local/include/ncurses/ncurses.h)) + HOSTCFLAGS += -I/usr/local/include/ncurses -DCURSES_LOC="" +else +ifeq (/usr/local/include/ncurses/curses.h, $(wildcard /usr/local/include/ncurses/curses.h)) + HOSTCFLAGS += -I/usr/local/include/ncurses -DCURSES_LOC="" +else +ifeq (/usr/include/ncurses.h, $(wildcard /usr/include/ncurses.h)) + HOSTNCURSES += -DCURSES_LOC="" +else + HOSTNCURSES += -DCURSES_LOC="" +endif +endif +endif +endif +endif + +CONF_SRC =conf.c +MCONF_SRC =mconf.c checklist.c menubox.c textbox.c yesno.c inputbox.c util.c msgbox.c +SHARED_SRC=zconf.tab.c +SHARED_DEPS:=$(srcdir)/lkc.h $(srcdir)/lkc_proto.h \ + lkc_defs.h $(srcdir)/expr.h zconf.tab.h +CONF_OBJS =$(patsubst %.c,%.o, $(CONF_SRC)) +MCONF_OBJS=$(patsubst %.c,%.o, $(MCONF_SRC)) +SHARED_OBJS=$(patsubst %.c,%.o, $(SHARED_SRC)) + +conf: $(CONF_OBJS) $(SHARED_OBJS) + $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@ + +mconf: $(MCONF_OBJS) $(SHARED_OBJS) + $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@ $(LIBS) + +$(CONF_OBJS): %.o : $(srcdir)/%.c $(SHARED_DEPS) + $(HOSTCC) $(HOSTCFLAGS) -I. -c $< -o $@ + +$(MCONF_OBJS): %.o : $(srcdir)/%.c $(SHARED_DEPS) + $(HOSTCC) $(HOSTCFLAGS) $(HOSTNCURSES) -I. -c $< -o $@ + +lkc_defs.h: $(srcdir)/lkc_proto.h + @sed < $< > $@ 's/P(\([^,]*\),.*/#define \1 (\*\1_p)/' + +### +# The following requires flex/bison +# By default we use the _shipped versions, uncomment the +# following line if you are modifying the flex/bison src. +#LKC_GENPARSER := 1 + +ifdef LKC_GENPARSER + +%.tab.c %.tab.h: $(srcdir)/%.y + bison -t -d -v -b $* -p $(notdir $*) $< + +lex.%.c: $(srcdir)/%.l + flex -P$(notdir $*) -o$@ $< +else + +lex.zconf.o: lex.zconf.c $(SHARED_DEPS) + $(HOSTCC) $(HOSTCFLAGS) -I$(srcdir) -c $< -o $@ + +lex.zconf.c: $(srcdir)/lex.zconf.c_shipped + cp $< $@ + +zconf.tab.c: $(srcdir)/zconf.tab.c_shipped + cp $< $@ + +zconf.tab.h: $(srcdir)/zconf.tab.h_shipped + cp $< $@ +endif + +zconf.tab.o: zconf.tab.c lex.zconf.c $(srcdir)/confdata.c $(srcdir)/expr.c \ + $(srcdir)/symbol.c $(srcdir)/menu.c $(SHARED_DEPS) + $(HOSTCC) $(HOSTCFLAGS) -I$(srcdir) -I. -c $< -o $@ + +.PHONY: ncurses + +ncurses: + @echo "main() {}" > lxtemp.c + @if $(HOSTCC) lxtemp.c $(LIBS) ; then \ + rm -f lxtemp.c a.out; \ + else \ + rm -f lxtemp.c; \ + echo -e "\007" ;\ + echo ">> Unable to find the Ncurses libraries." ;\ + echo ">>" ;\ + echo ">> You must have Ncurses installed in order" ;\ + echo ">> to use 'make menuconfig'" ;\ + echo ;\ + exit 1 ;\ + fi + +clean: + rm -f *.o *~ core $(TARGETS) $(MCONF_OBJS) $(CONF_OBJS) \ + conf mconf zconf.tab.c zconf.tab.h lex.zconf.c lkc_defs.h + diff --git a/busybox/scripts/config/checklist.c b/busybox/scripts/config/checklist.c new file mode 100644 index 000000000..4dbd16616 --- /dev/null +++ b/busybox/scripts/config/checklist.c @@ -0,0 +1,372 @@ +/* + * checklist.c -- implements the checklist box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension + * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +static int list_width, check_x, item_x, checkflag; + +/* + * Print list item + */ +static void +print_item (WINDOW * win, const char *item, int status, + int choice, int selected) +{ + int i; + + /* Clear 'residue' of last item */ + wattrset (win, menubox_attr); + wmove (win, choice, 0); + for (i = 0; i < list_width; i++) + waddch (win, ' '); + + wmove (win, choice, check_x); + wattrset (win, selected ? check_selected_attr : check_attr); + if (checkflag == FLAG_CHECK) + wprintw (win, "[%c]", status ? 'X' : ' '); + else + wprintw (win, "(%c)", status ? 'X' : ' '); + + wattrset (win, selected ? tag_selected_attr : tag_attr); + mvwaddch(win, choice, item_x, item[0]); + wattrset (win, selected ? item_selected_attr : item_attr); + waddstr (win, (char *)item+1); + if (selected) { + wmove (win, choice, check_x+1); + wrefresh (win); + } +} + +/* + * Print the scroll indicators. + */ +static void +print_arrows (WINDOW * win, int choice, int item_no, int scroll, + int y, int x, int height) +{ + wmove(win, y, x); + + if (scroll > 0) { + wattrset (win, uarrow_attr); + waddch (win, ACS_UARROW); + waddstr (win, "(-)"); + } + else { + wattrset (win, menubox_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + choice < item_no - 1)) { + wattrset (win, darrow_attr); + waddch (win, ACS_DARROW); + waddstr (win, "(+)"); + } + else { + wattrset (win, menubox_border_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } +} + +/* + * Display the termination buttons + */ +static void +print_buttons( WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button (dialog, "Select", y, x, selected == 0); + print_button (dialog, " Help ", y, x + 14, selected == 1); + + wmove(dialog, y, x+1 + 14*selected); + wrefresh (dialog); +} + +/* + * Display a dialog box with a list of options that can be turned on or off + * The `flag' parameter is used to select between radiolist and checklist. + */ +int +dialog_checklist (const char *title, const char *prompt, int height, int width, + int list_height, int item_no, struct dialog_list_item ** items, + int flag) + +{ + int i, x, y, box_x, box_y; + int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status; + WINDOW *dialog, *list; + + checkflag = flag; + + /* Allocate space for storing item on/off status */ + if ((status = malloc (sizeof (int) * item_no)) == NULL) { + endwin (); + fprintf (stderr, + "\nCan't allocate memory in dialog_checklist().\n"); + exit (-1); + } + + /* Initializes status */ + for (i = 0; i < item_no; i++) { + status[i] = (items[i]->selected == 1); /* ON */ + if ((!choice && status[i]) || items[i]->selected == 2) /* SELECTED */ + choice = i + 1; + } + if (choice) + choice--; + + max_choice = MIN (list_height, item_no); + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + list_width = width - 6; + box_y = height - list_height - 5; + box_x = (width - list_width) / 2 - 1; + + /* create new window for the list */ + list = subwin (dialog, list_height, list_width, y+box_y+1, x+box_x+1); + + keypad (list, TRUE); + + /* draw a box around the list items */ + draw_box (dialog, box_y, box_x, list_height + 2, list_width + 2, + menubox_border_attr, menubox_attr); + + /* Find length of longest item in order to center checklist */ + check_x = 0; + for (i = 0; i < item_no; i++) + check_x = MAX (check_x, + strlen (items[i]->name) + 4); + + check_x = (list_width - check_x) / 2; + item_x = check_x + 4; + + if (choice >= list_height) { + scroll = choice - list_height + 1; + choice -= scroll; + } + + /* Print the list */ + for (i = 0; i < max_choice; i++) { + print_item (list, items[scroll + i]->name, + status[i+scroll], i, i == choice); + } + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + print_buttons(dialog, height, width, 0); + + wnoutrefresh (list); + wnoutrefresh (dialog); + doupdate (); + + while (key != ESC) { + key = wgetch (dialog); + + for (i = 0; i < max_choice; i++) + if (toupper(key) == toupper(items[scroll + i]->name[0])) + break; + + + if ( i < max_choice || key == KEY_UP || key == KEY_DOWN || + key == '+' || key == '-' ) { + if (key == KEY_UP || key == '-') { + if (!choice) { + if (!scroll) + continue; + /* Scroll list down */ + if (list_height > 1) { + /* De-highlight current first item */ + print_item (list, items[scroll]->name, + status[scroll], 0, FALSE); + scrollok (list, TRUE); + wscrl (list, -1); + scrollok (list, FALSE); + } + scroll--; + print_item (list, items[scroll]->name, + status[scroll], 0, TRUE); + wnoutrefresh (list); + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + wrefresh (dialog); + + continue; /* wait for another key press */ + } else + i = choice - 1; + } else if (key == KEY_DOWN || key == '+') { + if (choice == max_choice - 1) { + if (scroll + choice >= item_no - 1) + continue; + /* Scroll list up */ + if (list_height > 1) { + /* De-highlight current last item before scrolling up */ + print_item (list, items[scroll + max_choice - 1]->name, + status[scroll + max_choice - 1], + max_choice - 1, FALSE); + scrollok (list, TRUE); + scroll (list); + scrollok (list, FALSE); + } + scroll++; + print_item (list, items[scroll + max_choice - 1]->name, + status[scroll + max_choice - 1], + max_choice - 1, TRUE); + wnoutrefresh (list); + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + wrefresh (dialog); + + continue; /* wait for another key press */ + } else + i = choice + 1; + } + if (i != choice) { + /* De-highlight current item */ + print_item (list, items[scroll + choice]->name, + status[scroll + choice], choice, FALSE); + /* Highlight new item */ + choice = i; + print_item (list, items[scroll + choice]->name, + status[scroll + choice], choice, TRUE); + wnoutrefresh (list); + wrefresh (dialog); + } + continue; /* wait for another key press */ + } + switch (key) { + case 'H': + case 'h': + case '?': + for (i = 0; i < item_no; i++) + items[i]->selected = 0; + items[scroll + choice]->selected = 1; + delwin (dialog); + free (status); + return 1; + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (dialog); + break; + case 'S': + case 's': + case ' ': + case '\n': + if (!button) { + if (flag == FLAG_CHECK) { + status[scroll + choice] = !status[scroll + choice]; + wmove (list, choice, check_x); + wattrset (list, check_selected_attr); + wprintw (list, "[%c]", status[scroll + choice] ? 'X' : ' '); + } else { + if (!status[scroll + choice]) { + for (i = 0; i < item_no; i++) + status[i] = 0; + status[scroll + choice] = 1; + for (i = 0; i < max_choice; i++) + print_item (list, items[scroll + i]->name, + status[scroll + i], i, i == choice); + } + } + wnoutrefresh (list); + wrefresh (dialog); + + for (i = 0; i < item_no; i++) { + items[i]->selected = status[i]; + } + } else { + for (i = 0; i < item_no; i++) + items[i]->selected = 0; + items[scroll + choice]->selected = 1; + } + delwin (dialog); + free (status); + return button; + case 'X': + case 'x': + key = ESC; + case ESC: + break; + } + + /* Now, update everything... */ + doupdate (); + } + + + delwin (dialog); + free (status); + return -1; /* ESC pressed */ +} diff --git a/busybox/scripts/config/colors.h b/busybox/scripts/config/colors.h new file mode 100644 index 000000000..d34dd37c6 --- /dev/null +++ b/busybox/scripts/config/colors.h @@ -0,0 +1,161 @@ +/* + * colors.h -- color attribute definitions + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + * Default color definitions + * + * *_FG = foreground + * *_BG = background + * *_HL = highlight? + */ +#define SCREEN_FG COLOR_CYAN +#define SCREEN_BG COLOR_BLUE +#define SCREEN_HL TRUE + +#define SHADOW_FG COLOR_BLACK +#define SHADOW_BG COLOR_BLACK +#define SHADOW_HL TRUE + +#define DIALOG_FG COLOR_BLACK +#define DIALOG_BG COLOR_WHITE +#define DIALOG_HL FALSE + +#define TITLE_FG COLOR_YELLOW +#define TITLE_BG COLOR_WHITE +#define TITLE_HL TRUE + +#define BORDER_FG COLOR_WHITE +#define BORDER_BG COLOR_WHITE +#define BORDER_HL TRUE + +#define BUTTON_ACTIVE_FG COLOR_WHITE +#define BUTTON_ACTIVE_BG COLOR_BLUE +#define BUTTON_ACTIVE_HL TRUE + +#define BUTTON_INACTIVE_FG COLOR_BLACK +#define BUTTON_INACTIVE_BG COLOR_WHITE +#define BUTTON_INACTIVE_HL FALSE + +#define BUTTON_KEY_ACTIVE_FG COLOR_WHITE +#define BUTTON_KEY_ACTIVE_BG COLOR_BLUE +#define BUTTON_KEY_ACTIVE_HL TRUE + +#define BUTTON_KEY_INACTIVE_FG COLOR_RED +#define BUTTON_KEY_INACTIVE_BG COLOR_WHITE +#define BUTTON_KEY_INACTIVE_HL FALSE + +#define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW +#define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE +#define BUTTON_LABEL_ACTIVE_HL TRUE + +#define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK +#define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE +#define BUTTON_LABEL_INACTIVE_HL TRUE + +#define INPUTBOX_FG COLOR_BLACK +#define INPUTBOX_BG COLOR_WHITE +#define INPUTBOX_HL FALSE + +#define INPUTBOX_BORDER_FG COLOR_BLACK +#define INPUTBOX_BORDER_BG COLOR_WHITE +#define INPUTBOX_BORDER_HL FALSE + +#define SEARCHBOX_FG COLOR_BLACK +#define SEARCHBOX_BG COLOR_WHITE +#define SEARCHBOX_HL FALSE + +#define SEARCHBOX_TITLE_FG COLOR_YELLOW +#define SEARCHBOX_TITLE_BG COLOR_WHITE +#define SEARCHBOX_TITLE_HL TRUE + +#define SEARCHBOX_BORDER_FG COLOR_WHITE +#define SEARCHBOX_BORDER_BG COLOR_WHITE +#define SEARCHBOX_BORDER_HL TRUE + +#define POSITION_INDICATOR_FG COLOR_YELLOW +#define POSITION_INDICATOR_BG COLOR_WHITE +#define POSITION_INDICATOR_HL TRUE + +#define MENUBOX_FG COLOR_BLACK +#define MENUBOX_BG COLOR_WHITE +#define MENUBOX_HL FALSE + +#define MENUBOX_BORDER_FG COLOR_WHITE +#define MENUBOX_BORDER_BG COLOR_WHITE +#define MENUBOX_BORDER_HL TRUE + +#define ITEM_FG COLOR_BLACK +#define ITEM_BG COLOR_WHITE +#define ITEM_HL FALSE + +#define ITEM_SELECTED_FG COLOR_WHITE +#define ITEM_SELECTED_BG COLOR_BLUE +#define ITEM_SELECTED_HL TRUE + +#define TAG_FG COLOR_YELLOW +#define TAG_BG COLOR_WHITE +#define TAG_HL TRUE + +#define TAG_SELECTED_FG COLOR_YELLOW +#define TAG_SELECTED_BG COLOR_BLUE +#define TAG_SELECTED_HL TRUE + +#define TAG_KEY_FG COLOR_YELLOW +#define TAG_KEY_BG COLOR_WHITE +#define TAG_KEY_HL TRUE + +#define TAG_KEY_SELECTED_FG COLOR_YELLOW +#define TAG_KEY_SELECTED_BG COLOR_BLUE +#define TAG_KEY_SELECTED_HL TRUE + +#define CHECK_FG COLOR_BLACK +#define CHECK_BG COLOR_WHITE +#define CHECK_HL FALSE + +#define CHECK_SELECTED_FG COLOR_WHITE +#define CHECK_SELECTED_BG COLOR_BLUE +#define CHECK_SELECTED_HL TRUE + +#define UARROW_FG COLOR_GREEN +#define UARROW_BG COLOR_WHITE +#define UARROW_HL TRUE + +#define DARROW_FG COLOR_GREEN +#define DARROW_BG COLOR_WHITE +#define DARROW_HL TRUE + +/* End of default color definitions */ + +#define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y))) +#define COLOR_NAME_LEN 10 +#define COLOR_COUNT 8 + +/* + * Global variables + */ + +typedef struct { + char name[COLOR_NAME_LEN]; + int value; +} color_names_st; + +extern color_names_st color_names[]; +extern int color_table[][3]; diff --git a/busybox/scripts/config/conf.c b/busybox/scripts/config/conf.c new file mode 100644 index 000000000..3b0c1c1b5 --- /dev/null +++ b/busybox/scripts/config/conf.c @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +static void conf(struct menu *menu); +static void check_conf(struct menu *menu); + +enum { + ask_all, + ask_new, + ask_silent, + set_default, + set_yes, + set_mod, + set_no, + set_random +} input_mode = ask_all; +char *defconfig_file; + +static int indent = 1; +static int valid_stdin = 1; +static int conf_cnt; +static char line[128]; +static struct menu *rootEntry; + +static char nohelp_text[] = "Sorry, no help available for this option yet.\n"; + +static void strip(char *str) +{ + char *p = str; + int l; + + while ((isspace(*p))) + p++; + l = strlen(p); + if (p != str) + memmove(str, p, l + 1); + if (!l) + return; + p = str + l - 1; + while ((isspace(*p))) + *p-- = 0; +} + +static void check_stdin(void) +{ + if (!valid_stdin && input_mode == ask_silent) { + printf("aborted!\n\n"); + printf("Console input/output is redirected. "); + printf("Run 'make oldconfig' to update configuration.\n\n"); + exit(1); + } +} + +static void conf_askvalue(struct symbol *sym, const char *def) +{ + enum symbol_type type = sym_get_type(sym); + tristate val; + + if (!sym_has_value(sym)) + printf("(NEW) "); + + line[0] = '\n'; + line[1] = 0; + + if (!sym_is_changable(sym)) { + printf("%s\n", def); + line[0] = '\n'; + line[1] = 0; + return; + } + + switch (input_mode) { + case ask_new: + case ask_silent: + if (sym_has_value(sym)) { + printf("%s\n", def); + return; + } + check_stdin(); + case ask_all: + fflush(stdout); + fgets(line, 128, stdin); + return; + case set_default: + printf("%s\n", def); + return; + default: + break; + } + + switch (type) { + case S_INT: + case S_HEX: + case S_STRING: + printf("%s\n", def); + return; + default: + ; + } + switch (input_mode) { + case set_yes: + if (sym_tristate_within_range(sym, yes)) { + line[0] = 'y'; + line[1] = '\n'; + line[2] = 0; + break; + } + case set_mod: + if (type == S_TRISTATE) { + if (sym_tristate_within_range(sym, mod)) { + line[0] = 'm'; + line[1] = '\n'; + line[2] = 0; + break; + } + } else { + if (sym_tristate_within_range(sym, yes)) { + line[0] = 'y'; + line[1] = '\n'; + line[2] = 0; + break; + } + } + case set_no: + if (sym_tristate_within_range(sym, no)) { + line[0] = 'n'; + line[1] = '\n'; + line[2] = 0; + break; + } + case set_random: + do { + val = (tristate)(random() % 3); + } while (!sym_tristate_within_range(sym, val)); + switch (val) { + case no: line[0] = 'n'; break; + case mod: line[0] = 'm'; break; + case yes: line[0] = 'y'; break; + } + line[1] = '\n'; + line[2] = 0; + break; + default: + break; + } + printf("%s", line); +} + +int conf_string(struct menu *menu) +{ + struct symbol *sym = menu->sym; + const char *def, *help; + + while (1) { + printf("%*s%s ", indent - 1, "", menu->prompt->text); + printf("(%s) ", sym->name); + def = sym_get_string_value(sym); + if (sym_get_string_value(sym)) + printf("[%s] ", def); + conf_askvalue(sym, def); + switch (line[0]) { + case '\n': + break; + case '?': + /* print help */ + if (line[1] == '\n') { + help = nohelp_text; + if (menu->sym->help) + help = menu->sym->help; + printf("\n%s\n", menu->sym->help); + def = NULL; + break; + } + default: + line[strlen(line)-1] = 0; + def = line; + } + if (def && sym_set_string_value(sym, def)) + return 0; + } +} + +static int conf_sym(struct menu *menu) +{ + struct symbol *sym = menu->sym; + int type; + tristate oldval, newval; + const char *help; + + while (1) { + printf("%*s%s ", indent - 1, "", menu->prompt->text); + if (sym->name) + printf("(%s) ", sym->name); + type = sym_get_type(sym); + putchar('['); + oldval = sym_get_tristate_value(sym); + switch (oldval) { + case no: + putchar('N'); + break; + case mod: + putchar('M'); + break; + case yes: + putchar('Y'); + break; + } + if (oldval != no && sym_tristate_within_range(sym, no)) + printf("/n"); + if (oldval != mod && sym_tristate_within_range(sym, mod)) + printf("/m"); + if (oldval != yes && sym_tristate_within_range(sym, yes)) + printf("/y"); + if (sym->help) + printf("/?"); + printf("] "); + conf_askvalue(sym, sym_get_string_value(sym)); + strip(line); + + switch (line[0]) { + case 'n': + case 'N': + newval = no; + if (!line[1] || !strcmp(&line[1], "o")) + break; + continue; + case 'm': + case 'M': + newval = mod; + if (!line[1]) + break; + continue; + case 'y': + case 'Y': + newval = yes; + if (!line[1] || !strcmp(&line[1], "es")) + break; + continue; + case 0: + newval = oldval; + break; + case '?': + goto help; + default: + continue; + } + if (sym_set_tristate_value(sym, newval)) + return 0; +help: + help = nohelp_text; + if (sym->help) + help = sym->help; + printf("\n%s\n", help); + } +} + +static int conf_choice(struct menu *menu) +{ + struct symbol *sym, *def_sym; + struct menu *child; + int type; + bool is_new; + + sym = menu->sym; + type = sym_get_type(sym); + is_new = !sym_has_value(sym); + if (sym_is_changable(sym)) { + conf_sym(menu); + sym_calc_value(sym); + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + return 0; + case yes: + break; + } + } else { + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); + return 0; + case yes: + break; + } + } + + while (1) { + int cnt, def; + + printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); + def_sym = sym_get_choice_value(sym); + cnt = def = 0; + line[0] = '0'; + line[1] = 0; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + if (!child->sym) { + printf("%*c %s\n", indent, '*', menu_get_prompt(child)); + continue; + } + cnt++; + if (child->sym == def_sym) { + def = cnt; + printf("%*c", indent, '>'); + } else + printf("%*c", indent, ' '); + printf(" %d. %s", cnt, menu_get_prompt(child)); + if (child->sym->name) + printf(" (%s)", child->sym->name); + if (!sym_has_value(child->sym)) + printf(" (NEW)"); + printf("\n"); + } + printf("%*schoice", indent - 1, ""); + if (cnt == 1) { + printf("[1]: 1\n"); + goto conf_childs; + } + printf("[1-%d", cnt); + if (sym->help) + printf("?"); + printf("]: "); + switch (input_mode) { + case ask_new: + case ask_silent: + if (!is_new) { + cnt = def; + printf("%d\n", cnt); + break; + } + check_stdin(); + case ask_all: + fflush(stdout); + fgets(line, 128, stdin); + strip(line); + if (line[0] == '?') { + printf("\n%s\n", menu->sym->help ? + menu->sym->help : nohelp_text); + continue; + } + if (!line[0]) + cnt = def; + else if (isdigit(line[0])) + cnt = atoi(line); + else + continue; + break; + case set_random: + def = (random() % cnt) + 1; + case set_default: + case set_yes: + case set_mod: + case set_no: + cnt = def; + printf("%d\n", cnt); + break; + } + + conf_childs: + for (child = menu->list; child; child = child->next) { + if (!child->sym || !menu_is_visible(child)) + continue; + if (!--cnt) + break; + } + if (!child) + continue; + if (line[strlen(line) - 1] == '?') { + printf("\n%s\n", child->sym->help ? + child->sym->help : nohelp_text); + continue; + } + sym_set_choice_value(sym, child->sym); + if (child->list) { + indent += 2; + conf(child->list); + indent -= 2; + } + return 1; + } +} + +static void conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (prop) { + const char *prompt; + + switch (prop->type) { + case P_MENU: + if (input_mode == ask_silent && rootEntry != menu) { + check_conf(menu); + return; + } + case P_COMMENT: + prompt = menu_get_prompt(menu); + if (prompt) + printf("%*c\n%*c %s\n%*c\n", + indent, '*', + indent, '*', prompt, + indent, '*'); + default: + ; + } + } + + if (!sym) + goto conf_childs; + + if (sym_is_choice(sym)) { + conf_choice(menu); + if (sym->curr.tri != mod) + return; + goto conf_childs; + } + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + conf_string(menu); + break; + default: + conf_sym(menu); + break; + } + +conf_childs: + if (sym) + indent += 2; + for (child = menu->list; child; child = child->next) + conf(child); + if (sym) + indent -= 2; +} + +static void check_conf(struct menu *menu) +{ + struct symbol *sym; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + if (sym) { + if (sym_is_changable(sym) && !sym_has_value(sym)) { + if (!conf_cnt++) + printf("*\n* Restart config...\n*\n"); + rootEntry = menu_get_parent_menu(menu); + conf(rootEntry); + } + if (sym_is_choice(sym) && sym_get_tristate_value(sym) != mod) + return; + } + + for (child = menu->list; child; child = child->next) + check_conf(child); +} + +int main(int ac, char **av) +{ + int i = 1; + const char *name; + struct stat tmpstat; + + if (ac > i && av[i][0] == '-') { + switch (av[i++][1]) { + case 'o': + input_mode = ask_new; + break; + case 's': + input_mode = ask_silent; + valid_stdin = isatty(0) && isatty(1) && isatty(2); + break; + case 'd': + input_mode = set_default; + break; + case 'D': + input_mode = set_default; + defconfig_file = av[i++]; + if (!defconfig_file) { + printf("%s: No default config file specified\n", + av[0]); + exit(1); + } + break; + case 'n': + input_mode = set_no; + break; + case 'm': + input_mode = set_mod; + break; + case 'y': + input_mode = set_yes; + break; + case 'r': + input_mode = set_random; + srandom(time(NULL)); + break; + case 'h': + case '?': + printf("%s [-o|-s] config\n", av[0]); + exit(0); + } + } + name = av[i]; + if (!name) { + printf("%s: configuration file missing\n", av[0]); + } + conf_parse(name); + //zconfdump(stdout); + switch (input_mode) { + case set_default: + if (!defconfig_file) + defconfig_file = conf_get_default_confname(); + if (conf_read(defconfig_file)) { + printf("***\n" + "*** Can't find default configuration \"%s\"!\n" + "***\n", defconfig_file); + exit(1); + } + break; + case ask_silent: + if (stat(".config", &tmpstat)) { + printf("***\n" + "*** You have not yet configured BusyBox!\n" + "***\n" + "*** Please run some configurator (e.g. \"make oldconfig\" or\n" + "*** \"make menuconfig\" or \"make config\").\n" + "***\n"); + exit(1); + } + case ask_all: + case ask_new: + conf_read(NULL); + break; + default: + break; + } + + if (input_mode != ask_silent) { + rootEntry = &rootmenu; + conf(&rootmenu); + if (input_mode == ask_all) { + input_mode = ask_silent; + valid_stdin = 1; + } + } + do { + conf_cnt = 0; + check_conf(&rootmenu); + } while (conf_cnt); + if (conf_write(NULL)) { + fprintf(stderr, "\n*** Error during writing of the BusyBox configuration.\n\n"); + return 1; + } + return 0; +} diff --git a/busybox/scripts/config/confdata.c b/busybox/scripts/config/confdata.c new file mode 100644 index 000000000..fd3a345e2 --- /dev/null +++ b/busybox/scripts/config/confdata.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +const char conf_def_filename[] = ".config"; + +const char conf_defname[] = "sysdeps/linux/defconfig"; + +const char *conf_confnames[] = { + ".config", + conf_defname, + NULL, +}; + +static char *conf_expand_value(const char *in) +{ + struct symbol *sym; + const char *src; + static char res_value[SYMBOL_MAXLENGTH]; + char *dst, name[SYMBOL_MAXLENGTH]; + + res_value[0] = 0; + dst = name; + while ((src = strchr(in, '$'))) { + strncat(res_value, in, src - in); + src++; + dst = name; + while (isalnum(*src) || *src == '_') + *dst++ = *src++; + *dst = 0; + sym = sym_lookup(name, 0); + sym_calc_value(sym); + strcat(res_value, sym_get_string_value(sym)); + in = src; + } + strcat(res_value, in); + + return res_value; +} + +char *conf_get_default_confname(void) +{ + struct stat buf; + static char fullname[PATH_MAX+1]; + char *env, *name; + + name = conf_expand_value(conf_defname); + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + if (!stat(fullname, &buf)) + return fullname; + } + return name; +} + +int conf_read(const char *name) +{ + FILE *in = NULL; + char line[1024]; + char *p, *p2; + int lineno = 0; + struct symbol *sym; + struct property *prop; + struct expr *e; + int i; + + if (name) { + in = zconf_fopen(name); + } else { + const char **names = conf_confnames; + while ((name = *names++)) { + name = conf_expand_value(name); + in = zconf_fopen(name); + if (in) { + printf("#\n" + "# using defaults found in %s\n" + "#\n", name); + break; + } + } + } + + if (!in) + return 1; + + for_all_symbols(i, sym) { + sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED; + sym->flags &= ~SYMBOL_VALID; + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + if (sym->user.val) + free(sym->user.val); + default: + sym->user.val = NULL; + sym->user.tri = no; + } + } + + while (fgets(line, sizeof(line), in)) { + lineno++; + sym = NULL; + switch (line[0]) { + case '#': + if (line[1]!=' ') + continue; + p = strchr(line + 2, ' '); + if (!p) + continue; + *p++ = 0; + if (strncmp(p, "is not set", 10)) + continue; + sym = sym_find(line + 2); + if (!sym) { + fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line + 2); + break; + } + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + sym->user.tri = no; + sym->flags &= ~SYMBOL_NEW; + break; + default: + ; + } + break; + + case 'A' ... 'Z': + p = strchr(line, '='); + if (!p) + continue; + *p++ = 0; + p2 = strchr(p, '\n'); + if (p2) + *p2 = 0; + sym = sym_find(line); + if (!sym) { + fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line); + break; + } + switch (sym->type) { + case S_TRISTATE: + if (p[0] == 'm') { + sym->user.tri = mod; + sym->flags &= ~SYMBOL_NEW; + break; + } + case S_BOOLEAN: + if (p[0] == 'y') { + sym->user.tri = yes; + sym->flags &= ~SYMBOL_NEW; + break; + } + if (p[0] == 'n') { + sym->user.tri = no; + sym->flags &= ~SYMBOL_NEW; + break; + } + break; + case S_STRING: + if (*p++ != '"') + break; + for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { + if (*p2 == '"') { + *p2 = 0; + break; + } + memmove(p2, p2 + 1, strlen(p2)); + } + if (!p2) { + fprintf(stderr, "%s:%d: invalid string found\n", name, lineno); + exit(1); + } + case S_INT: + case S_HEX: + if (sym_string_valid(sym, p)) { + sym->user.val = strdup(p); + sym->flags &= ~SYMBOL_NEW; + } else { + fprintf(stderr, "%s:%d: symbol value '%s' invalid for %s\n", name, lineno, p, sym->name); + exit(1); + } + break; + default: + ; + } + break; + case '\n': + break; + default: + continue; + } + if (sym && sym_is_choice_value(sym)) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + switch (sym->user.tri) { + case no: + break; + case mod: + if (cs->user.tri == yes) + /* warn? */; + break; + case yes: + if (cs->user.tri != no) + /* warn? */; + cs->user.val = sym; + break; + } + cs->user.tri = E_OR(cs->user.tri, sym->user.tri); + cs->flags &= ~SYMBOL_NEW; + } + } + fclose(in); + + if (modules_sym) + sym_calc_value(modules_sym); + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (sym_has_value(sym) && !sym_is_choice_value(sym)) { + if (sym->visible == no) + sym->flags |= SYMBOL_NEW; + switch (sym->type) { + case S_STRING: + case S_INT: + case S_HEX: + if (!sym_string_within_range(sym, sym->user.val)) + sym->flags |= SYMBOL_NEW; + default: + break; + } + } + if (!sym_is_choice(sym)) + continue; + prop = sym_get_choice_prop(sym); + for (e = prop->expr; e; e = e->left.expr) + if (e->right.sym->visible != no) + sym->flags |= e->right.sym->flags & SYMBOL_NEW; + } + + sym_change_count = 1; + + return 0; +} + +int conf_write(const char *name) +{ + FILE *out, *out_h; + struct symbol *sym; + struct menu *menu; + const char *basename; + char dirname[128], tmpname[128], newname[128]; + int type, l; + const char *str; + + dirname[0] = 0; + if (name && name[0]) { + struct stat st; + char *slash; + + if (!stat(name, &st) && S_ISDIR(st.st_mode)) { + strcpy(dirname, name); + strcat(dirname, "/"); + basename = conf_def_filename; + } else if ((slash = strrchr(name, '/'))) { + int size = slash - name + 1; + memcpy(dirname, name, size); + dirname[size] = 0; + if (slash[1]) + basename = slash + 1; + else + basename = conf_def_filename; + } else + basename = name; + } else + basename = conf_def_filename; + + sprintf(newname, "%s.tmpconfig.%d", dirname, getpid()); + out = fopen(newname, "w"); + if (!out) + return 1; + out_h = NULL; + if (!name) { + out_h = fopen(".tmpconfig.h", "w"); + if (!out_h) + return 1; + } + fprintf(out, "#\n" + "# Automatically generated make config: don't edit\n" + "#\n"); + if (out_h) { + fprintf(out_h, "/*\n" + " * Automatically generated header file: don't edit\n" + " */\n\n" + "#define AUTOCONF_INCLUDED\n\n" + "/* Version Number */\n" + "#define BB_VER \"%s\"\n" + "#define BB_BT \"%s\"\n", + getenv("VERSION"), + getenv("BUILDTIME")); + if (getenv("EXTRA_VERSION")) + fprintf(out_h, "#define BB_EXTRA_VERSION \"%s\"\n", + getenv("EXTRA_VERSION")); + fprintf(out_h, "\n"); + } + + if (!sym_change_count) + sym_clear_all_valid(); + + menu = rootmenu.list; + while (menu) { + sym = menu->sym; + if (!sym) { + if (!menu_is_visible(menu)) + goto next; + str = menu_get_prompt(menu); + fprintf(out, "\n" + "#\n" + "# %s\n" + "#\n", str); + if (out_h) + fprintf(out_h, "\n" + "/*\n" + " * %s\n" + " */\n", str); + } else if (!(sym->flags & SYMBOL_CHOICE)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + goto next; + sym->flags &= ~SYMBOL_WRITE; + type = sym->type; + if (type == S_TRISTATE) { + sym_calc_value(modules_sym); + if (modules_sym->curr.tri == no) + type = S_BOOLEAN; + } + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (sym_get_tristate_value(sym)) { + case no: + fprintf(out, "# %s is not set\n", sym->name); + if (out_h) + fprintf(out_h, "#undef %s\n", sym->name); + break; + case mod: +#if 0 + fprintf(out, "%s=m\n", sym->name); + if (out_h) + fprintf(out_h, "#define %s_MODULE 1\n", sym->name); +#endif + break; + case yes: + fprintf(out, "%s=y\n", sym->name); + if (out_h) + fprintf(out_h, "#define %s 1\n", sym->name); + break; + } + break; + case S_STRING: + // fix me + str = sym_get_string_value(sym); + fprintf(out, "%s=\"", sym->name); + if (out_h) + fprintf(out_h, "#define %s \"", sym->name); + do { + l = strcspn(str, "\"\\"); + if (l) { + fwrite(str, l, 1, out); + if (out_h) + fwrite(str, l, 1, out_h); + } + str += l; + while (*str == '\\' || *str == '"') { + fprintf(out, "\\%c", *str); + if (out_h) + fprintf(out_h, "\\%c", *str); + str++; + } + } while (*str); + fputs("\"\n", out); + if (out_h) + fputs("\"\n", out_h); + break; + case S_HEX: + str = sym_get_string_value(sym); + if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) { + fprintf(out, "%s=%s\n", sym->name, str); + if (out_h) + fprintf(out_h, "#define %s 0x%s\n", sym->name, str); + break; + } + case S_INT: + str = sym_get_string_value(sym); + fprintf(out, "%s=%s\n", sym->name, str); + if (out_h) + fprintf(out_h, "#define %s %s\n", sym->name, str); + break; + } + } + + next: + if (menu->list) { + menu = menu->list; + continue; + } + if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->next) { + menu = menu->next; + break; + } + } + } + fclose(out); + if (out_h) { + fclose(out_h); + rename(".tmpconfig.h", "include/config.h"); + file_write_dep(NULL); + } + if (!name || basename != conf_def_filename) { + if (!name) + name = conf_def_filename; + sprintf(tmpname, "%s.old", name); + rename(name, tmpname); + } + sprintf(tmpname, "%s%s", dirname, basename); + if (rename(newname, tmpname)) + return 1; + + sym_change_count = 0; + + return 0; +} diff --git a/busybox/scripts/config/dialog.h b/busybox/scripts/config/dialog.h new file mode 100644 index 000000000..6486cc8f7 --- /dev/null +++ b/busybox/scripts/config/dialog.h @@ -0,0 +1,196 @@ + +/* + * dialog.h -- common declarations for all dialog modules + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CURSES_LOC +#include CURSES_LOC + +/* + * Colors in ncurses 1.9.9e do not work properly since foreground and + * background colors are OR'd rather than separately masked. This version + * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible + * with standard curses. The simplest fix (to make this work with standard + * curses) uses the wbkgdset() function, not used in the original hack. + * Turn it off if we're building with 1.9.9e, since it just confuses things. + */ +#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE) +#define OLD_NCURSES 1 +#undef wbkgdset +#define wbkgdset(w,p) /*nothing*/ +#else +#define OLD_NCURSES 0 +#endif + +#define TR(params) _tracef params + +#define ESC 27 +#define TAB 9 +#define MAX_LEN 2048 +#define BUF_SIZE (10*1024) +#define MIN(x,y) (x < y ? x : y) +#define MAX(x,y) (x > y ? x : y) + + +#ifndef ACS_ULCORNER +#define ACS_ULCORNER '+' +#endif +#ifndef ACS_LLCORNER +#define ACS_LLCORNER '+' +#endif +#ifndef ACS_URCORNER +#define ACS_URCORNER '+' +#endif +#ifndef ACS_LRCORNER +#define ACS_LRCORNER '+' +#endif +#ifndef ACS_HLINE +#define ACS_HLINE '-' +#endif +#ifndef ACS_VLINE +#define ACS_VLINE '|' +#endif +#ifndef ACS_LTEE +#define ACS_LTEE '+' +#endif +#ifndef ACS_RTEE +#define ACS_RTEE '+' +#endif +#ifndef ACS_UARROW +#define ACS_UARROW '^' +#endif +#ifndef ACS_DARROW +#define ACS_DARROW 'v' +#endif + +/* + * Attribute names + */ +#define screen_attr attributes[0] +#define shadow_attr attributes[1] +#define dialog_attr attributes[2] +#define title_attr attributes[3] +#define border_attr attributes[4] +#define button_active_attr attributes[5] +#define button_inactive_attr attributes[6] +#define button_key_active_attr attributes[7] +#define button_key_inactive_attr attributes[8] +#define button_label_active_attr attributes[9] +#define button_label_inactive_attr attributes[10] +#define inputbox_attr attributes[11] +#define inputbox_border_attr attributes[12] +#define searchbox_attr attributes[13] +#define searchbox_title_attr attributes[14] +#define searchbox_border_attr attributes[15] +#define position_indicator_attr attributes[16] +#define menubox_attr attributes[17] +#define menubox_border_attr attributes[18] +#define item_attr attributes[19] +#define item_selected_attr attributes[20] +#define tag_attr attributes[21] +#define tag_selected_attr attributes[22] +#define tag_key_attr attributes[23] +#define tag_key_selected_attr attributes[24] +#define check_attr attributes[25] +#define check_selected_attr attributes[26] +#define uarrow_attr attributes[27] +#define darrow_attr attributes[28] + +/* number of attributes */ +#define ATTRIBUTE_COUNT 29 + +/* + * Global variables + */ +extern bool use_colors; + +extern chtype attributes[]; +#endif + +extern char *backtitle; + +struct dialog_list_item { + char *name; + int namelen; + char *tag; + int selected; /* Set to 1 by dialog_*() function. */ +}; + +/* + * Function prototypes + */ + +void init_dialog (void); +void end_dialog (void); +void dialog_clear (void); +#ifdef CURSES_LOC +void attr_clear (WINDOW * win, int height, int width, chtype attr); +void color_setup (void); +void print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x); +void print_button (WINDOW * win, const char *label, int y, int x, int selected); +void draw_box (WINDOW * win, int y, int x, int height, int width, chtype box, + chtype border); +void draw_shadow (WINDOW * win, int y, int x, int height, int width); +#endif + +int first_alpha (const char *string, const char *exempt); +int dialog_yesno (const char *title, const char *prompt, int height, int width); +int dialog_msgbox (const char *title, const char *prompt, int height, + int width, int pause); +int dialog_textbox (const char *title, const char *file, int height, int width); +int dialog_menu (const char *title, const char *prompt, int height, int width, + int menu_height, const char *choice, int item_no, + struct dialog_list_item ** items); +int dialog_checklist (const char *title, const char *prompt, int height, + int width, int list_height, int item_no, + struct dialog_list_item ** items, int flag); +extern unsigned char dialog_input_result[]; +int dialog_inputbox (const char *title, const char *prompt, int height, + int width, const char *init); + +struct dialog_list_item *first_sel_item(int item_no, + struct dialog_list_item ** items); + +/* + * This is the base for fictitious keys, which activate + * the buttons. + * + * Mouse-generated keys are the following: + * -- the first 32 are used as numbers, in addition to '0'-'9' + * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o') + * -- uppercase chars are used to invoke the button (M_EVENT + 'O') + */ +#ifdef CURSES_LOC +#define M_EVENT (KEY_MAX+1) +#endif + + +/* + * The `flag' parameter in checklist is used to select between + * radiolist and checklist + */ +#define FLAG_CHECK 1 +#define FLAG_RADIO 0 diff --git a/busybox/scripts/config/expr.c b/busybox/scripts/config/expr.c new file mode 100644 index 000000000..10f45232b --- /dev/null +++ b/busybox/scripts/config/expr.c @@ -0,0 +1,1089 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define DEBUG_EXPR 0 + +struct expr *expr_alloc_symbol(struct symbol *sym) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = E_SYMBOL; + e->left.sym = sym; + return e; +} + +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.expr = ce; + return e; +} + +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.expr = e1; + e->right.expr = e2; + return e; +} + +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.sym = s1; + e->right.sym = s2; + return e; +} + +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_AND, e1, e2) : e1; +} + +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_OR, e1, e2) : e1; +} + +struct expr *expr_copy(struct expr *org) +{ + struct expr *e; + + if (!org) + return NULL; + + e = malloc(sizeof(*org)); + memcpy(e, org, sizeof(*org)); + switch (org->type) { + case E_SYMBOL: + e->left = org->left; + break; + case E_NOT: + e->left.expr = expr_copy(org->left.expr); + break; + case E_EQUAL: + case E_UNEQUAL: + e->left.sym = org->left.sym; + e->right.sym = org->right.sym; + break; + case E_AND: + case E_OR: + case E_CHOICE: + e->left.expr = expr_copy(org->left.expr); + e->right.expr = expr_copy(org->right.expr); + break; + default: + printf("can't copy type %d\n", e->type); + free(e); + e = NULL; + break; + } + + return e; +} + +void expr_free(struct expr *e) +{ + if (!e) + return; + + switch (e->type) { + case E_SYMBOL: + break; + case E_NOT: + expr_free(e->left.expr); + return; + case E_EQUAL: + case E_UNEQUAL: + break; + case E_OR: + case E_AND: + expr_free(e->left.expr); + expr_free(e->right.expr); + break; + default: + printf("how to free type %d?\n", e->type); + break; + } + free(e); +} + +static int trans_count; + +#define e1 (*ep1) +#define e2 (*ep2) + +static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ + if (e1->type == type) { + __expr_eliminate_eq(type, &e1->left.expr, &e2); + __expr_eliminate_eq(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + __expr_eliminate_eq(type, &e1, &e2->left.expr); + __expr_eliminate_eq(type, &e1, &e2->right.expr); + return; + } + if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO))) + return; + if (!expr_eq(e1, e2)) + return; + trans_count++; + expr_free(e1); expr_free(e2); + switch (type) { + case E_OR: + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + break; + case E_AND: + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + break; + default: + ; + } +} + +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) +{ + if (!e1 || !e2) + return; + switch (e1->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e1->type, ep1, ep2); + default: + ; + } + if (e1->type != e2->type) switch (e2->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e2->type, ep1, ep2); + default: + ; + } + e1 = expr_eliminate_yn(e1); + e2 = expr_eliminate_yn(e2); +} + +#undef e1 +#undef e2 + +int expr_eq(struct expr *e1, struct expr *e2) +{ + int res, old_count; + + if (e1->type != e2->type) + return 0; + switch (e1->type) { + case E_EQUAL: + case E_UNEQUAL: + return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; + case E_SYMBOL: + return e1->left.sym == e2->left.sym; + case E_NOT: + return expr_eq(e1->left.expr, e2->left.expr); + case E_AND: + case E_OR: + e1 = expr_copy(e1); + e2 = expr_copy(e2); + old_count = trans_count; + expr_eliminate_eq(&e1, &e2); + res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym); + expr_free(e1); + expr_free(e2); + trans_count = old_count; + return res; + case E_CHOICE: + case E_RANGE: + case E_NONE: + /* panic */; + } + + if (DEBUG_EXPR) { + expr_fprint(e1, stdout); + printf(" = "); + expr_fprint(e2, stdout); + printf(" ?\n"); + } + + return 0; +} + +struct expr *expr_eliminate_yn(struct expr *e) +{ + struct expr *tmp; + + if (e) switch (e->type) { + case E_AND: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } + } + break; + case E_OR: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + break; + default: + ; + } + return e; +} + +/* + * bool FOO!=n => FOO + */ +struct expr *expr_trans_bool(struct expr *e) +{ + if (!e) + return NULL; + switch (e->type) { + case E_AND: + case E_OR: + case E_NOT: + e->left.expr = expr_trans_bool(e->left.expr); + e->right.expr = expr_trans_bool(e->right.expr); + break; + case E_UNEQUAL: + // FOO!=n -> FOO + if (e->left.sym->type == S_TRISTATE) { + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + } + } + break; + default: + ; + } + return e; +} + +/* + * e1 || e2 -> ? + */ +struct expr *expr_join_or(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='m') -> (a!='n') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='n') -> (a!='m') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { + // (a='m') || (a='n') -> (a!='y') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); + } + } + if (sym1->type == S_BOOLEAN && sym1 == sym2) { + if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || + (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) + return expr_alloc_symbol(&symbol_yes); + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") || ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +struct expr *expr_join_and(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) + // (a) && (a='y') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) + // (a) && (a!='n') -> (a) + return expr_alloc_symbol(sym1); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) + // (a) && (a!='m') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e1->right.sym; + if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e2->right.sym; + if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='m') -> (a='n') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) + // (a!='m') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || + (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) + return NULL; + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") && ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp; + + if (e1->type == type) { + expr_eliminate_dups1(type, &e1->left.expr, &e2); + expr_eliminate_dups1(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups1(type, &e1, &e2->left.expr); + expr_eliminate_dups1(type, &e1, &e2->right.expr); + return; + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e1->type, &e1, &e1); + default: + ; + } + + switch (type) { + case E_OR: + tmp = expr_join_or(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_no); + e2 = tmp; + trans_count++; + } + break; + case E_AND: + tmp = expr_join_and(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_yes); + e2 = tmp; + trans_count++; + } + break; + default: + ; + } +#undef e1 +#undef e2 +} + +static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp, *tmp1, *tmp2; + + if (e1->type == type) { + expr_eliminate_dups2(type, &e1->left.expr, &e2); + expr_eliminate_dups2(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups2(type, &e1, &e2->left.expr); + expr_eliminate_dups2(type, &e1, &e2->right.expr); + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO || BAR) && (!FOO && !BAR) -> n + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_and(&tmp1, &tmp2); + if (expr_is_yes(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_no); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + case E_AND: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO && BAR) || (!FOO || !BAR) -> y + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_or(&tmp1, &tmp2); + if (expr_is_no(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_yes); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + default: + ; + } +#undef e1 +#undef e2 +} + +struct expr *expr_eliminate_dups(struct expr *e) +{ + int oldcount; + if (!e) + return e; + + oldcount = trans_count; + while (1) { + trans_count = 0; + switch (e->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e->type, &e, &e); + expr_eliminate_dups2(e->type, &e, &e); + default: + ; + } + if (!trans_count) + break; + e = expr_eliminate_yn(e); + } + trans_count = oldcount; + return e; +} + +struct expr *expr_transform(struct expr *e) +{ + struct expr *tmp; + + if (!e) + return NULL; + switch (e->type) { + case E_EQUAL: + case E_UNEQUAL: + case E_SYMBOL: + case E_CHOICE: + break; + default: + e->left.expr = expr_transform(e->left.expr); + e->right.expr = expr_transform(e->right.expr); + } + + switch (e->type) { + case E_EQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + break; + case E_UNEQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + break; + case E_NOT: + switch (e->left.expr->type) { + case E_NOT: + // !!a -> a + tmp = e->left.expr->left.expr; + free(e->left.expr); + free(e); + e = tmp; + e = expr_transform(e); + break; + case E_EQUAL: + case E_UNEQUAL: + // !a='x' -> a!='x' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; + break; + case E_OR: + // !(a || b) -> !a && !b + tmp = e->left.expr; + e->type = E_AND; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_AND: + // !(a && b) -> !a || !b + tmp = e->left.expr; + e->type = E_OR; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_SYMBOL: + if (e->left.expr->left.sym == &symbol_yes) { + // !'y' -> 'n' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + break; + } + if (e->left.expr->left.sym == &symbol_mod) { + // !'m' -> 'm' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_mod; + break; + } + if (e->left.expr->left.sym == &symbol_no) { + // !'n' -> 'y' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + break; + } + break; + default: + ; + } + break; + default: + ; + } + return e; +} + +int expr_contains_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return 0; + + switch (dep->type) { + case E_AND: + case E_OR: + return expr_contains_symbol(dep->left.expr, sym) || + expr_contains_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + case E_UNEQUAL: + return dep->left.sym == sym || + dep->right.sym == sym; + case E_NOT: + return expr_contains_symbol(dep->left.expr, sym); + default: + ; + } + return 0; +} + +bool expr_depends_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return false; + + switch (dep->type) { + case E_AND: + return expr_depends_symbol(dep->left.expr, sym) || + expr_depends_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod) + return true; + } + break; + case E_UNEQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_no) + return true; + } + break; + default: + ; + } + return false; +} + +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_AND, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_OR, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + if (e1->type == type) { + expr_extract_eq(type, ep, &e1->left.expr, &e2); + expr_extract_eq(type, ep, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_extract_eq(type, ep, ep1, &e2->left.expr); + expr_extract_eq(type, ep, ep1, &e2->right.expr); + return; + } + if (expr_eq(e1, e2)) { + *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1; + expr_free(e2); + if (type == E_AND) { + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + } else if (type == E_OR) { + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + } + } +#undef e1 +#undef e2 +} + +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym) +{ + struct expr *e1, *e2; + + if (!e) { + e = expr_alloc_symbol(sym); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + } + switch (e->type) { + case E_AND: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_AND, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_OR, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_OR: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_OR, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_AND, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_NOT: + return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); + case E_UNEQUAL: + case E_EQUAL: + if (type == E_EQUAL) { + if (sym == &symbol_yes) + return expr_copy(e); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_no); + if (sym == &symbol_no) + return expr_alloc_one(E_NOT, expr_copy(e)); + } else { + if (sym == &symbol_yes) + return expr_alloc_one(E_NOT, expr_copy(e)); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_yes); + if (sym == &symbol_no) + return expr_copy(e); + } + break; + case E_SYMBOL: + return expr_alloc_comp(type, e->left.sym, sym); + case E_CHOICE: + case E_RANGE: + case E_NONE: + /* panic */; + } + return NULL; +} + +tristate expr_calc_value(struct expr *e) +{ + tristate val1, val2; + const char *str1, *str2; + + if (!e) + return yes; + + switch (e->type) { + case E_SYMBOL: + sym_calc_value(e->left.sym); + return e->left.sym->curr.tri; + case E_AND: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return E_AND(val1, val2); + case E_OR: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return E_OR(val1, val2); + case E_NOT: + val1 = expr_calc_value(e->left.expr); + return E_NOT(val1); + case E_EQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? yes : no; + case E_UNEQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? no : yes; + default: + printf("expr_calc_value: %d?\n", e->type); + return no; + } +} + +int expr_compare_type(enum expr_type t1, enum expr_type t2) +{ +#if 0 + return 1; +#else + if (t1 == t2) + return 0; + switch (t1) { + case E_EQUAL: + case E_UNEQUAL: + if (t2 == E_NOT) + return 1; + case E_NOT: + if (t2 == E_AND) + return 1; + case E_AND: + if (t2 == E_OR) + return 1; + case E_OR: + if (t2 == E_CHOICE) + return 1; + case E_CHOICE: + if (t2 == 0) + return 1; + default: + return -1; + } + printf("[%dgt%d?]", t1, t2); + return 0; +#endif +} + +void expr_print(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken) +{ + if (!e) { + fn(data, "y"); + return; + } + + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, "("); + switch (e->type) { + case E_SYMBOL: + if (e->left.sym->name) + fn(data, e->left.sym->name); + else + fn(data, ""); + break; + case E_NOT: + fn(data, "!"); + expr_print(e->left.expr, fn, data, E_NOT); + break; + case E_EQUAL: + fn(data, e->left.sym->name); + fn(data, "="); + fn(data, e->right.sym->name); + break; + case E_UNEQUAL: + fn(data, e->left.sym->name); + fn(data, "!="); + fn(data, e->right.sym->name); + break; + case E_OR: + expr_print(e->left.expr, fn, data, E_OR); + fn(data, " || "); + expr_print(e->right.expr, fn, data, E_OR); + break; + case E_AND: + expr_print(e->left.expr, fn, data, E_AND); + fn(data, " && "); + expr_print(e->right.expr, fn, data, E_AND); + break; + case E_CHOICE: + fn(data, e->right.sym->name); + if (e->left.expr) { + fn(data, " ^ "); + expr_print(e->left.expr, fn, data, E_CHOICE); + } + break; + case E_RANGE: + fn(data, "["); + fn(data, e->left.sym->name); + fn(data, " "); + fn(data, e->right.sym->name); + fn(data, "]"); + break; + default: + { + char buf[32]; + sprintf(buf, "", e->type); + fn(data, buf); + break; + } + } + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, ")"); +} + +static void expr_print_file_helper(void *data, const char *str) +{ + fwrite(str, strlen(str), 1, data); +} + +void expr_fprint(struct expr *e, FILE *out) +{ + expr_print(e, expr_print_file_helper, out, E_NONE); +} diff --git a/busybox/scripts/config/expr.h b/busybox/scripts/config/expr.h new file mode 100644 index 000000000..cac51f6a8 --- /dev/null +++ b/busybox/scripts/config/expr.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef EXPR_H +#define EXPR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifndef __cplusplus +#include +#endif + +struct file { + struct file *next; + struct file *parent; + char *name; + int lineno; + int flags; +}; + +#define FILE_BUSY 0x0001 +#define FILE_SCANNED 0x0002 +#define FILE_PRINTED 0x0004 + +typedef enum tristate { + no, mod, yes +} tristate; + +enum expr_type { + E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_CHOICE, E_SYMBOL, E_RANGE +}; + +union expr_data { + struct expr *expr; + struct symbol *sym; +}; + +struct expr { + enum expr_type type; + union expr_data left, right; +}; + +#define E_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2)) +#define E_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) +#define E_NOT(dep) (2-(dep)) + +struct expr_value { + struct expr *expr; + tristate tri; +}; + +struct symbol_value { + void *val; + tristate tri; +}; + +enum symbol_type { + S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER +}; + +struct symbol { + struct symbol *next; + char *name; + char *help; + enum symbol_type type; + struct symbol_value curr, user; + tristate visible; + int flags; + struct property *prop; + struct expr *dep, *dep2; + struct expr_value rev_dep; +}; + +#define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER) + +#define SYMBOL_YES 0x0001 +#define SYMBOL_MOD 0x0002 +#define SYMBOL_NO 0x0004 +#define SYMBOL_CONST 0x0007 +#define SYMBOL_CHECK 0x0008 +#define SYMBOL_CHOICE 0x0010 +#define SYMBOL_CHOICEVAL 0x0020 +#define SYMBOL_PRINTED 0x0040 +#define SYMBOL_VALID 0x0080 +#define SYMBOL_OPTIONAL 0x0100 +#define SYMBOL_WRITE 0x0200 +#define SYMBOL_CHANGED 0x0400 +#define SYMBOL_NEW 0x0800 +#define SYMBOL_AUTO 0x1000 +#define SYMBOL_CHECKED 0x2000 +#define SYMBOL_CHECK_DONE 0x4000 +#define SYMBOL_WARNED 0x8000 + +#define SYMBOL_MAXLENGTH 256 +#define SYMBOL_HASHSIZE 257 +#define SYMBOL_HASHMASK 0xff + +enum prop_type { + P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, P_SELECT, P_RANGE +}; + +struct property { + struct property *next; + struct symbol *sym; + enum prop_type type; + const char *text; + struct expr_value visible; + struct expr *expr; + struct menu *menu; + struct file *file; + int lineno; +}; + +#define for_all_properties(sym, st, tok) \ + for (st = sym->prop; st; st = st->next) \ + if (st->type == (tok)) +#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) +#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) +#define for_all_prompts(sym, st) \ + for (st = sym->prop; st; st = st->next) \ + if (st->text) + +struct menu { + struct menu *next; + struct menu *parent; + struct menu *list; + struct symbol *sym; + struct property *prompt; + struct expr *dep; + unsigned int flags; + //char *help; + struct file *file; + int lineno; + void *data; +}; + +#define MENU_CHANGED 0x0001 +#define MENU_ROOT 0x0002 + +#ifndef SWIG + +extern struct file *file_list; +extern struct file *current_file; +struct file *lookup_file(const char *name); + +extern struct symbol symbol_yes, symbol_no, symbol_mod; +extern struct symbol *modules_sym; +extern int cdebug; +struct expr *expr_alloc_symbol(struct symbol *sym); +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2); +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2); +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); +struct expr *expr_copy(struct expr *org); +void expr_free(struct expr *e); +int expr_eq(struct expr *e1, struct expr *e2); +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); +tristate expr_calc_value(struct expr *e); +struct expr *expr_eliminate_yn(struct expr *e); +struct expr *expr_trans_bool(struct expr *e); +struct expr *expr_eliminate_dups(struct expr *e); +struct expr *expr_transform(struct expr *e); +int expr_contains_symbol(struct expr *dep, struct symbol *sym); +bool expr_depends_symbol(struct expr *dep, struct symbol *sym); +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2); +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2); +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2); +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); + +void expr_fprint(struct expr *e, FILE *out); + +static inline int expr_is_yes(struct expr *e) +{ + return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); +} + +static inline int expr_is_no(struct expr *e) +{ + return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no); +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EXPR_H */ diff --git a/busybox/scripts/config/inputbox.c b/busybox/scripts/config/inputbox.c new file mode 100644 index 000000000..fa7bebc69 --- /dev/null +++ b/busybox/scripts/config/inputbox.c @@ -0,0 +1,240 @@ +/* + * inputbox.c -- implements the input box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +unsigned char dialog_input_result[MAX_LEN + 1]; + +/* + * Print the termination buttons + */ +static void +print_buttons(WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button (dialog, " Ok ", y, x, selected==0); + print_button (dialog, " Help ", y, x + 14, selected==1); + + wmove(dialog, y, x+1+14*selected); + wrefresh(dialog); +} + +/* + * Display a dialog box for inputing a string + */ +int +dialog_inputbox (const char *title, const char *prompt, int height, int width, + const char *init) +{ + int i, x, y, box_y, box_x, box_width; + int input_x = 0, scroll = 0, key = 0, button = -1; + unsigned char *instr = dialog_input_result; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + /* Draw the input field box */ + box_width = width - 6; + getyx (dialog, y, x); + box_y = y + 2; + box_x = (width - box_width) / 2; + draw_box (dialog, y + 1, box_x - 1, 3, box_width + 2, + border_attr, dialog_attr); + + print_buttons(dialog, height, width, 0); + + /* Set up the initial value */ + wmove (dialog, box_y, box_x); + wattrset (dialog, inputbox_attr); + + if (!init) + instr[0] = '\0'; + else + strcpy (instr, init); + + input_x = strlen (instr); + + if (input_x >= box_width) { + scroll = input_x - box_width + 1; + input_x = box_width - 1; + for (i = 0; i < box_width - 1; i++) + waddch (dialog, instr[scroll + i]); + } else + waddstr (dialog, instr); + + wmove (dialog, box_y, box_x + input_x); + + wrefresh (dialog); + + while (key != ESC) { + key = wgetch (dialog); + + if (button == -1) { /* Input box selected */ + switch (key) { + case TAB: + case KEY_UP: + case KEY_DOWN: + break; + case KEY_LEFT: + continue; + case KEY_RIGHT: + continue; + case KEY_BACKSPACE: + case 127: + if (input_x || scroll) { + wattrset (dialog, inputbox_attr); + if (!input_x) { + scroll = scroll < box_width - 1 ? + 0 : scroll - (box_width - 1); + wmove (dialog, box_y, box_x); + for (i = 0; i < box_width; i++) + waddch (dialog, instr[scroll + input_x + i] ? + instr[scroll + input_x + i] : ' '); + input_x = strlen (instr) - scroll; + } else + input_x--; + instr[scroll + input_x] = '\0'; + mvwaddch (dialog, box_y, input_x + box_x, ' '); + wmove (dialog, box_y, input_x + box_x); + wrefresh (dialog); + } + continue; + default: + if (key < 0x100 && isprint (key)) { + if (scroll + input_x < MAX_LEN) { + wattrset (dialog, inputbox_attr); + instr[scroll + input_x] = key; + instr[scroll + input_x + 1] = '\0'; + if (input_x == box_width - 1) { + scroll++; + wmove (dialog, box_y, box_x); + for (i = 0; i < box_width - 1; i++) + waddch (dialog, instr[scroll + i]); + } else { + wmove (dialog, box_y, input_x++ + box_x); + waddch (dialog, key); + } + wrefresh (dialog); + } else + flash (); /* Alarm user about overflow */ + continue; + } + } + } + switch (key) { + case 'O': + case 'o': + delwin (dialog); + return 0; + case 'H': + case 'h': + delwin (dialog); + return 1; + case KEY_UP: + case KEY_LEFT: + switch (button) { + case -1: + button = 1; /* Indicates "Cancel" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 0: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove (dialog, box_y, box_x + input_x); + wrefresh (dialog); + break; + case 1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + } + break; + case TAB: + case KEY_DOWN: + case KEY_RIGHT: + switch (button) { + case -1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + case 0: + button = 1; /* Indicates "Cancel" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 1: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove (dialog, box_y, box_x + input_x); + wrefresh (dialog); + break; + } + break; + case ' ': + case '\n': + delwin (dialog); + return (button == -1 ? 0 : button); + case 'X': + case 'x': + key = ESC; + case ESC: + break; + } + } + + delwin (dialog); + return -1; /* ESC pressed */ +} diff --git a/busybox/scripts/config/lex.zconf.c_shipped b/busybox/scripts/config/lex.zconf.c_shipped new file mode 100644 index 000000000..b877bb6b3 --- /dev/null +++ b/busybox/scripts/config/lex.zconf.c_shipped @@ -0,0 +1,3688 @@ + +#line 3 "lex.zconf.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 31 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE zconfrestart(zconfin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int zconfleng; + +extern FILE *zconfin, *zconfout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up zconftext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via zconfrestart()), so that the user can continue scanning by + * just pointing zconfin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when zconftext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int zconfleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow zconfwrap()'s to do buffer switches + * instead of setting up a fresh zconfin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void zconfrestart (FILE *input_file ); +void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size ); +void zconf_delete_buffer (YY_BUFFER_STATE b ); +void zconf_flush_buffer (YY_BUFFER_STATE b ); +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void zconfpop_buffer_state (void ); + +static void zconfensure_buffer_stack (void ); +static void zconf_load_buffer_state (void ); +static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len ); + +void *zconfalloc (yy_size_t ); +void *zconfrealloc (void *,yy_size_t ); +void zconffree (void * ); + +#define yy_new_buffer zconf_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define zconfwrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0; + +typedef int yy_state_type; + +extern int zconflineno; + +int zconflineno = 1; + +extern char *zconftext; +#define yytext_ptr zconftext +static yyconst flex_int16_t yy_nxt[][38] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 18, 18, 19, 20, + 21, 22, 18, 18, 23, 24, 18, 25, 18, 26, + 27, 18, 28, 29, 30, 18, 18, 16 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 18, 18, 19, 20, + 21, 22, 18, 18, 23, 24, 18, 25, 18, 26, + 27, 18, 28, 29, 30, 18, 18, 16 + + }, + + { + 11, 31, 32, 33, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31 + }, + + { + 11, 31, 32, 33, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31 + }, + + { + 11, 34, 34, 35, 34, 36, 34, 34, 36, 34, + 34, 34, 34, 34, 34, 37, 34, 34, 34, 34, + + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34 + }, + + { + 11, 34, 34, 35, 34, 36, 34, 34, 36, 34, + 34, 34, 34, 34, 34, 37, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34 + }, + + { + 11, 38, 38, 39, 40, 41, 42, 43, 41, 44, + 45, 46, 47, 47, 48, 49, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 50, 47, 47, 47, 51, + 47, 47, 47, 47, 47, 47, 47, 52 + + }, + + { + 11, 38, 38, 39, 40, 41, 42, 43, 41, 44, + 45, 46, 47, 47, 48, 49, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 50, 47, 47, 47, 51, + 47, 47, 47, 47, 47, 47, 47, 52 + }, + + { + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11 + }, + + { + 11, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12 + }, + + { + 11, -13, 53, 54, -13, -13, 55, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13, -13 + }, + + { + 11, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14 + + }, + + { + 11, 56, 56, 57, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56 + }, + + { + 11, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16 + }, + + { + 11, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, + + -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17 + }, + + { + 11, -18, -18, -18, -18, -18, -18, -18, -18, -18, + -18, -18, -18, 58, -18, -18, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -18 + }, + + { + 11, -19, -19, -19, -19, -19, -19, -19, -19, -19, + -19, -19, -19, 58, -19, -19, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, + 58, 58, 58, 58, 58, 58, 58, -19 + + }, + + { + 11, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20, 58, -20, -20, 58, 58, 58, 58, + 58, 58, 58, 58, 60, 58, 58, 58, 58, 61, + 58, 58, 58, 58, 58, 58, 58, -20 + }, + + { + 11, -21, -21, -21, -21, -21, -21, -21, -21, -21, + -21, -21, -21, 58, -21, -21, 58, 58, 58, 58, + 58, 62, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -21 + }, + + { + 11, -22, -22, -22, -22, -22, -22, -22, -22, -22, + -22, -22, -22, 58, -22, -22, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 63, 58, + 58, 58, 58, 58, 58, 58, 58, -22 + }, + + { + 11, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, 58, -23, -23, 58, 58, 58, 58, + 58, 64, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -23 + }, + + { + 11, -24, -24, -24, -24, -24, -24, -24, -24, -24, + -24, -24, -24, 58, -24, -24, 58, 58, 58, 58, + 58, 58, 65, 58, 58, 58, 58, 58, 66, 58, + 58, 58, 58, 58, 58, 58, 58, -24 + + }, + + { + 11, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, 58, -25, -25, 58, 67, 58, 58, + 58, 68, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -25 + }, + + { + 11, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, 58, -26, -26, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 69, 58, 58, 58, 58, 58, 58, -26 + }, + + { + 11, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, 58, -27, -27, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 70, 58, 58, 58, 58, -27 + }, + + { + 11, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, 58, -28, -28, 58, 71, 58, 58, + 58, 72, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -28 + }, + + { + 11, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, 58, -29, -29, 58, 58, 58, 58, + 58, 73, 58, 58, 58, 58, 58, 58, 58, 74, + 58, 58, 58, 58, 75, 58, 58, -29 + + }, + + { + 11, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, 58, -30, -30, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 76, 58, 58, 58, 58, -30 + }, + + { + 11, 77, 77, -31, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77 + }, + + { + 11, -32, 78, 79, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32 + }, + + { + 11, 80, -33, -33, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80 + }, + + { + 11, 81, 81, 82, 81, -34, 81, 81, -34, 81, + 81, 81, 81, 81, 81, -34, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81 + + }, + + { + 11, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35 + }, + + { + 11, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36 + }, + + { + 11, 83, 83, 84, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83 + }, + + { + 11, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38 + }, + + { + 11, -39, -39, -39, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39, -39 + + }, + + { + 11, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, 85, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40 + }, + + { + 11, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41 + }, + + { + 11, 86, 86, -42, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86 + }, + + { + 11, -43, -43, -43, -43, -43, -43, 87, -43, -43, + -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, + -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, + -43, -43, -43, -43, -43, -43, -43, -43 + }, + + { + 11, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44 + + }, + + { + 11, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45 + }, + + { + 11, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, 88, 89, 89, -46, -46, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -46 + }, + + { + 11, -47, -47, -47, -47, -47, -47, -47, -47, -47, + -47, 89, 89, 89, -47, -47, 89, 89, 89, 89, + + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -47 + }, + + { + 11, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48 + }, + + { + 11, -49, -49, 90, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49 + + }, + + { + 11, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, 89, 89, 89, -50, -50, 89, 89, 89, 89, + 89, 89, 91, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -50 + }, + + { + 11, -51, -51, -51, -51, -51, -51, -51, -51, -51, + -51, 89, 89, 89, -51, -51, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 92, 89, + 89, 89, 89, 89, 89, 89, 89, -51 + }, + + { + 11, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, + + -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52, 93 + }, + + { + 11, -53, 53, 54, -53, -53, 55, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53 + }, + + { + 11, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54 + + }, + + { + 11, 56, 56, 57, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56 + }, + + { + 11, 56, 56, 57, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56 + }, + + { + 11, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57 + }, + + { + 11, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, 58, -58, -58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -58 + }, + + { + 11, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, 58, -59, -59, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 94, + 58, 58, 58, 58, 58, 58, 58, -59 + + }, + + { + 11, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, 58, -60, -60, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 95, + 58, 58, 58, 58, 58, 58, 58, -60 + }, + + { + 11, -61, -61, -61, -61, -61, -61, -61, -61, -61, + -61, -61, -61, 58, -61, -61, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 96, 97, 58, + 58, 58, 58, 58, 58, 58, 58, -61 + }, + + { + 11, -62, -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -62, 58, -62, -62, 58, 58, 58, 58, + + 58, 58, 98, 58, 58, 58, 58, 58, 58, 58, + 99, 58, 58, 58, 58, 58, 58, -62 + }, + + { + 11, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, 58, -63, -63, 58, 100, 58, 58, + 101, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -63 + }, + + { + 11, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, 58, -64, -64, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 102, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 103, -64 + + }, + + { + 11, -65, -65, -65, -65, -65, -65, -65, -65, -65, + -65, -65, -65, 58, -65, -65, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -65 + }, + + { + 11, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, 58, -66, -66, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 104, 58, 58, -66 + }, + + { + 11, -67, -67, -67, -67, -67, -67, -67, -67, -67, + -67, -67, -67, 58, -67, -67, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 105, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -67 + }, + + { + 11, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, 58, -68, -68, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 106, 58, + 58, 58, 58, 58, 58, 58, 58, -68 + }, + + { + 11, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, 58, -69, -69, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 107, 58, 58, -69 + + }, + + { + 11, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, 58, -70, -70, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 108, + 58, 58, 58, 58, 58, 58, 58, -70 + }, + + { + 11, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, 58, -71, -71, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 109, 58, + 58, 58, 58, 58, 58, 58, 58, -71 + }, + + { + 11, -72, -72, -72, -72, -72, -72, -72, -72, -72, + -72, -72, -72, 58, -72, -72, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 110, 58, 58, 58, 58, 58, -72 + }, + + { + 11, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, 58, -73, -73, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 111, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -73 + }, + + { + 11, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, 58, -74, -74, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 112, 58, -74 + + }, + + { + 11, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, 58, -75, -75, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 113, 58, 58, 58, 58, -75 + }, + + { + 11, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, 58, -76, -76, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 114, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -76 + }, + + { + 11, 77, 77, -77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77 + }, + + { + 11, -78, 78, 79, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78 + }, + + { + 11, 80, -79, -79, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80 + + }, + + { + 11, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80 + }, + + { + 11, 81, 81, 82, 81, -81, 81, 81, -81, 81, + 81, 81, 81, 81, 81, -81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81 + }, + + { + 11, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82 + }, + + { + 11, -83, -83, 84, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83 + }, + + { + 11, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84 + + }, + + { + 11, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85 + }, + + { + 11, 86, 86, -86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86 + }, + + { + 11, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + + -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, -87, -87, -87, -87, -87 + }, + + { + 11, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, 115, 89, 89, -88, -88, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -88 + }, + + { + 11, -89, -89, -89, -89, -89, -89, -89, -89, -89, + -89, 89, 89, 89, -89, -89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -89 + + }, + + { + 11, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90 + }, + + { + 11, -91, -91, -91, -91, -91, -91, -91, -91, -91, + -91, 89, 89, 89, -91, -91, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -91 + }, + + { + 11, -92, -92, -92, -92, -92, -92, -92, -92, -92, + -92, 89, 89, 89, -92, -92, 89, 89, 89, 89, + + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -92 + }, + + { + 11, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93 + }, + + { + 11, -94, -94, -94, -94, -94, -94, -94, -94, -94, + -94, -94, -94, 58, -94, -94, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 116, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -94 + + }, + + { + 11, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, 58, -95, -95, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 117, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -95 + }, + + { + 11, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, 58, -96, -96, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 118, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -96 + }, + + { + 11, -97, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, 58, -97, -97, 58, 58, 58, 58, + + 58, 58, 119, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -97 + }, + + { + 11, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, 58, -98, -98, 120, 121, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -98 + }, + + { + 11, -99, -99, -99, -99, -99, -99, -99, -99, -99, + -99, -99, -99, 58, -99, -99, 58, 58, 58, 58, + 58, 122, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -99 + + }, + + { + 11, -100, -100, -100, -100, -100, -100, -100, -100, -100, + -100, -100, -100, 58, -100, -100, 58, 58, 123, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -100 + }, + + { + 11, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, 58, -101, -101, 58, 58, 58, 124, + 58, 58, 58, 58, 58, 125, 58, 126, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -101 + }, + + { + 11, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, 58, -102, -102, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 127, 58, 58, 58, 58, 58, 58, -102 + }, + + { + 11, -103, -103, -103, -103, -103, -103, -103, -103, -103, + -103, -103, -103, 58, -103, -103, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -103 + }, + + { + 11, -104, -104, -104, -104, -104, -104, -104, -104, -104, + -104, -104, -104, 58, -104, -104, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -104 + + }, + + { + 11, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, 58, -105, -105, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 128, 58, + 58, 58, 58, 58, 58, 58, 58, -105 + }, + + { + 11, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, 58, -106, -106, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 129, 58, -106 + }, + + { + 11, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, 58, -107, -107, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 130, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -107 + }, + + { + 11, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, 58, -108, -108, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 131, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -108 + }, + + { + 11, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, 58, -109, -109, 58, 58, 58, 58, + 58, 58, 58, 132, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -109 + + }, + + { + 11, -110, -110, -110, -110, -110, -110, -110, -110, -110, + -110, -110, -110, 58, -110, -110, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 133, 58, -110 + }, + + { + 11, -111, -111, -111, -111, -111, -111, -111, -111, -111, + -111, -111, -111, 58, -111, -111, 58, 58, 58, 58, + 58, 134, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -111 + }, + + { + 11, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, 58, -112, -112, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 135, 58, 58, 58, 58, -112 + }, + + { + 11, -113, -113, -113, -113, -113, -113, -113, -113, -113, + -113, -113, -113, 58, -113, -113, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 136, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -113 + }, + + { + 11, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, 58, -114, -114, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 137, 58, 58, 58, -114 + + }, + + { + 11, -115, -115, -115, -115, -115, -115, -115, -115, -115, + -115, 89, 89, 89, -115, -115, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -115 + }, + + { + 11, -116, -116, -116, -116, -116, -116, -116, -116, -116, + -116, -116, -116, 58, -116, -116, 58, 58, 58, 58, + 58, 138, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -116 + }, + + { + 11, -117, -117, -117, -117, -117, -117, -117, -117, -117, + -117, -117, -117, 58, -117, -117, 58, 58, 58, 139, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -117 + }, + + { + 11, -118, -118, -118, -118, -118, -118, -118, -118, -118, + -118, -118, -118, 58, -118, -118, 58, 58, 58, 58, + 58, 140, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -118 + }, + + { + 11, -119, -119, -119, -119, -119, -119, -119, -119, -119, + -119, -119, -119, 58, -119, -119, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 141, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -119 + + }, + + { + 11, -120, -120, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, 58, -120, -120, 58, 58, 142, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 143, 58, 58, -120 + }, + + { + 11, -121, -121, -121, -121, -121, -121, -121, -121, -121, + -121, -121, -121, 58, -121, -121, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 144, 58, -121 + }, + + { + 11, -122, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, 58, -122, -122, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 145, 58, + 58, 58, 58, 58, 58, 58, 58, -122 + }, + + { + 11, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, 58, -123, -123, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 146, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -123 + }, + + { + 11, -124, -124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, 58, -124, -124, 58, 58, 58, 58, + 58, 58, 58, 58, 147, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -124 + + }, + + { + 11, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, -125, 58, -125, -125, 58, 58, 58, 58, + 58, 58, 148, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -125 + }, + + { + 11, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, 58, -126, -126, 58, 58, 58, 58, + 58, 149, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -126 + }, + + { + 11, -127, -127, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, 58, -127, -127, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -127 + }, + + { + 11, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, 58, -128, -128, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 150, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -128 + }, + + { + 11, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, 58, -129, -129, 58, 58, 58, 151, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -129 + + }, + + { + 11, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, 58, -130, -130, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 152, + 58, 58, 58, 58, 58, 58, 58, -130 + }, + + { + 11, -131, -131, -131, -131, -131, -131, -131, -131, -131, + -131, -131, -131, 58, -131, -131, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 153, 58, 58, 58, 58, 58, 58, -131 + }, + + { + 11, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, 58, -132, -132, 58, 58, 58, 58, + + 58, 154, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -132 + }, + + { + 11, -133, -133, -133, -133, -133, -133, -133, -133, -133, + -133, -133, -133, 58, -133, -133, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 155, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -133 + }, + + { + 11, -134, -134, -134, -134, -134, -134, -134, -134, -134, + -134, -134, -134, 58, -134, -134, 58, 58, 58, 156, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -134 + + }, + + { + 11, -135, -135, -135, -135, -135, -135, -135, -135, -135, + -135, -135, -135, 58, -135, -135, 58, 58, 58, 157, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -135 + }, + + { + 11, -136, -136, -136, -136, -136, -136, -136, -136, -136, + -136, -136, -136, 58, -136, -136, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 158, 58, + 58, 58, 58, 58, 58, 58, 58, -136 + }, + + { + 11, -137, -137, -137, -137, -137, -137, -137, -137, -137, + -137, -137, -137, 58, -137, -137, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 159, 58, 58, -137 + }, + + { + 11, -138, -138, -138, -138, -138, -138, -138, -138, -138, + -138, -138, -138, 58, -138, -138, 58, 160, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -138 + }, + + { + 11, -139, -139, -139, -139, -139, -139, -139, -139, -139, + -139, -139, -139, 58, -139, -139, 58, 58, 58, 58, + 58, 161, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -139 + + }, + + { + 11, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, 58, -140, -140, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 162, 58, + 58, 58, 58, 58, 58, 58, 58, -140 + }, + + { + 11, -141, -141, -141, -141, -141, -141, -141, -141, -141, + -141, -141, -141, 58, -141, -141, 58, 58, 58, 58, + 58, 58, 58, 163, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -141 + }, + + { + 11, -142, -142, -142, -142, -142, -142, -142, -142, -142, + -142, -142, -142, 58, -142, -142, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 164, + 58, 58, 58, 58, 58, 58, 58, -142 + }, + + { + 11, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, 58, -143, -143, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 165, 58, 58, 58, 58, -143 + }, + + { + 11, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, 58, -144, -144, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 166, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -144 + + }, + + { + 11, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, 58, -145, -145, 58, 58, 58, 58, + 167, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -145 + }, + + { + 11, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, 58, -146, -146, 58, 58, 58, 58, + 58, 168, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -146 + }, + + { + 11, -147, -147, -147, -147, -147, -147, -147, -147, -147, + -147, -147, -147, 58, -147, -147, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 169, + 58, 58, 58, 58, 58, 58, 58, -147 + }, + + { + 11, -148, -148, -148, -148, -148, -148, -148, -148, -148, + -148, -148, -148, 58, -148, -148, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -148 + }, + + { + 11, -149, -149, -149, -149, -149, -149, -149, -149, -149, + -149, -149, -149, 58, -149, -149, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 170, 58, + 58, 58, 58, 58, 58, 58, 58, -149 + + }, + + { + 11, -150, -150, -150, -150, -150, -150, -150, -150, -150, + -150, -150, -150, 58, -150, -150, 58, 58, 58, 58, + 58, 171, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -150 + }, + + { + 11, -151, -151, -151, -151, -151, -151, -151, -151, -151, + -151, -151, -151, 58, -151, -151, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 172, + 58, 58, 58, 58, 58, 58, 58, -151 + }, + + { + 11, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -152, -152, -152, 58, -152, -152, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 173, 58, + 58, 58, 58, 58, 58, 58, 58, -152 + }, + + { + 11, -153, -153, -153, -153, -153, -153, -153, -153, -153, + -153, -153, -153, 58, -153, -153, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 174, 58, 58, -153 + }, + + { + 11, -154, -154, -154, -154, -154, -154, -154, -154, -154, + -154, -154, -154, 58, -154, -154, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -154 + + }, + + { + 11, -155, -155, -155, -155, -155, -155, -155, -155, -155, + -155, -155, -155, 58, -155, -155, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 175, 58, 58, 58, 58, -155 + }, + + { + 11, -156, -156, -156, -156, -156, -156, -156, -156, -156, + -156, -156, -156, 58, -156, -156, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 176, 58, 58, -156 + }, + + { + 11, -157, -157, -157, -157, -157, -157, -157, -157, -157, + -157, -157, -157, 58, -157, -157, 58, 58, 58, 58, + + 58, 177, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -157 + }, + + { + 11, -158, -158, -158, -158, -158, -158, -158, -158, -158, + -158, -158, -158, 58, -158, -158, 58, 58, 58, 58, + 58, 58, 58, 178, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -158 + }, + + { + 11, -159, -159, -159, -159, -159, -159, -159, -159, -159, + -159, -159, -159, 58, -159, -159, 58, 179, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -159 + + }, + + { + 11, -160, -160, -160, -160, -160, -160, -160, -160, -160, + -160, -160, -160, 58, -160, -160, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 180, 58, + 58, 58, 58, 58, 58, 58, 58, -160 + }, + + { + 11, -161, -161, -161, -161, -161, -161, -161, -161, -161, + -161, -161, -161, 58, -161, -161, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -161 + }, + + { + 11, -162, -162, -162, -162, -162, -162, -162, -162, -162, + -162, -162, -162, 58, -162, -162, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 181, 58, 58, -162 + }, + + { + 11, -163, -163, -163, -163, -163, -163, -163, -163, -163, + -163, -163, -163, 58, -163, -163, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -163 + }, + + { + 11, -164, -164, -164, -164, -164, -164, -164, -164, -164, + -164, -164, -164, 58, -164, -164, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 182, + 58, 58, 58, 58, 58, 58, 58, -164 + + }, + + { + 11, -165, -165, -165, -165, -165, -165, -165, -165, -165, + -165, -165, -165, 58, -165, -165, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 183, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -165 + }, + + { + 11, -166, -166, -166, -166, -166, -166, -166, -166, -166, + -166, -166, -166, 58, -166, -166, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 184, 58, 58, -166 + }, + + { + 11, -167, -167, -167, -167, -167, -167, -167, -167, -167, + -167, -167, -167, 58, -167, -167, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 185, 58, 58, 58, -167 + }, + + { + 11, -168, -168, -168, -168, -168, -168, -168, -168, -168, + -168, -168, -168, 58, -168, -168, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -168 + }, + + { + 11, -169, -169, -169, -169, -169, -169, -169, -169, -169, + -169, -169, -169, 58, -169, -169, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 186, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -169 + + }, + + { + 11, -170, -170, -170, -170, -170, -170, -170, -170, -170, + -170, -170, -170, 58, -170, -170, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 187, 58, -170 + }, + + { + 11, -171, -171, -171, -171, -171, -171, -171, -171, -171, + -171, -171, -171, 58, -171, -171, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 188, 58, + 58, 58, 58, 58, 58, 58, 58, -171 + }, + + { + 11, -172, -172, -172, -172, -172, -172, -172, -172, -172, + -172, -172, -172, 58, -172, -172, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 189, 58, + 58, 58, 58, 58, 58, 58, 58, -172 + }, + + { + 11, -173, -173, -173, -173, -173, -173, -173, -173, -173, + -173, -173, -173, 58, -173, -173, 58, 190, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -173 + }, + + { + 11, -174, -174, -174, -174, -174, -174, -174, -174, -174, + -174, -174, -174, 58, -174, -174, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -174 + + }, + + { + 11, -175, -175, -175, -175, -175, -175, -175, -175, -175, + -175, -175, -175, 58, -175, -175, 58, 58, 58, 58, + 58, 191, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -175 + }, + + { + 11, -176, -176, -176, -176, -176, -176, -176, -176, -176, + -176, -176, -176, 58, -176, -176, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -176 + }, + + { + 11, -177, -177, -177, -177, -177, -177, -177, -177, -177, + -177, -177, -177, 58, -177, -177, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -177 + }, + + { + 11, -178, -178, -178, -178, -178, -178, -178, -178, -178, + -178, -178, -178, 58, -178, -178, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -178 + }, + + { + 11, -179, -179, -179, -179, -179, -179, -179, -179, -179, + -179, -179, -179, 58, -179, -179, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 192, 58, 58, -179 + + }, + + { + 11, -180, -180, -180, -180, -180, -180, -180, -180, -180, + -180, -180, -180, 58, -180, -180, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -180 + }, + + { + 11, -181, -181, -181, -181, -181, -181, -181, -181, -181, + -181, -181, -181, 58, -181, -181, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -181 + }, + + { + 11, -182, -182, -182, -182, -182, -182, -182, -182, -182, + -182, -182, -182, 58, -182, -182, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 193, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -182 + }, + + { + 11, -183, -183, -183, -183, -183, -183, -183, -183, -183, + -183, -183, -183, 58, -183, -183, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 194, 58, 58, 58, -183 + }, + + { + 11, -184, -184, -184, -184, -184, -184, -184, -184, -184, + -184, -184, -184, 58, -184, -184, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -184 + + }, + + { + 11, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, 58, -185, -185, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -185 + }, + + { + 11, -186, -186, -186, -186, -186, -186, -186, -186, -186, + -186, -186, -186, 58, -186, -186, 58, 58, 58, 195, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -186 + }, + + { + 11, -187, -187, -187, -187, -187, -187, -187, -187, -187, + -187, -187, -187, 58, -187, -187, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -187 + }, + + { + 11, -188, -188, -188, -188, -188, -188, -188, -188, -188, + -188, -188, -188, 58, -188, -188, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 196, 58, -188 + }, + + { + 11, -189, -189, -189, -189, -189, -189, -189, -189, -189, + -189, -189, -189, 58, -189, -189, 58, 58, 58, 58, + 58, 58, 197, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -189 + + }, + + { + 11, -190, -190, -190, -190, -190, -190, -190, -190, -190, + -190, -190, -190, 58, -190, -190, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 198, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -190 + }, + + { + 11, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, 58, -191, -191, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 199, 58, 58, 58, -191 + }, + + { + 11, -192, -192, -192, -192, -192, -192, -192, -192, -192, + -192, -192, -192, 58, -192, -192, 58, 58, 58, 58, + + 58, 200, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -192 + }, + + { + 11, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, 58, -193, -193, 58, 58, 58, 58, + 58, 201, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -193 + }, + + { + 11, -194, -194, -194, -194, -194, -194, -194, -194, -194, + -194, -194, -194, 58, -194, -194, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 202, 58, 58, -194 + + }, + + { + 11, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, 58, -195, -195, 58, 58, 58, 58, + 58, 203, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -195 + }, + + { + 11, -196, -196, -196, -196, -196, -196, -196, -196, -196, + -196, -196, -196, 58, -196, -196, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -196 + }, + + { + 11, -197, -197, -197, -197, -197, -197, -197, -197, -197, + -197, -197, -197, 58, -197, -197, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 204, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -197 + }, + + { + 11, -198, -198, -198, -198, -198, -198, -198, -198, -198, + -198, -198, -198, 58, -198, -198, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -198 + }, + + { + 11, -199, -199, -199, -199, -199, -199, -199, -199, -199, + -199, -199, -199, 58, -199, -199, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -199 + + }, + + { + 11, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, 58, -200, -200, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -200 + }, + + { + 11, -201, -201, -201, -201, -201, -201, -201, -201, -201, + -201, -201, -201, 58, -201, -201, 58, 205, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -201 + }, + + { + 11, -202, -202, -202, -202, -202, -202, -202, -202, -202, + -202, -202, -202, 58, -202, -202, 58, 206, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -202 + }, + + { + 11, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, 58, -203, -203, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -203 + }, + + { + 11, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, 58, -204, -204, 58, 58, 58, 58, + 58, 58, 58, 207, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -204 + + }, + + { + 11, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, 58, -205, -205, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 208, 58, + 58, 58, 58, 58, 58, 58, 58, -205 + }, + + { + 11, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, 58, -206, -206, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 209, 58, 58, -206 + }, + + { + 11, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, 58, -207, -207, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -207 + }, + + { + 11, -208, -208, -208, -208, -208, -208, -208, -208, -208, + -208, -208, -208, 58, -208, -208, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -208 + }, + + { + 11, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, 58, -209, -209, 58, 58, 58, 58, + 58, 210, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -209 + + }, + + { + 11, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, 58, -210, -210, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -210 + }, + + } ; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up zconftext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + zconfleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 64 +#define YY_END_OF_BUFFER 65 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[211] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 65, 5, 4, 3, 2, 36, 37, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 63, 60, 62, 55, 59, 58, 57, 53, 48, 42, + 47, 51, 53, 40, 41, 50, 50, 43, 53, 50, + 50, 53, 4, 3, 2, 2, 1, 35, 35, 35, + 35, 35, 35, 35, 16, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 63, 60, 62, 61, + 55, 54, 57, 56, 44, 51, 38, 50, 50, 52, + 45, 46, 39, 35, 35, 35, 35, 35, 35, 35, + + 35, 35, 30, 29, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 49, 25, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 15, 35, 7, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 17, 35, 35, + 35, 35, 35, 34, 35, 35, 35, 35, 35, 35, + 10, 35, 13, 35, 35, 35, 35, 33, 35, 35, + 35, 35, 35, 22, 35, 32, 9, 31, 35, 26, + 12, 35, 35, 21, 18, 35, 8, 35, 35, 35, + 35, 35, 27, 35, 35, 6, 35, 20, 19, 23, + + 35, 35, 11, 35, 35, 35, 14, 28, 35, 24 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 1, 7, 8, 9, + 10, 1, 1, 1, 11, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, + 14, 1, 1, 1, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 15, 1, 1, 16, 1, 17, 18, 19, 20, + + 21, 22, 23, 24, 25, 13, 13, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 13, 13, 36, + 13, 13, 1, 37, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +extern int zconf_flex_debug; +int zconf_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *zconftext; + +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define START_STRSIZE 16 + +char *text; +static char *text_ptr; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static struct buffer *zconf_endfile(void); + +void new_string(void) +{ + text = malloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_ptr = text; + text_size = 0; + *text_ptr = 0; +} + +void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + text = realloc(text, new_size); + text_asize = new_size; + text_ptr = text + text_size; + } + memcpy(text_ptr, str, size); + text_ptr += size; + text_size += size; + *text_ptr = 0; +} + +void alloc_string(const char *str, int size) +{ + text = malloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} + +#define INITIAL 0 +#define COMMAND 1 +#define HELP 2 +#define STRING 3 +#define PARAM 4 + +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int zconfwrap (void ); +#else +extern int zconfwrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + errno=0; \ + while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(zconfin); \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int zconflex (void); + +#define YY_DECL int zconflex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after zconftext and zconfleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + int str = 0; + int ts, i; + + if ( (yy_init) ) + { + (yy_init) = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! zconfin ) + zconfin = stdin; + + if ( ! zconfout ) + zconfout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of zconftext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + ++yy_cp; + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +current_file->lineno++; + YY_BREAK +case 2: +YY_RULE_SETUP + + YY_BREAK +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +current_file->lineno++; return T_EOL; + YY_BREAK +case 4: +YY_RULE_SETUP +{ + BEGIN(COMMAND); +} + YY_BREAK +case 5: +YY_RULE_SETUP +{ + unput(zconftext[0]); + BEGIN(COMMAND); +} + YY_BREAK + +case 6: +YY_RULE_SETUP +BEGIN(PARAM); return T_MAINMENU; + YY_BREAK +case 7: +YY_RULE_SETUP +BEGIN(PARAM); return T_MENU; + YY_BREAK +case 8: +YY_RULE_SETUP +BEGIN(PARAM); return T_ENDMENU; + YY_BREAK +case 9: +YY_RULE_SETUP +BEGIN(PARAM); return T_SOURCE; + YY_BREAK +case 10: +YY_RULE_SETUP +BEGIN(PARAM); return T_CHOICE; + YY_BREAK +case 11: +YY_RULE_SETUP +BEGIN(PARAM); return T_ENDCHOICE; + YY_BREAK +case 12: +YY_RULE_SETUP +BEGIN(PARAM); return T_COMMENT; + YY_BREAK +case 13: +YY_RULE_SETUP +BEGIN(PARAM); return T_CONFIG; + YY_BREAK +case 14: +YY_RULE_SETUP +BEGIN(PARAM); return T_MENUCONFIG; + YY_BREAK +case 15: +YY_RULE_SETUP +BEGIN(PARAM); return T_HELP; + YY_BREAK +case 16: +YY_RULE_SETUP +BEGIN(PARAM); return T_IF; + YY_BREAK +case 17: +YY_RULE_SETUP +BEGIN(PARAM); return T_ENDIF; + YY_BREAK +case 18: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEPENDS; + YY_BREAK +case 19: +YY_RULE_SETUP +BEGIN(PARAM); return T_REQUIRES; + YY_BREAK +case 20: +YY_RULE_SETUP +BEGIN(PARAM); return T_OPTIONAL; + YY_BREAK +case 21: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEFAULT; + YY_BREAK +case 22: +YY_RULE_SETUP +BEGIN(PARAM); return T_PROMPT; + YY_BREAK +case 23: +YY_RULE_SETUP +BEGIN(PARAM); return T_TRISTATE; + YY_BREAK +case 24: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEF_TRISTATE; + YY_BREAK +case 25: +YY_RULE_SETUP +BEGIN(PARAM); return T_BOOLEAN; + YY_BREAK +case 26: +YY_RULE_SETUP +BEGIN(PARAM); return T_BOOLEAN; + YY_BREAK +case 27: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEF_BOOLEAN; + YY_BREAK +case 28: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEF_BOOLEAN; + YY_BREAK +case 29: +YY_RULE_SETUP +BEGIN(PARAM); return T_INT; + YY_BREAK +case 30: +YY_RULE_SETUP +BEGIN(PARAM); return T_HEX; + YY_BREAK +case 31: +YY_RULE_SETUP +BEGIN(PARAM); return T_STRING; + YY_BREAK +case 32: +YY_RULE_SETUP +BEGIN(PARAM); return T_SELECT; + YY_BREAK +case 33: +YY_RULE_SETUP +BEGIN(PARAM); return T_SELECT; + YY_BREAK +case 34: +YY_RULE_SETUP +BEGIN(PARAM); return T_RANGE; + YY_BREAK +case 35: +YY_RULE_SETUP +{ + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 36: +YY_RULE_SETUP + + YY_BREAK +case 37: +/* rule 37 can match eol */ +YY_RULE_SETUP +current_file->lineno++; BEGIN(INITIAL); + YY_BREAK + +case 38: +YY_RULE_SETUP +return T_AND; + YY_BREAK +case 39: +YY_RULE_SETUP +return T_OR; + YY_BREAK +case 40: +YY_RULE_SETUP +return T_OPEN_PAREN; + YY_BREAK +case 41: +YY_RULE_SETUP +return T_CLOSE_PAREN; + YY_BREAK +case 42: +YY_RULE_SETUP +return T_NOT; + YY_BREAK +case 43: +YY_RULE_SETUP +return T_EQUAL; + YY_BREAK +case 44: +YY_RULE_SETUP +return T_UNEQUAL; + YY_BREAK +case 45: +YY_RULE_SETUP +return T_IF; + YY_BREAK +case 46: +YY_RULE_SETUP +return T_ON; + YY_BREAK +case 47: +YY_RULE_SETUP +{ + str = zconftext[0]; + new_string(); + BEGIN(STRING); + } + YY_BREAK +case 48: +/* rule 48 can match eol */ +YY_RULE_SETUP +BEGIN(INITIAL); current_file->lineno++; return T_EOL; + YY_BREAK +case 49: +YY_RULE_SETUP +/* ignore */ + YY_BREAK +case 50: +YY_RULE_SETUP +{ + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 51: +YY_RULE_SETUP +/* comment */ + YY_BREAK +case 52: +/* rule 52 can match eol */ +YY_RULE_SETUP +current_file->lineno++; + YY_BREAK +case 53: +YY_RULE_SETUP + + YY_BREAK +case YY_STATE_EOF(PARAM): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 54: +/* rule 54 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 55: +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + } + YY_BREAK +case 56: +/* rule 56 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 57: +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + } + YY_BREAK +case 58: +YY_RULE_SETUP +{ + if (str == zconftext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(zconftext, 1); + } + YY_BREAK +case 59: +/* rule 59 can match eol */ +YY_RULE_SETUP +{ + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + YY_BREAK +case YY_STATE_EOF(STRING): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 60: +YY_RULE_SETUP +{ + ts = 0; + for (i = 0; i < zconfleng; i++) { + if (zconftext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + YY_BREAK +case 61: +/* rule 61 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK +case 62: +/* rule 62 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + append_string("\n", 1); + } + YY_BREAK +case 63: +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + if (!first_ts) + first_ts = last_ts; + } + YY_BREAK +case YY_STATE_EOF(HELP): +{ + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK + +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(COMMAND): +{ + if (current_buf) { + zconf_endfile(); + return T_EOF; + } + fclose(zconfin); + yyterminate(); +} + YY_BREAK +case 64: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed zconfin at a new source and called + * zconflex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( zconfwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * zconftext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of zconflex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + zconfrestart(zconfin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up zconftext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + zconfrestart(zconfin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( zconfwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve zconftext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void zconfrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_init_buffer(YY_CURRENT_BUFFER,input_file ); + zconf_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * zconfpop_buffer_state(); + * zconfpush_buffer_state(new_buffer); + */ + zconfensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + zconf_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (zconfwrap()) processing, but the only time this flag + * is looked at is after zconfwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void zconf_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + zconf_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with zconf_create_buffer() + * + */ + void zconf_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + zconffree((void *) b->yy_ch_buf ); + + zconffree((void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a zconfrestart() or at EOF. + */ + static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + zconf_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then zconf_init_buffer was _probably_ + * called from zconfrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void zconf_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + zconf_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + zconfensure_buffer_stack(); + + /* This block is copied from zconf_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from zconf_switch_to_buffer. */ + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void zconfpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void zconfensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + zconf_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to zconflex() will + * scan from a @e copy of @a str. + * @param str a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * zconf_scan_bytes() instead. + */ +YY_BUFFER_STATE zconf_scan_string (yyconst char * str ) +{ + + return zconf_scan_bytes(str,strlen(str) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_bytes (yyconst char * bytes, int len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) zconfalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = zconf_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + zconftext[zconfleng] = (yy_hold_char); \ + (yy_c_buf_p) = zconftext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + zconfleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int zconfget_lineno (void) +{ + + return zconflineno; +} + +/** Get the input stream. + * + */ +FILE *zconfget_in (void) +{ + return zconfin; +} + +/** Get the output stream. + * + */ +FILE *zconfget_out (void) +{ + return zconfout; +} + +/** Get the length of the current token. + * + */ +int zconfget_leng (void) +{ + return zconfleng; +} + +/** Get the current token. + * + */ + +char *zconfget_text (void) +{ + return zconftext; +} + +/** Set the current line number. + * @param line_number + * + */ +void zconfset_lineno (int line_number ) +{ + + zconflineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see zconf_switch_to_buffer + */ +void zconfset_in (FILE * in_str ) +{ + zconfin = in_str ; +} + +void zconfset_out (FILE * out_str ) +{ + zconfout = out_str ; +} + +int zconfget_debug (void) +{ + return zconf_flex_debug; +} + +void zconfset_debug (int bdebug ) +{ + zconf_flex_debug = bdebug ; +} + +/* zconflex_destroy is for both reentrant and non-reentrant scanners. */ +int zconflex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + zconfpop_buffer_state(); + } + + /* Destroy the stack itself. */ + zconffree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *zconfalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *zconfrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void zconffree (void * ptr ) +{ + free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef yytext_ptr +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = malloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; + current_file->flags = FILE_BUSY; +} + +void zconf_nextfile(const char *name) +{ + struct file *file = file_lookup(name); + struct buffer *buf = malloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name); + exit(1); + } + zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + if (file->flags & FILE_BUSY) { + printf("recursive scan (%s)?\n", name); + exit(1); + } + if (file->flags & FILE_SCANNED) { + printf("file %s already scanned?\n", name); + exit(1); + } + file->flags |= FILE_BUSY; + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static struct buffer *zconf_endfile(void) +{ + struct buffer *parent; + + current_file->flags |= FILE_SCANNED; + current_file->flags &= ~FILE_BUSY; + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(zconfin); + zconf_delete_buffer(YY_CURRENT_BUFFER); + zconf_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; + + return parent; +} + +int zconf_lineno(void) +{ + if (current_buf) + return current_file->lineno - 1; + else + return 0; +} + +char *zconf_curname(void) +{ + if (current_buf) + return current_file->name; + else + return ""; +} + diff --git a/busybox/scripts/config/lkc.h b/busybox/scripts/config/lkc.h new file mode 100644 index 000000000..dd040f7a8 --- /dev/null +++ b/busybox/scripts/config/lkc.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef LKC_H +#define LKC_H + +#include "expr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LKC_DIRECT_LINK +#define P(name,type,arg) extern type name arg +#else +#include "lkc_defs.h" +#define P(name,type,arg) extern type (*name ## _p) arg +#endif +#include "lkc_proto.h" +#undef P + +#define SRCTREE "srctree" + +int zconfparse(void); +void zconfdump(FILE *out); + +extern int zconfdebug; +void zconf_starthelp(void); +FILE *zconf_fopen(const char *name); +void zconf_initscan(const char *name); +void zconf_nextfile(const char *name); +int zconf_lineno(void); +char *zconf_curname(void); + +/* confdata.c */ +extern const char conf_def_filename[]; +extern char conf_filename[]; + +char *conf_get_default_confname(void); + +/* kconfig_load.c */ +void kconfig_load(void); + +/* menu.c */ +void menu_init(void); +void menu_add_menu(void); +void menu_end_menu(void); +void menu_add_entry(struct symbol *sym); +void menu_end_entry(void); +void menu_add_dep(struct expr *dep); +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep); +void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); +void menu_finalize(struct menu *parent); +void menu_set_type(int type); +struct file *file_lookup(const char *name); +int file_write_dep(const char *name); + +extern struct menu *current_entry; +extern struct menu *current_menu; + +/* symbol.c */ +void sym_init(void); +void sym_clear_all_valid(void); +void sym_set_changed(struct symbol *sym); +struct symbol *sym_check_deps(struct symbol *sym); +struct property *prop_alloc(enum prop_type type, struct symbol *sym); +struct symbol *prop_get_symbol(struct property *prop); + +static inline tristate sym_get_tristate_value(struct symbol *sym) +{ + return sym->curr.tri; +} + + +static inline struct symbol *sym_get_choice_value(struct symbol *sym) +{ + return (struct symbol *)sym->curr.val; +} + +static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval) +{ + return sym_set_tristate_value(chval, yes); +} + +static inline bool sym_is_choice(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICE ? true : false; +} + +static inline bool sym_is_choice_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICEVAL ? true : false; +} + +static inline bool sym_is_optional(struct symbol *sym) +{ + return sym->flags & SYMBOL_OPTIONAL ? true : false; +} + +static inline bool sym_has_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_NEW ? false : true; +} + +#ifdef __cplusplus +} +#endif + +#endif /* LKC_H */ diff --git a/busybox/scripts/config/lkc_proto.h b/busybox/scripts/config/lkc_proto.h new file mode 100644 index 000000000..97c79178e --- /dev/null +++ b/busybox/scripts/config/lkc_proto.h @@ -0,0 +1,39 @@ + +/* confdata.c */ +P(conf_parse,void,(const char *name)); +P(conf_read,int,(const char *name)); +P(conf_write,int,(const char *name)); + +/* menu.c */ +P(rootmenu,struct menu,); + +P(menu_is_visible,bool,(struct menu *menu)); +P(menu_get_prompt,const char *,(struct menu *menu)); +P(menu_get_root_menu,struct menu *,(struct menu *menu)); +P(menu_get_parent_menu,struct menu *,(struct menu *menu)); + +/* symbol.c */ +P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]); +P(sym_change_count,int,); + +P(sym_lookup,struct symbol *,(const char *name, int isconst)); +P(sym_find,struct symbol *,(const char *name)); +P(sym_type_name,const char *,(enum symbol_type type)); +P(sym_calc_value,void,(struct symbol *sym)); +P(sym_get_type,enum symbol_type,(struct symbol *sym)); +P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri)); +P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri)); +P(sym_toggle_tristate_value,tristate,(struct symbol *sym)); +P(sym_string_valid,bool,(struct symbol *sym, const char *newval)); +P(sym_string_within_range,bool,(struct symbol *sym, const char *str)); +P(sym_set_string_value,bool,(struct symbol *sym, const char *newval)); +P(sym_is_changable,bool,(struct symbol *sym)); +P(sym_get_choice_prop,struct property *,(struct symbol *sym)); +P(sym_get_default_prop,struct property *,(struct symbol *sym)); +P(sym_get_string_value,const char *,(struct symbol *sym)); + +P(prop_get_type_name,const char *,(enum prop_type type)); + +/* expr.c */ +P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2)); +P(expr_print,void,(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken)); diff --git a/busybox/scripts/config/mconf.c b/busybox/scripts/config/mconf.c new file mode 100644 index 000000000..63b4ff72f --- /dev/null +++ b/busybox/scripts/config/mconf.c @@ -0,0 +1,713 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + * + * Introduced single menu mode (show all sub-menus in one large tree). + * 2002-11-06 Petr Baudis + * + * Directly use liblxdialog library routines. + * 2002-11-14 Petr Baudis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dialog.h" + +#define LKC_DIRECT_LINK +#include "lkc.h" + +static char menu_backtitle[128]; +static const char menu_instructions[] = + "Arrow keys navigate the menu. " + " selects submenus --->. " + "Highlighted letters are hotkeys. " + "Pressing selectes a feature, while will exclude a feature. " + "Press to exit, for Help. " + "Legend: [*] feature is selected [ ] feature is excluded", +radiolist_instructions[] = + "Use the arrow keys to navigate this window or " + "press the hotkey of the item you wish to select " + "followed by the . " + "Press for additional information about this option.", +inputbox_instructions_int[] = + "Please enter a decimal value. " + "Fractions will not be accepted. " + "Use the key to move from the input field to the buttons below it.", +inputbox_instructions_hex[] = + "Please enter a hexadecimal value. " + "Use the key to move from the input field to the buttons below it.", +inputbox_instructions_string[] = + "Please enter a string value. " + "Use the key to move from the input field to the buttons below it.", +setmod_text[] = + "This feature depends on another which has been configured as a module.\n" + "As a result, this feature will be built as a module.", +nohelp_text[] = + "There is no help available for this option.\n", +load_config_text[] = + "Enter the name of the configuration file you wish to load. " + "Accept the name shown to restore the configuration you " + "last retrieved. Leave blank to abort.", +load_config_help[] = + "\n" + "For various reasons, one may wish to keep several different BusyBox\n" + "configurations available on a single machine.\n" + "\n" + "If you have saved a previous configuration in a file other than the\n" + "BusyBox's default, entering the name of the file here will allow you\n" + "to modify that configuration.\n" + "\n" + "If you are uncertain, then you have probably never used alternate\n" + "configuration files. You should therefor leave this blank to abort.\n", +save_config_text[] = + "Enter a filename to which this configuration should be saved " + "as an alternate. Leave blank to abort.", +save_config_help[] = + "\n" + "For various reasons, one may wish to keep different BusyBox\n" + "configurations available on a single machine.\n" + "\n" + "Entering a file name here will allow you to later retrieve, modify\n" + "and use the current configuration as an alternate to whatever\n" + "configuration options you have selected at that time.\n" + "\n" + "If you are uncertain what all this means then you should probably\n" + "leave this blank.\n", +top_menu_help[] = + "\n" + "Use the Up/Down arrow keys (cursor keys) to highlight the item\n" + "you wish to change or submenu wish to select and press .\n" + "Submenus are designated by \"--->\".\n" + "\n" + "Shortcut: Press the option's highlighted letter (hotkey).\n" + "\n" + "You may also use the and keys to scroll\n" + "unseen options into view.\n" +; + +static char filename[PATH_MAX+1] = ".config"; +static int indent = 0; +static struct termios ios_org; +static int rows, cols; +struct menu *current_menu; +static int child_count; +static int single_menu_mode; + +static struct dialog_list_item *items[16384]; /* FIXME: This ought to be dynamic. */ +static int item_no; + +static void conf(struct menu *menu); +static void conf_choice(struct menu *menu); +static void conf_string(struct menu *menu); +static void conf_load(void); +static void conf_save(void); +static void show_textbox(const char *title, const char *text, int r, int c); +static void show_helptext(const char *title, const char *text); +static void show_help(struct menu *menu); +static void show_readme(void); + +static void init_wsize(void) +{ + struct winsize ws; + char *env; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1) { + rows = 24; + cols = 80; + } else { + rows = ws.ws_row; + cols = ws.ws_col; + if (!rows) { + env = getenv("LINES"); + if (env) + rows = atoi(env); + if (!rows) + rows = 24; + } + if (!cols) { + env = getenv("COLUMNS"); + if (env) + cols = atoi(env); + if (!cols) + cols = 80; + } + } + + if (rows < 19 || cols < 80) { + fprintf(stderr, "Your display is too small to run Menuconfig!\n"); + fprintf(stderr, "It must be at least 19 lines by 80 columns.\n"); + exit(1); + } + + rows -= 4; + cols -= 5; +} + +static void cinit(void) +{ + item_no = 0; +} + +static void cmake(void) +{ + items[item_no] = malloc(sizeof(struct dialog_list_item)); + memset(items[item_no], 0, sizeof(struct dialog_list_item)); + items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0; + items[item_no]->name = malloc(512); items[item_no]->name[0] = 0; + items[item_no]->namelen = 0; + item_no++; +} + +static int cprint_name(const char *fmt, ...) +{ + va_list ap; + int res; + + if (!item_no) + cmake(); + va_start(ap, fmt); + res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen, + 512 - items[item_no - 1]->namelen, fmt, ap); + if (res > 0) + items[item_no - 1]->namelen += res; + va_end(ap); + + return res; +} + +static int cprint_tag(const char *fmt, ...) +{ + va_list ap; + int res; + + if (!item_no) + cmake(); + va_start(ap, fmt); + res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap); + va_end(ap); + + return res; +} + +static void cdone(void) +{ + int i; + + for (i = 0; i < item_no; i++) { + free(items[i]->tag); + free(items[i]->name); + free(items[i]); + } + + item_no = 0; +} + +static void build_conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + int type, tmp, doint = 2; + tristate val; + char ch; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (!sym) { + if (prop && menu != current_menu) { + const char *prompt = menu_get_prompt(menu); + switch (prop->type) { + case P_MENU: + child_count++; + cmake(); + cprint_tag("m%p", menu); + + if (single_menu_mode) { + cprint_name("%s%*c%s", + menu->data ? "-->" : "++>", + indent + 1, ' ', prompt); + } else { + cprint_name(" %*c%s --->", indent + 1, ' ', prompt); + } + + if (single_menu_mode && menu->data) + goto conf_childs; + return; + default: + if (prompt) { + child_count++; + cmake(); + cprint_tag(":%p", menu); + cprint_name("---%*c%s", indent + 1, ' ', prompt); + } + } + } else + doint = 0; + goto conf_childs; + } + + cmake(); + type = sym_get_type(sym); + if (sym_is_choice(sym)) { + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + child_count++; + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + val = sym_get_tristate_value(sym); + if (sym_is_changable(sym)) { + cprint_tag("t%p", menu); + switch (type) { + case S_BOOLEAN: + cprint_name("[%c]", val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + cprint_name("<%c>", ch); + break; + } + } else { + cprint_tag("%c%p", def_menu ? 't' : ':', menu); + cprint_name(" "); + } + + cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); + if (val == yes) { + if (def_menu) { + cprint_name(" (%s)", menu_get_prompt(def_menu)); + cprint_name(" --->"); + if (def_menu->list) { + indent += 2; + build_conf(def_menu); + indent -= 2; + } + } + return; + } + } else { + child_count++; + val = sym_get_tristate_value(sym); + if (sym_is_choice_value(sym) && val == yes) { + cprint_tag(":%p", menu); + cprint_name(" "); + } else { + switch (type) { + case S_BOOLEAN: + cprint_tag("t%p", menu); + if (sym_is_changable(sym)) + cprint_name("[%c]", val == no ? ' ' : '*'); + else + cprint_name("---"); + break; + case S_TRISTATE: + cprint_tag("t%p", menu); + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + if (sym_is_changable(sym)) + cprint_name("<%c>", ch); + else + cprint_name("---"); + break; + default: + cprint_tag("s%p", menu); + tmp = cprint_name("(%s)", sym_get_string_value(sym)); + tmp = indent - tmp + 4; + if (tmp < 0) + tmp = 0; + cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : " (NEW)"); + goto conf_childs; + } + } + cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : " (NEW)"); + if (menu->prompt->type == P_MENU) { + cprint_name(" --->"); + return; + } + } + +conf_childs: + indent += doint; + for (child = menu->list; child; child = child->next) + build_conf(child); + indent -= doint; +} + +static void conf(struct menu *menu) +{ + struct dialog_list_item *active_item = NULL; + struct menu *submenu; + const char *prompt = menu_get_prompt(menu); + struct symbol *sym; + char active_entry[40]; + int stat, type; + + unlink("lxdialog.scrltmp"); + active_entry[0] = 0; + while (1) { + indent = 0; + child_count = 0; + current_menu = menu; + cdone(); cinit(); + build_conf(menu); + if (!child_count) + break; + if (menu == &rootmenu) { + cmake(); cprint_tag(":"); cprint_name("--- "); + cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File"); + cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File"); + } + dialog_clear(); + stat = dialog_menu(prompt ? prompt : "Main Menu", + menu_instructions, rows, cols, rows - 10, + active_entry, item_no, items); + if (stat < 0) + return; + + if (stat == 1 || stat == 255) + break; + + active_item = first_sel_item(item_no, items); + if (!active_item) + continue; + active_item->selected = 0; + strncpy(active_entry, active_item->tag, sizeof(active_entry)); + active_entry[sizeof(active_entry)-1] = 0; + type = active_entry[0]; + if (!type) + continue; + + sym = NULL; + submenu = NULL; + if (sscanf(active_entry + 1, "%p", &submenu) == 1) + sym = submenu->sym; + + switch (stat) { + case 0: + switch (type) { + case 'm': + if (single_menu_mode) + submenu->data = (void *) (long) !submenu->data; + else + conf(submenu); + break; + case 't': + if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) + conf_choice(submenu); + else if (submenu->prompt->type == P_MENU) + conf(submenu); + break; + case 's': + conf_string(submenu); + break; + case 'L': + conf_load(); + break; + case 'S': + conf_save(); + break; + } + break; + case 2: + if (sym) + show_help(submenu); + else + show_readme(); + break; + case 3: + if (type == 't') { + if (sym_set_tristate_value(sym, yes)) + break; + if (sym_set_tristate_value(sym, mod)) + show_textbox(NULL, setmod_text, 6, 74); + } + break; + case 4: + if (type == 't') + sym_set_tristate_value(sym, no); + break; + case 5: + if (type == 't') + sym_set_tristate_value(sym, mod); + break; + case 6: + if (type == 't') + sym_toggle_tristate_value(sym); + else if (type == 'm') + conf(submenu); + break; + } + } +} + +static void show_textbox(const char *title, const char *text, int r, int c) +{ + int fd; + + fd = creat(".help.tmp", 0777); + write(fd, text, strlen(text)); + close(fd); + while (dialog_textbox(title, ".help.tmp", r, c) < 0) + ; + unlink(".help.tmp"); +} + +static void show_helptext(const char *title, const char *text) +{ + show_textbox(title, text, rows, cols); +} + +static void show_help(struct menu *menu) +{ + const char *help; + char *helptext; + struct symbol *sym = menu->sym; + + help = sym->help; + if (!help) + help = nohelp_text; + if (sym->name) { + helptext = malloc(strlen(sym->name) + strlen(help) + 16); + sprintf(helptext, "%s:\n\n%s", sym->name, help); + show_helptext(menu_get_prompt(menu), helptext); + free(helptext); + } else + show_helptext(menu_get_prompt(menu), help); +} + +static void show_readme(void) +{ + show_helptext("Help", top_menu_help); +} + +static void conf_choice(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + struct menu *child; + struct symbol *active; + + active = sym_get_choice_value(menu->sym); + while (1) { + current_menu = menu; + cdone(); cinit(); + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + cmake(); + cprint_tag("%p", child); + cprint_name("%s", menu_get_prompt(child)); + if (child->sym == sym_get_choice_value(menu->sym)) + items[item_no - 1]->selected = 1; /* ON */ + else if (child->sym == active) + items[item_no - 1]->selected = 2; /* SELECTED */ + else + items[item_no - 1]->selected = 0; /* OFF */ + } + + switch (dialog_checklist(prompt ? prompt : "Main Menu", + radiolist_instructions, 15, 70, 6, + item_no, items, FLAG_RADIO)) { + case 0: + if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) != 1) + break; + sym_set_tristate_value(child->sym, yes); + return; + case 1: + if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) == 1) { + show_help(child); + active = child->sym; + } else + show_help(menu); + break; + case 255: + return; + } + } +} + +static void conf_string(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + + while (1) { + char *heading; + + switch (sym_get_type(menu->sym)) { + case S_INT: + heading = (char *) inputbox_instructions_int; + break; + case S_HEX: + heading = (char *) inputbox_instructions_hex; + break; + case S_STRING: + heading = (char *) inputbox_instructions_string; + break; + default: + heading = "Internal mconf error!"; + /* panic? */; + } + + switch (dialog_inputbox(prompt ? prompt : "Main Menu", + heading, 10, 75, + sym_get_string_value(menu->sym))) { + case 0: + if (sym_set_string_value(menu->sym, dialog_input_result)) + return; + show_textbox(NULL, "You have made an invalid entry.", 5, 43); + break; + case 1: + show_help(menu); + break; + case 255: + return; + } + } +} + +static void conf_load(void) +{ + while (1) { + switch (dialog_inputbox(NULL, load_config_text, 11, 55, + filename)) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_read(dialog_input_result)) + return; + show_textbox(NULL, "File does not exist!", 5, 38); + break; + case 1: + show_helptext("Load Alternate Configuration", load_config_help); + break; + case 255: + return; + } + } +} + +static void conf_save(void) +{ + while (1) { + switch (dialog_inputbox(NULL, save_config_text, 11, 55, + filename)) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_write(dialog_input_result)) + return; + show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60); + break; + case 1: + show_helptext("Save Alternate Configuration", save_config_help); + break; + case 255: + return; + } + } +} + +static void conf_cleanup(void) +{ + tcsetattr(1, TCSAFLUSH, &ios_org); + unlink(".help.tmp"); +} + +static void winch_handler(int sig) +{ + struct winsize ws; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1) { + rows = 24; + cols = 80; + } else { + rows = ws.ws_row; + cols = ws.ws_col; + } + + if (rows < 19 || cols < 80) { + end_dialog(); + fprintf(stderr, "Your display is too small to run Menuconfig!\n"); + fprintf(stderr, "It must be at least 19 lines by 80 columns.\n"); + exit(1); + } + + rows -= 4; + cols -= 5; + +} + +int main(int ac, char **av) +{ + int stat; + char *mode; + struct symbol *sym; + + conf_parse(av[1]); + conf_read(NULL); + + sym = sym_lookup("VERSION", 0); + sym_calc_value(sym); + snprintf(menu_backtitle, 128, "BusyBox v%s Configuration", + sym_get_string_value(sym)); + + mode = getenv("MENUCONFIG_MODE"); + if (mode) { + if (!strcasecmp(mode, "single_menu")) + single_menu_mode = 1; + } + + tcgetattr(1, &ios_org); + atexit(conf_cleanup); + init_wsize(); + init_dialog(); + signal(SIGWINCH, winch_handler); + conf(&rootmenu); + end_dialog(); + + /* Restart dialog to act more like when lxdialog was still separate */ + init_dialog(); + do { + stat = dialog_yesno(NULL, + "Do you wish to save your new BusyBox configuration?", 5, 60); + } while (stat < 0); + end_dialog(); + + if (stat == 0) { + conf_write(NULL); + printf("\n\n" + "*** End of BusyBox configuration.\n" + "*** Check the top-level Makefile for additional configuration options.\n\n"); + } else + printf("\n\nYour BusyBox configuration changes were NOT saved.\n\n"); + + return 0; +} diff --git a/busybox/scripts/config/menu.c b/busybox/scripts/config/menu.c new file mode 100644 index 000000000..6425296fc --- /dev/null +++ b/busybox/scripts/config/menu.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +struct menu rootmenu; +struct menu *current_menu, *current_entry; +static struct menu **last_entry_ptr; + +struct file *file_list; +struct file *current_file; + +static void menu_warn(struct menu *menu, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +static void prop_warn(struct property *prop, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +void menu_init(void) +{ + current_entry = current_menu = &rootmenu; + last_entry_ptr = &rootmenu.list; +} + +void menu_add_entry(struct symbol *sym) +{ + struct menu *menu; + + menu = malloc(sizeof(*menu)); + memset(menu, 0, sizeof(*menu)); + menu->sym = sym; + menu->parent = current_menu; + menu->file = current_file; + menu->lineno = zconf_lineno(); + + *last_entry_ptr = menu; + last_entry_ptr = &menu->next; + current_entry = menu; +} + +void menu_end_entry(void) +{ +} + +void menu_add_menu(void) +{ + current_menu = current_entry; + last_entry_ptr = ¤t_entry->list; +} + +void menu_end_menu(void) +{ + last_entry_ptr = ¤t_menu->next; + current_menu = current_menu->parent; +} + +struct expr *menu_check_dep(struct expr *e) +{ + if (!e) + return e; + + switch (e->type) { + case E_NOT: + e->left.expr = menu_check_dep(e->left.expr); + break; + case E_OR: + case E_AND: + e->left.expr = menu_check_dep(e->left.expr); + e->right.expr = menu_check_dep(e->right.expr); + break; + case E_SYMBOL: + /* change 'm' into 'm' && MODULES */ + if (e->left.sym == &symbol_mod) + return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); + break; + default: + break; + } + return e; +} + +void menu_add_dep(struct expr *dep) +{ + current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); +} + +void menu_set_type(int type) +{ + struct symbol *sym = current_entry->sym; + + if (sym->type == type) + return; + if (sym->type == S_UNKNOWN) { + sym->type = type; + return; + } + menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n", + sym->name ? sym->name : "", + sym_type_name(sym->type), sym_type_name(type)); +} + +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) +{ + struct property *prop = prop_alloc(type, current_entry->sym); + + prop->menu = current_entry; + prop->text = prompt; + prop->expr = expr; + prop->visible.expr = menu_check_dep(dep); + + if (prompt) { + if (current_entry->prompt) + menu_warn(current_entry, "prompt redefined\n"); + current_entry->prompt = prop; + } + + return prop; +} + +void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) +{ + menu_add_prop(type, prompt, NULL, dep); +} + +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) +{ + menu_add_prop(type, NULL, expr, dep); +} + +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) +{ + menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); +} + +void sym_check_prop(struct symbol *sym) +{ + struct property *prop; + struct symbol *sym2; + for (prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_DEFAULT: + if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && + prop->expr->type != E_SYMBOL) + prop_warn(prop, + "default for config symbol '%'" + " must be a single symbol", sym->name); + break; + case P_SELECT: + sym2 = prop_get_symbol(prop); + if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) + prop_warn(prop, + "config symbol '%s' uses select, but is " + "not boolean or tristate", sym->name); + else if (sym2->type == S_UNKNOWN) + prop_warn(prop, + "'select' used by config symbol '%s' " + "refer to undefined symbol '%s'", + sym->name, sym2->name); + else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE) + prop_warn(prop, + "'%s' has wrong type. 'select' only " + "accept arguments of boolean and " + "tristate type", sym2->name); + break; + case P_RANGE: + if (sym->type != S_INT && sym->type != S_HEX) + prop_warn(prop, "range is only allowed " + "for int or hex symbols"); + if (!sym_string_valid(sym, prop->expr->left.sym->name) || + !sym_string_valid(sym, prop->expr->right.sym->name)) + prop_warn(prop, "range is invalid"); + break; + default: + ; + } + } +} + +void menu_finalize(struct menu *parent) +{ + struct menu *menu, *last_menu; + struct symbol *sym; + struct property *prop; + struct expr *parentdep, *basedep, *dep, *dep2, **ep; + + sym = parent->sym; + if (parent->list) { + if (sym && sym_is_choice(sym)) { + /* find the first choice value and find out choice type */ + for (menu = parent->list; menu; menu = menu->next) { + if (menu->sym) { + current_entry = parent; + menu_set_type(menu->sym->type); + current_entry = menu; + menu_set_type(sym->type); + break; + } + } + parentdep = expr_alloc_symbol(sym); + } else if (parent->prompt) + parentdep = parent->prompt->visible.expr; + else + parentdep = parent->dep; + + for (menu = parent->list; menu; menu = menu->next) { + basedep = expr_transform(menu->dep); + basedep = expr_alloc_and(expr_copy(parentdep), basedep); + basedep = expr_eliminate_dups(basedep); + menu->dep = basedep; + if (menu->sym) + prop = menu->sym->prop; + else + prop = menu->prompt; + for (; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + dep = expr_transform(prop->visible.expr); + dep = expr_alloc_and(expr_copy(basedep), dep); + dep = expr_eliminate_dups(dep); + if (menu->sym && menu->sym->type != S_TRISTATE) + dep = expr_trans_bool(dep); + prop->visible.expr = dep; + if (prop->type == P_SELECT) { + struct symbol *es = prop_get_symbol(prop); + es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, + expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); + } + } + } + for (menu = parent->list; menu; menu = menu->next) + menu_finalize(menu); + } else if (sym) { + basedep = parent->prompt ? parent->prompt->visible.expr : NULL; + basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); + basedep = expr_eliminate_dups(expr_transform(basedep)); + last_menu = NULL; + for (menu = parent->next; menu; menu = menu->next) { + dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; + if (!expr_contains_symbol(dep, sym)) + break; + if (expr_depends_symbol(dep, sym)) + goto next; + dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); + dep = expr_eliminate_dups(expr_transform(dep)); + dep2 = expr_copy(basedep); + expr_eliminate_eq(&dep, &dep2); + expr_free(dep); + if (!expr_is_yes(dep2)) { + expr_free(dep2); + break; + } + expr_free(dep2); + next: + menu_finalize(menu); + menu->parent = parent; + last_menu = menu; + } + if (last_menu) { + parent->list = parent->next; + parent->next = last_menu->next; + last_menu->next = NULL; + } + } + for (menu = parent->list; menu; menu = menu->next) { + if (sym && sym_is_choice(sym) && menu->sym) { + menu->sym->flags |= SYMBOL_CHOICEVAL; + if (!menu->prompt) + menu_warn(menu, "choice value must have a prompt"); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type == P_PROMPT && prop->menu != menu) { + prop_warn(prop, "choice values " + "currently only support a " + "single prompt"); + } + if (prop->type == P_DEFAULT) + prop_warn(prop, "defaults for choice " + "values not supported"); + } + current_entry = menu; + menu_set_type(sym->type); + menu_add_symbol(P_CHOICE, sym, NULL); + prop = sym_get_choice_prop(sym); + for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) + ; + *ep = expr_alloc_one(E_CHOICE, NULL); + (*ep)->right.sym = menu->sym; + } + if (menu->list && (!menu->prompt || !menu->prompt->text)) { + for (last_menu = menu->list; ; last_menu = last_menu->next) { + last_menu->parent = parent; + if (!last_menu->next) + break; + } + last_menu->next = menu->next; + menu->next = menu->list; + menu->list = NULL; + } + } + + if (sym && !(sym->flags & SYMBOL_WARNED)) { + if (sym->type == S_UNKNOWN) + menu_warn(parent, "config symbol defined " + "without type\n"); + + if (sym_is_choice(sym) && !parent->prompt) + menu_warn(parent, "choice must have a prompt\n"); + + /* Check properties connected to this symbol */ + sym_check_prop(sym); + sym->flags |= SYMBOL_WARNED; + } + + if (sym && !sym_is_optional(sym) && parent->prompt) { + sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, + expr_alloc_and(parent->prompt->visible.expr, + expr_alloc_symbol(&symbol_mod))); + } +} + +bool menu_is_visible(struct menu *menu) +{ + struct menu *child; + struct symbol *sym; + tristate visible; + + if (!menu->prompt) + return false; + sym = menu->sym; + if (sym) { + sym_calc_value(sym); + visible = menu->prompt->visible.tri; + } else + visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); + + if (visible != no) + return true; + if (!sym || sym_get_tristate_value(menu->sym) == no) + return false; + + for (child = menu->list; child; child = child->next) + if (menu_is_visible(child)) + return true; + return false; +} + +const char *menu_get_prompt(struct menu *menu) +{ + if (menu->prompt) + return menu->prompt->text; + else if (menu->sym) + return menu->sym->name; + return NULL; +} + +struct menu *menu_get_root_menu(struct menu *menu) +{ + return &rootmenu; +} + +struct menu *menu_get_parent_menu(struct menu *menu) +{ + enum prop_type type; + + for (; menu != &rootmenu; menu = menu->parent) { + type = menu->prompt ? menu->prompt->type : 0; + if (type == P_MENU) + break; + } + return menu; +} + +struct file *file_lookup(const char *name) +{ + struct file *file; + + for (file = file_list; file; file = file->next) { + if (!strcmp(name, file->name)) + return file; + } + + file = malloc(sizeof(*file)); + memset(file, 0, sizeof(*file)); + file->name = strdup(name); + file->next = file_list; + file_list = file; + return file; +} + +int file_write_dep(const char *name) +{ + struct file *file; + FILE *out; + + if (!name) + name = ".config.cmd"; + out = fopen(".config.tmp", "w"); + if (!out) + return 1; + fprintf(out, "deps_config := \\\n"); + for (file = file_list; file; file = file->next) { + if (file->next) + fprintf(out, "\t%s \\\n", file->name); + else + fprintf(out, "\t%s\n", file->name); + } + fprintf(out, "\n.config include/config.h: $(deps_config)\n\n$(deps_config):\n"); + fclose(out); + rename(".config.tmp", name); + return 0; +} + diff --git a/busybox/scripts/config/menubox.c b/busybox/scripts/config/menubox.c new file mode 100644 index 000000000..431f09fc9 --- /dev/null +++ b/busybox/scripts/config/menubox.c @@ -0,0 +1,436 @@ +/* + * menubox.c -- implements the menu box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Changes by Clifford Wolf (god@clifford.at) + * + * [ 1998-06-13 ] + * + * *) A bugfix for the Page-Down problem + * + * *) Formerly when I used Page Down and Page Up, the cursor would be set + * to the first position in the menu box. Now lxdialog is a bit + * smarter and works more like other menu systems (just have a look at + * it). + * + * *) Formerly if I selected something my scrolling would be broken because + * lxdialog is re-invoked by the Menuconfig shell script, can't + * remember the last scrolling position, and just sets it so that the + * cursor is at the bottom of the box. Now it writes the temporary file + * lxdialog.scrltmp which contains this information. The file is + * deleted by lxdialog if the user leaves a submenu or enters a new + * one, but it would be nice if Menuconfig could make another "rm -f" + * just to be sure. Just try it out - you will recognise a difference! + * + * [ 1998-06-14 ] + * + * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files + * and menus change their size on the fly. + * + * *) If for some reason the last scrolling position is not saved by + * lxdialog, it sets the scrolling so that the selected item is in the + * middle of the menu box, not at the bottom. + * + * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net) + * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus. + * This fixes a bug in Menuconfig where using ' ' to descend into menus + * would leave mis-synchronized lxdialog.scrltmp files lying around, + * fscanf would read in 'scroll', and eventually that value would get used. + */ + +#include "dialog.h" + +static int menu_width, item_x; + +/* + * Print menu item + */ +static void +print_item (WINDOW * win, const char *item, int choice, int selected, int hotkey) +{ + int j; + char menu_item[menu_width+1]; + + strncpy(menu_item, item, menu_width); + menu_item[menu_width] = 0; + j = first_alpha(menu_item, "YyNnMm"); + + /* Clear 'residue' of last item */ + wattrset (win, menubox_attr); + wmove (win, choice, 0); +#if OLD_NCURSES + { + int i; + for (i = 0; i < menu_width; i++) + waddch (win, ' '); + } +#else + wclrtoeol(win); +#endif + wattrset (win, selected ? item_selected_attr : item_attr); + mvwaddstr (win, choice, item_x, menu_item); + if (hotkey) { + wattrset (win, selected ? tag_key_selected_attr : tag_key_attr); + mvwaddch(win, choice, item_x+j, menu_item[j]); + } + if (selected) { + wmove (win, choice, item_x+1); + wrefresh (win); + } +} + +/* + * Print the scroll indicators. + */ +static void +print_arrows (WINDOW * win, int item_no, int scroll, + int y, int x, int height) +{ + int cur_y, cur_x; + + getyx(win, cur_y, cur_x); + + wmove(win, y, x); + + if (scroll > 0) { + wattrset (win, uarrow_attr); + waddch (win, ACS_UARROW); + waddstr (win, "(-)"); + } + else { + wattrset (win, menubox_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + height < item_no)) { + wattrset (win, darrow_attr); + waddch (win, ACS_DARROW); + waddstr (win, "(+)"); + } + else { + wattrset (win, menubox_border_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + wmove(win, cur_y, cur_x); +} + +/* + * Display the termination buttons. + */ +static void +print_buttons (WINDOW *win, int height, int width, int selected) +{ + int x = width / 2 - 16; + int y = height - 2; + + print_button (win, "Select", y, x, selected == 0); + print_button (win, " Exit ", y, x + 12, selected == 1); + print_button (win, " Help ", y, x + 24, selected == 2); + + wmove(win, y, x+1+12*selected); + wrefresh (win); +} + +/* + * Display a menu for choosing among a number of options + */ +int +dialog_menu (const char *title, const char *prompt, int height, int width, + int menu_height, const char *current, int item_no, + struct dialog_list_item ** items) +{ + int i, j, x, y, box_x, box_y; + int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice; + WINDOW *dialog, *menu; + FILE *f; + + max_choice = MIN (menu_height, item_no); + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + wbkgdset (dialog, dialog_attr & A_COLOR); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + menu_width = width - 6; + box_y = height - menu_height - 5; + box_x = (width - menu_width) / 2 - 1; + + /* create new window for the menu */ + menu = subwin (dialog, menu_height, menu_width, + y + box_y + 1, x + box_x + 1); + keypad (menu, TRUE); + + /* draw a box around the menu items */ + draw_box (dialog, box_y, box_x, menu_height + 2, menu_width + 2, + menubox_border_attr, menubox_attr); + + /* + * Find length of longest item in order to center menu. + * Set 'choice' to default item. + */ + item_x = 0; + for (i = 0; i < item_no; i++) { + item_x = MAX (item_x, MIN(menu_width, strlen (items[i]->name) + 2)); + if (strcmp(current, items[i]->tag) == 0) choice = i; + } + + item_x = (menu_width - item_x) / 2; + + /* get the scroll info from the temp file */ + if ( (f=fopen("lxdialog.scrltmp","r")) != NULL ) { + if ( (fscanf(f,"%d\n",&scroll) == 1) && (scroll <= choice) && + (scroll+max_choice > choice) && (scroll >= 0) && + (scroll+max_choice <= item_no) ) { + first_item = scroll; + choice = choice - scroll; + fclose(f); + } else { + scroll=0; + remove("lxdialog.scrltmp"); + fclose(f); + f=NULL; + } + } + if ( (choice >= max_choice) || (f==NULL && choice >= max_choice/2) ) { + if (choice >= item_no-max_choice/2) + scroll = first_item = item_no-max_choice; + else + scroll = first_item = choice - max_choice/2; + choice = choice - scroll; + } + + /* Print the menu */ + for (i=0; i < max_choice; i++) { + print_item (menu, items[first_item + i]->name, i, i == choice, + (items[first_item + i]->tag[0] != ':')); + } + + wnoutrefresh (menu); + + print_arrows(dialog, item_no, scroll, + box_y, box_x+item_x+1, menu_height); + + print_buttons (dialog, height, width, 0); + wmove (menu, choice, item_x+1); + wrefresh (menu); + + while (key != ESC) { + key = wgetch(menu); + + if (key < 256 && isalpha(key)) key = tolower(key); + + if (strchr("ynm", key)) + i = max_choice; + else { + for (i = choice+1; i < max_choice; i++) { + j = first_alpha(items[scroll + i]->name, "YyNnMm>"); + if (key == tolower(items[scroll + i]->name[j])) + break; + } + if (i == max_choice) + for (i = 0; i < max_choice; i++) { + j = first_alpha(items[scroll + i]->name, "YyNnMm>"); + if (key == tolower(items[scroll + i]->name[j])) + break; + } + } + + if (i < max_choice || + key == KEY_UP || key == KEY_DOWN || + key == '-' || key == '+' || + key == KEY_PPAGE || key == KEY_NPAGE) { + + print_item (menu, items[scroll + choice]->name, choice, FALSE, + (items[scroll + choice]->tag[0] != ':')); + + if (key == KEY_UP || key == '-') { + if (choice < 2 && scroll) { + /* Scroll menu down */ + scrollok (menu, TRUE); + wscrl (menu, -1); + scrollok (menu, FALSE); + + scroll--; + + print_item (menu, items[scroll]->name, 0, FALSE, + (items[scroll]->tag[0] != ':')); + } else + choice = MAX(choice - 1, 0); + + } else if (key == KEY_DOWN || key == '+') { + + print_item (menu, items[scroll + choice]->name, choice, FALSE, + (items[scroll + choice]->tag[0] != ':')); + + if ((choice > max_choice-3) && + (scroll + max_choice < item_no) + ) { + /* Scroll menu up */ + scrollok (menu, TRUE); + scroll (menu); + scrollok (menu, FALSE); + + scroll++; + + print_item (menu, items[scroll + max_choice - 1]->name, + max_choice-1, FALSE, + (items[scroll + max_choice - 1]->tag[0] != ':')); + } else + choice = MIN(choice+1, max_choice-1); + + } else if (key == KEY_PPAGE) { + scrollok (menu, TRUE); + for (i=0; (i < max_choice); i++) { + if (scroll > 0) { + wscrl (menu, -1); + scroll--; + print_item (menu, items[scroll]->name, 0, FALSE, + (items[scroll]->tag[0] != ':')); + } else { + if (choice > 0) + choice--; + } + } + scrollok (menu, FALSE); + + } else if (key == KEY_NPAGE) { + for (i=0; (i < max_choice); i++) { + if (scroll+max_choice < item_no) { + scrollok (menu, TRUE); + scroll(menu); + scrollok (menu, FALSE); + scroll++; + print_item (menu, items[scroll + max_choice - 1]->name, + max_choice-1, FALSE, + (items[scroll + max_choice - 1]->tag[0] != ':')); + } else { + if (choice+1 < max_choice) + choice++; + } + } + + } else + choice = i; + + print_item (menu, items[scroll + choice]->name, choice, TRUE, + (items[scroll + choice]->tag[0] != ':')); + + print_arrows(dialog, item_no, scroll, + box_y, box_x+item_x+1, menu_height); + + wnoutrefresh (dialog); + wrefresh (menu); + + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_LEFT: + case TAB: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 2 : (button > 2 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (menu); + break; + case ' ': + case 's': + case 'y': + case 'n': + case 'm': + /* save scroll info */ + if ( (f=fopen("lxdialog.scrltmp","w")) != NULL ) { + fprintf(f,"%d\n",scroll); + fclose(f); + } + delwin (dialog); + items[scroll + choice]->selected = 1; + switch (key) { + case 's': return 3; + case 'y': return 3; + case 'n': return 4; + case 'm': return 5; + case ' ': return 6; + } + return 0; + case 'h': + case '?': + button = 2; + case '\n': + delwin (dialog); + items[scroll + choice]->selected = 1; + + remove("lxdialog.scrltmp"); + return button; + case 'e': + case 'x': + key = ESC; + case ESC: + break; + } + } + + delwin (dialog); + remove("lxdialog.scrltmp"); + return -1; /* ESC pressed */ +} diff --git a/busybox/scripts/config/msgbox.c b/busybox/scripts/config/msgbox.c new file mode 100644 index 000000000..93692e1fb --- /dev/null +++ b/busybox/scripts/config/msgbox.c @@ -0,0 +1,85 @@ +/* + * msgbox.c -- implements the message box and info box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +/* + * Display a message box. Program will pause and display an "OK" button + * if the parameter 'pause' is non-zero. + */ +int +dialog_msgbox (const char *title, const char *prompt, int height, int width, + int pause) +{ + int i, x, y, key = 0; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 2); + + if (pause) { + wattrset (dialog, border_attr); + mvwaddch (dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + print_button (dialog, " Ok ", + height - 2, width / 2 - 4, TRUE); + + wrefresh (dialog); + while (key != ESC && key != '\n' && key != ' ' && + key != 'O' && key != 'o' && key != 'X' && key != 'x') + key = wgetch (dialog); + } else { + key = '\n'; + wrefresh (dialog); + } + + delwin (dialog); + return key == ESC ? -1 : 0; +} diff --git a/busybox/scripts/config/symbol.c b/busybox/scripts/config/symbol.c new file mode 100644 index 000000000..a9fae9c13 --- /dev/null +++ b/busybox/scripts/config/symbol.c @@ -0,0 +1,771 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +struct symbol symbol_yes = { + .name = "y", + .curr = { "y", yes }, + .flags = SYMBOL_YES|SYMBOL_VALID, +}, symbol_mod = { + .name = "m", + .curr = { "m", mod }, + .flags = SYMBOL_MOD|SYMBOL_VALID, +}, symbol_no = { + .name = "n", + .curr = { "n", no }, + .flags = SYMBOL_NO|SYMBOL_VALID, +}, symbol_empty = { + .name = "", + .curr = { "", no }, + .flags = SYMBOL_VALID, +}; + +int sym_change_count; +struct symbol *modules_sym; +tristate modules_val; + +void sym_add_default(struct symbol *sym, const char *def) +{ + struct property *prop = prop_alloc(P_DEFAULT, sym); + + prop->expr = expr_alloc_symbol(sym_lookup(def, 1)); +} + +void sym_init(void) +{ + struct symbol *sym; + char *p; + static bool inited = false; + + if (inited) + return; + inited = true; + + sym = sym_lookup("VERSION", 0); + sym->type = S_STRING; + sym->flags |= SYMBOL_AUTO; + p = getenv("VERSION"); + if (p) + sym_add_default(sym, p); + + sym = sym_lookup("TARGET_ARCH", 0); + sym->type = S_STRING; + sym->flags |= SYMBOL_AUTO; + p = getenv("TARGET_ARCH"); + if (p) + sym_add_default(sym, p); + +} + +enum symbol_type sym_get_type(struct symbol *sym) +{ + enum symbol_type type = sym->type; + + if (type == S_TRISTATE) { + if (sym_is_choice_value(sym) && sym->visible == yes) + type = S_BOOLEAN; + else if (modules_val == no) + type = S_BOOLEAN; + } + return type; +} + +const char *sym_type_name(enum symbol_type type) +{ + switch (type) { + case S_BOOLEAN: + return "boolean"; + case S_TRISTATE: + return "tristate"; + case S_INT: + return "integer"; + case S_HEX: + return "hex"; + case S_STRING: + return "string"; + case S_UNKNOWN: + return "unknown"; + case S_OTHER: + break; + } + return "???"; +} + +struct property *sym_get_choice_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_choices(sym, prop) + return prop; + return NULL; +} + +struct property *sym_get_default_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +struct property *sym_get_range_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_RANGE) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static void sym_calc_visibility(struct symbol *sym) +{ + struct property *prop; + tristate tri; + + /* any prompt visible? */ + tri = no; + for_all_prompts(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + tri = E_OR(tri, prop->visible.tri); + } + if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) + tri = yes; + if (sym->visible != tri) { + sym->visible = tri; + sym_set_changed(sym); + } + if (sym_is_choice_value(sym)) + return; + tri = no; + if (sym->rev_dep.expr) + tri = expr_calc_value(sym->rev_dep.expr); + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) + tri = yes; + if (sym->rev_dep.tri != tri) { + sym->rev_dep.tri = tri; + sym_set_changed(sym); + } +} + +static struct symbol *sym_calc_choice(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + + /* is the user choice visible? */ + def_sym = sym->user.val; + if (def_sym) { + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* any of the defaults visible? */ + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri == no) + continue; + def_sym = prop_get_symbol(prop); + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* just get the first visible value */ + prop = sym_get_choice_prop(sym); + for (e = prop->expr; e; e = e->left.expr) { + def_sym = e->right.sym; + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* no choice? reset tristate value */ + sym->curr.tri = no; + return NULL; +} + +void sym_calc_value(struct symbol *sym) +{ + struct symbol_value newval, oldval; + struct property *prop; + struct expr *e; + + if (!sym) + return; + + if (sym->flags & SYMBOL_VALID) + return; + sym->flags |= SYMBOL_VALID; + + oldval = sym->curr; + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + newval = symbol_empty.curr; + break; + case S_BOOLEAN: + case S_TRISTATE: + newval = symbol_no.curr; + break; + default: + sym->curr.val = sym->name; + sym->curr.tri = no; + return; + } + if (!sym_is_choice_value(sym)) + sym->flags &= ~SYMBOL_WRITE; + + sym_calc_visibility(sym); + + /* set default if recursively called */ + sym->curr = newval; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_is_choice_value(sym) && sym->visible == yes) { + prop = sym_get_choice_prop(sym); + newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; + } else if (E_OR(sym->visible, sym->rev_dep.tri) != no) { + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) + newval.tri = sym->user.tri; + else if (!sym_is_choice(sym)) { + prop = sym_get_default_prop(sym); + if (prop) + newval.tri = expr_calc_value(prop->expr); + } + newval.tri = E_OR(E_AND(newval.tri, sym->visible), sym->rev_dep.tri); + } else if (!sym_is_choice(sym)) { + prop = sym_get_default_prop(sym); + if (prop) { + sym->flags |= SYMBOL_WRITE; + newval.tri = expr_calc_value(prop->expr); + } + } + if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN) + newval.tri = yes; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (sym->visible != no) { + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.val = sym->user.val; + break; + } + } + prop = sym_get_default_prop(sym); + if (prop) { + struct symbol *ds = prop_get_symbol(prop); + if (ds) { + sym->flags |= SYMBOL_WRITE; + sym_calc_value(ds); + newval.val = ds->curr.val; + } + } + break; + default: + ; + } + + sym->curr = newval; + if (sym_is_choice(sym) && newval.tri == yes) + sym->curr.val = sym_calc_choice(sym); + + if (memcmp(&oldval, &sym->curr, sizeof(oldval))) + sym_set_changed(sym); + if (modules_sym == sym) + modules_val = modules_sym->curr.tri; + + if (sym_is_choice(sym)) { + int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE); + prop = sym_get_choice_prop(sym); + for (e = prop->expr; e; e = e->left.expr) { + e->right.sym->flags |= flags; + if (flags & SYMBOL_CHANGED) + sym_set_changed(e->right.sym); + } + } +} + +void sym_clear_all_valid(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym->flags &= ~SYMBOL_VALID; + sym_change_count++; + if (modules_sym) + sym_calc_value(modules_sym); +} + +void sym_set_changed(struct symbol *sym) +{ + struct property *prop; + + sym->flags |= SYMBOL_CHANGED; + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu) + prop->menu->flags |= MENU_CHANGED; + } +} + +void sym_set_all_changed(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym_set_changed(sym); +} + +bool sym_tristate_within_range(struct symbol *sym, tristate val) +{ + int type = sym_get_type(sym); + + if (sym->visible == no) + return false; + + if (type != S_BOOLEAN && type != S_TRISTATE) + return false; + + if (type == S_BOOLEAN && val == mod) + return false; + if (sym->visible <= sym->rev_dep.tri) + return false; + if (sym_is_choice_value(sym) && sym->visible == yes) + return val == yes; + return val >= sym->rev_dep.tri && val <= sym->visible; +} + +bool sym_set_tristate_value(struct symbol *sym, tristate val) +{ + tristate oldval = sym_get_tristate_value(sym); + + if (oldval != val && !sym_tristate_within_range(sym, val)) + return false; + + if (sym->flags & SYMBOL_NEW) { + sym->flags &= ~SYMBOL_NEW; + sym_set_changed(sym); + } + if (sym_is_choice_value(sym) && val == yes) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + + cs->user.val = sym; + cs->flags &= ~SYMBOL_NEW; + } + + sym->user.tri = val; + if (oldval != val) { + sym_clear_all_valid(); + if (sym == modules_sym) + sym_set_all_changed(); + } + + return true; +} + +tristate sym_toggle_tristate_value(struct symbol *sym) +{ + tristate oldval, newval; + + oldval = newval = sym_get_tristate_value(sym); + do { + switch (newval) { + case no: + newval = mod; + break; + case mod: + newval = yes; + break; + case yes: + newval = no; + break; + } + if (sym_set_tristate_value(sym, newval)) + break; + } while (oldval != newval); + return newval; +} + +bool sym_string_valid(struct symbol *sym, const char *str) +{ + char ch; + + switch (sym->type) { + case S_STRING: + return true; + case S_INT: + ch = *str++; + if (ch == '-') + ch = *str++; + if (!isdigit(ch)) + return false; + if (ch == '0' && *str != 0) + return false; + while ((ch = *str++)) { + if (!isdigit(ch)) + return false; + } + return true; + case S_HEX: + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + str += 2; + ch = *str++; + do { + if (!isxdigit(ch)) + return false; + } while ((ch = *str++)); + return true; + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + case 'm': case 'M': + case 'n': case 'N': + return true; + } + return false; + default: + return false; + } +} + +bool sym_string_within_range(struct symbol *sym, const char *str) +{ + struct property *prop; + int val; + + switch (sym->type) { + case S_STRING: + return sym_string_valid(sym, str); + case S_INT: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtol(str, NULL, 10); + return val >= strtol(prop->expr->left.sym->name, NULL, 10) && + val <= strtol(prop->expr->right.sym->name, NULL, 10); + case S_HEX: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtol(str, NULL, 16); + return val >= strtol(prop->expr->left.sym->name, NULL, 16) && + val <= strtol(prop->expr->right.sym->name, NULL, 16); + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + return sym_tristate_within_range(sym, yes); + case 'm': case 'M': + return sym_tristate_within_range(sym, mod); + case 'n': case 'N': + return sym_tristate_within_range(sym, no); + } + return false; + default: + return false; + } +} + +bool sym_set_string_value(struct symbol *sym, const char *newval) +{ + const char *oldval; + char *val; + int size; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (newval[0]) { + case 'y': case 'Y': + return sym_set_tristate_value(sym, yes); + case 'm': case 'M': + return sym_set_tristate_value(sym, mod); + case 'n': case 'N': + return sym_set_tristate_value(sym, no); + } + return false; + default: + ; + } + + if (!sym_string_within_range(sym, newval)) + return false; + + if (sym->flags & SYMBOL_NEW) { + sym->flags &= ~SYMBOL_NEW; + sym_set_changed(sym); + } + + oldval = sym->user.val; + size = strlen(newval) + 1; + if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { + size += 2; + sym->user.val = val = malloc(size); + *val++ = '0'; + *val++ = 'x'; + } else if (!oldval || strcmp(oldval, newval)) + sym->user.val = val = malloc(size); + else + return true; + + strcpy(val, newval); + free((void *)oldval); + sym_clear_all_valid(); + + return true; +} + +const char *sym_get_string_value(struct symbol *sym) +{ + tristate val; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + return "n"; + case mod: + return "m"; + case yes: + return "y"; + } + break; + default: + ; + } + return (const char *)sym->curr.val; +} + +bool sym_is_changable(struct symbol *sym) +{ + return sym->visible > sym->rev_dep.tri; +} + +struct symbol *sym_lookup(const char *name, int isconst) +{ + struct symbol *symbol; + const char *ptr; + char *new_name; + int hash = 0; + + if (name) { + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + for (ptr = name; *ptr; ptr++) + hash += *ptr; + hash &= 0xff; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (!strcmp(symbol->name, name)) { + if ((isconst && symbol->flags & SYMBOL_CONST) || + (!isconst && !(symbol->flags & SYMBOL_CONST))) + return symbol; + } + } + new_name = strdup(name); + } else { + new_name = NULL; + hash = 256; + } + + symbol = malloc(sizeof(*symbol)); + memset(symbol, 0, sizeof(*symbol)); + symbol->name = new_name; + symbol->type = S_UNKNOWN; + symbol->flags = SYMBOL_NEW; + if (isconst) + symbol->flags |= SYMBOL_CONST; + + symbol->next = symbol_hash[hash]; + symbol_hash[hash] = symbol; + + return symbol; +} + +struct symbol *sym_find(const char *name) +{ + struct symbol *symbol = NULL; + const char *ptr; + int hash = 0; + + if (!name) + return NULL; + + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + for (ptr = name; *ptr; ptr++) + hash += *ptr; + hash &= 0xff; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (!strcmp(symbol->name, name) && + !(symbol->flags & SYMBOL_CONST)) + break; + } + + return symbol; +} + +struct symbol *sym_check_deps(struct symbol *sym); + +static struct symbol *sym_check_expr_deps(struct expr *e) +{ + struct symbol *sym; + + if (!e) + return NULL; + switch (e->type) { + case E_OR: + case E_AND: + sym = sym_check_expr_deps(e->left.expr); + if (sym) + return sym; + return sym_check_expr_deps(e->right.expr); + case E_NOT: + return sym_check_expr_deps(e->left.expr); + case E_EQUAL: + case E_UNEQUAL: + sym = sym_check_deps(e->left.sym); + if (sym) + return sym; + return sym_check_deps(e->right.sym); + case E_SYMBOL: + return sym_check_deps(e->left.sym); + default: + break; + } + printf("Oops! How to check %d?\n", e->type); + return NULL; +} + +struct symbol *sym_check_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + + if (sym->flags & SYMBOL_CHECK_DONE) + return NULL; + if (sym->flags & SYMBOL_CHECK) { + printf("Warning! Found recursive dependency: %s", sym->name); + return sym; + } + + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_expr_deps(sym->rev_dep.expr); + if (sym2) + goto out; + + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->type == P_CHOICE || prop->type == P_SELECT) + continue; + sym2 = sym_check_expr_deps(prop->visible.expr); + if (sym2) + goto out; + if (prop->type != P_DEFAULT || sym_is_choice(sym)) + continue; + sym2 = sym_check_expr_deps(prop->expr); + if (sym2) + goto out; + } +out: + if (sym2) + printf(" %s", sym->name); + sym->flags &= ~SYMBOL_CHECK; + return sym2; +} + +struct property *prop_alloc(enum prop_type type, struct symbol *sym) +{ + struct property *prop; + struct property **propp; + + prop = malloc(sizeof(*prop)); + memset(prop, 0, sizeof(*prop)); + prop->type = type; + prop->sym = sym; + prop->file = current_file; + prop->lineno = zconf_lineno(); + + /* append property to the prop list of symbol */ + if (sym) { + for (propp = &sym->prop; *propp; propp = &(*propp)->next) + ; + *propp = prop; + } + + return prop; +} + +struct symbol *prop_get_symbol(struct property *prop) +{ + if (prop->expr && (prop->expr->type == E_SYMBOL || + prop->expr->type == E_CHOICE)) + return prop->expr->left.sym; + return NULL; +} + +const char *prop_get_type_name(enum prop_type type) +{ + switch (type) { + case P_PROMPT: + return "prompt"; + case P_COMMENT: + return "comment"; + case P_MENU: + return "menu"; + case P_DEFAULT: + return "default"; + case P_CHOICE: + return "choice"; + case P_SELECT: + return "select"; + case P_RANGE: + return "range"; + case P_UNKNOWN: + break; + } + return "unknown"; +} diff --git a/busybox/scripts/config/textbox.c b/busybox/scripts/config/textbox.c new file mode 100644 index 000000000..a5a460b5c --- /dev/null +++ b/busybox/scripts/config/textbox.c @@ -0,0 +1,556 @@ +/* + * textbox.c -- implements the text box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +static void back_lines (int n); +static void print_page (WINDOW * win, int height, int width); +static void print_line (WINDOW * win, int row, int width); +static char *get_line (void); +static void print_position (WINDOW * win, int height, int width); + +static int hscroll, fd, file_size, bytes_read; +static int begin_reached = 1, end_reached, page_length; +static char *buf, *page; + +/* + * Display text from a file in a dialog box. + */ +int +dialog_textbox (const char *title, const char *file, int height, int width) +{ + int i, x, y, cur_x, cur_y, fpos, key = 0; + int passed_end; + char search_term[MAX_LEN + 1]; + WINDOW *dialog, *text; + + search_term[0] = '\0'; /* no search term entered yet */ + + /* Open input file for reading */ + if ((fd = open (file, O_RDONLY)) == -1) { + endwin (); + fprintf (stderr, + "\nCan't open input file in dialog_textbox().\n"); + exit (-1); + } + /* Get file size. Actually, 'file_size' is the real file size - 1, + since it's only the last byte offset from the beginning */ + if ((file_size = lseek (fd, 0, SEEK_END)) == -1) { + endwin (); + fprintf (stderr, "\nError getting file size in dialog_textbox().\n"); + exit (-1); + } + /* Restore file pointer to beginning of file after getting file size */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + /* Allocate space for read buffer */ + if ((buf = malloc (BUF_SIZE + 1)) == NULL) { + endwin (); + fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; /* mark end of valid data */ + page = buf; /* page is pointer to start of page to be displayed */ + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + /* Create window for text region, used for scrolling text */ + text = subwin (dialog, height - 4, width - 2, y + 1, x + 1); + wattrset (text, dialog_attr); + wbkgdset (text, dialog_attr & A_COLOR); + + keypad (text, TRUE); + + /* register the new window, along with its borders */ + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + wbkgdset (dialog, dialog_attr & A_COLOR); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE); + wnoutrefresh (dialog); + getyx (dialog, cur_y, cur_x); /* Save cursor position */ + + /* Print first page of text */ + attr_clear (text, height - 4, width - 2, dialog_attr); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + + while ((key != ESC) && (key != '\n')) { + key = wgetch (dialog); + switch (key) { + case 'E': /* Exit */ + case 'e': + case 'X': + case 'x': + delwin (dialog); + free (buf); + close (fd); + return 0; + case 'g': /* First page */ + case KEY_HOME: + if (!begin_reached) { + begin_reached = 1; + /* First page not in buffer? */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if (fpos > bytes_read) { /* Yes, we have to read it in */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, + "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } + page = buf; + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case 'G': /* Last page */ + case KEY_END: + + end_reached = 1; + /* Last page not in buffer? */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if (fpos < file_size) { /* Yes, we have to read it in */ + if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, + "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } + page = buf + bytes_read; + back_lines (height - 4); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + break; + case 'K': /* Previous line */ + case 'k': + case KEY_UP: + if (!begin_reached) { + back_lines (page_length + 1); + + /* We don't call print_page() here but use scrolling to ensure + faster screen update. However, 'end_reached' and + 'page_length' should still be updated, and 'page' should + point to start of next page. This is done by calling + get_line() in the following 'for' loop. */ + scrollok (text, TRUE); + wscrl (text, -1); /* Scroll text region down one line */ + scrollok (text, FALSE); + page_length = 0; + passed_end = 0; + for (i = 0; i < height - 4; i++) { + if (!i) { + /* print first line of page */ + print_line (text, 0, width - 2); + wnoutrefresh (text); + } else + /* Called to update 'end_reached' and 'page' */ + get_line (); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case 'B': /* Previous page */ + case 'b': + case KEY_PPAGE: + if (begin_reached) + break; + back_lines (page_length + height - 4); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case 'J': /* Next line */ + case 'j': + case KEY_DOWN: + if (!end_reached) { + begin_reached = 0; + scrollok (text, TRUE); + scroll (text); /* Scroll text region up one line */ + scrollok (text, FALSE); + print_line (text, height - 5, width - 2); + wnoutrefresh (text); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case KEY_NPAGE: /* Next page */ + case ' ': + if (end_reached) + break; + + begin_reached = 0; + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case '0': /* Beginning of line */ + case 'H': /* Scroll left */ + case 'h': + case KEY_LEFT: + if (hscroll <= 0) + break; + + if (key == '0') + hscroll = 0; + else + hscroll--; + /* Reprint current page to scroll horizontally */ + back_lines (page_length); + print_page (text, height - 4, width - 2); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case 'L': /* Scroll right */ + case 'l': + case KEY_RIGHT: + if (hscroll >= MAX_LEN) + break; + hscroll++; + /* Reprint current page to scroll horizontally */ + back_lines (page_length); + print_page (text, height - 4, width - 2); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case ESC: + break; + } + } + + delwin (dialog); + free (buf); + close (fd); + return 1; /* ESC pressed */ +} + +/* + * Go back 'n' lines in text file. Called by dialog_textbox(). + * 'page' will be updated to point to the desired line in 'buf'. + */ +static void +back_lines (int n) +{ + int i, fpos; + + begin_reached = 0; + /* We have to distinguish between end_reached and !end_reached + since at end of file, the line is not ended by a '\n'. + The code inside 'if' basically does a '--page' to move one + character backward so as to skip '\n' of the previous line */ + if (!end_reached) { + /* Either beginning of buffer or beginning of file reached? */ + if (page == buf) { + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "back_lines().\n"); + exit (-1); + } + if (fpos > bytes_read) { /* Not beginning of file yet */ + /* We've reached beginning of buffer, but not beginning of + file yet, so read previous part of file into buffer. + Note that we only move backward for BUF_SIZE/2 bytes, + but not BUF_SIZE bytes to avoid re-reading again in + print_page() later */ + /* Really possible to move backward BUF_SIZE/2 bytes? */ + if (fpos < BUF_SIZE / 2 + bytes_read) { + /* No, move less then */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "back_lines().\n"); + exit (-1); + } + page = buf + fpos - bytes_read; + } else { /* Move backward BUF_SIZE/2 bytes */ + if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) + == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer " + "in back_lines().\n"); + exit (-1); + } + page = buf + BUF_SIZE / 2; + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in back_lines().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } else { /* Beginning of file reached */ + begin_reached = 1; + return; + } + } + if (*(--page) != '\n') { /* '--page' here */ + /* Something's wrong... */ + endwin (); + fprintf (stderr, "\nInternal error in back_lines().\n"); + exit (-1); + } + } + /* Go back 'n' lines */ + for (i = 0; i < n; i++) + do { + if (page == buf) { + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in back_lines().\n"); + exit (-1); + } + if (fpos > bytes_read) { + /* Really possible to move backward BUF_SIZE/2 bytes? */ + if (fpos < BUF_SIZE / 2 + bytes_read) { + /* No, move less then */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer " + "in back_lines().\n"); + exit (-1); + } + page = buf + fpos - bytes_read; + } else { /* Move backward BUF_SIZE/2 bytes */ + if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), + SEEK_CUR) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer" + " in back_lines().\n"); + exit (-1); + } + page = buf + BUF_SIZE / 2; + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in " + "back_lines().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } else { /* Beginning of file reached */ + begin_reached = 1; + return; + } + } + } while (*(--page) != '\n'); + page++; +} + +/* + * Print a new page of text. Called by dialog_textbox(). + */ +static void +print_page (WINDOW * win, int height, int width) +{ + int i, passed_end = 0; + + page_length = 0; + for (i = 0; i < height; i++) { + print_line (win, i, width); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + wnoutrefresh (win); +} + +/* + * Print a new line of text. Called by dialog_textbox() and print_page(). + */ +static void +print_line (WINDOW * win, int row, int width) +{ + int y, x; + char *line; + + line = get_line (); + line += MIN (strlen (line), hscroll); /* Scroll horizontally */ + wmove (win, row, 0); /* move cursor to correct line */ + waddch (win, ' '); + waddnstr (win, line, MIN (strlen (line), width - 2)); + + getyx (win, y, x); + /* Clear 'residue' of previous line */ +#if OLD_NCURSES + { + int i; + for (i = 0; i < width - x; i++) + waddch (win, ' '); + } +#else + wclrtoeol(win); +#endif +} + +/* + * Return current line of text. Called by dialog_textbox() and print_line(). + * 'page' should point to start of current line before calling, and will be + * updated to point to start of next line. + */ +static char * +get_line (void) +{ + int i = 0, fpos; + static char line[MAX_LEN + 1]; + + end_reached = 0; + while (*page != '\n') { + if (*page == '\0') { + /* Either end of file or end of buffer reached */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "get_line().\n"); + exit (-1); + } + if (fpos < file_size) { /* Not end of file yet */ + /* We've reached end of buffer, but not end of file yet, + so read next part of file into buffer */ + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in get_line().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + page = buf; + } else { + if (!end_reached) + end_reached = 1; + break; + } + } else if (i < MAX_LEN) + line[i++] = *(page++); + else { + /* Truncate lines longer than MAX_LEN characters */ + if (i == MAX_LEN) + line[i++] = '\0'; + page++; + } + } + if (i <= MAX_LEN) + line[i] = '\0'; + if (!end_reached) + page++; /* move pass '\n' */ + + return line; +} + +/* + * Print current position + */ +static void +print_position (WINDOW * win, int height, int width) +{ + int fpos, percent; + + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in print_position().\n"); + exit (-1); + } + wattrset (win, position_indicator_attr); + wbkgdset (win, position_indicator_attr & A_COLOR); + percent = !file_size ? + 100 : ((fpos - bytes_read + page - buf) * 100) / file_size; + wmove (win, height - 3, width - 9); + wprintw (win, "(%3d%%)", percent); +} diff --git a/busybox/scripts/config/util.c b/busybox/scripts/config/util.c new file mode 100644 index 000000000..0a2f82757 --- /dev/null +++ b/busybox/scripts/config/util.c @@ -0,0 +1,375 @@ +/* + * util.c + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + + +/* use colors by default? */ +bool use_colors = 1; + +char *backtitle = NULL; + +const char *dialog_result; + +/* + * Attribute values, default is for mono display + */ +chtype attributes[] = +{ + A_NORMAL, /* screen_attr */ + A_NORMAL, /* shadow_attr */ + A_NORMAL, /* dialog_attr */ + A_BOLD, /* title_attr */ + A_NORMAL, /* border_attr */ + A_REVERSE, /* button_active_attr */ + A_DIM, /* button_inactive_attr */ + A_REVERSE, /* button_key_active_attr */ + A_BOLD, /* button_key_inactive_attr */ + A_REVERSE, /* button_label_active_attr */ + A_NORMAL, /* button_label_inactive_attr */ + A_NORMAL, /* inputbox_attr */ + A_NORMAL, /* inputbox_border_attr */ + A_NORMAL, /* searchbox_attr */ + A_BOLD, /* searchbox_title_attr */ + A_NORMAL, /* searchbox_border_attr */ + A_BOLD, /* position_indicator_attr */ + A_NORMAL, /* menubox_attr */ + A_NORMAL, /* menubox_border_attr */ + A_NORMAL, /* item_attr */ + A_REVERSE, /* item_selected_attr */ + A_BOLD, /* tag_attr */ + A_REVERSE, /* tag_selected_attr */ + A_BOLD, /* tag_key_attr */ + A_REVERSE, /* tag_key_selected_attr */ + A_BOLD, /* check_attr */ + A_REVERSE, /* check_selected_attr */ + A_BOLD, /* uarrow_attr */ + A_BOLD /* darrow_attr */ +}; + + +#include "colors.h" + +/* + * Table of color values + */ +int color_table[][3] = +{ + {SCREEN_FG, SCREEN_BG, SCREEN_HL}, + {SHADOW_FG, SHADOW_BG, SHADOW_HL}, + {DIALOG_FG, DIALOG_BG, DIALOG_HL}, + {TITLE_FG, TITLE_BG, TITLE_HL}, + {BORDER_FG, BORDER_BG, BORDER_HL}, + {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL}, + {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL}, + {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL}, + {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL}, + {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL}, + {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG, + BUTTON_LABEL_INACTIVE_HL}, + {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL}, + {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL}, + {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL}, + {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL}, + {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL}, + {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL}, + {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL}, + {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL}, + {ITEM_FG, ITEM_BG, ITEM_HL}, + {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL}, + {TAG_FG, TAG_BG, TAG_HL}, + {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL}, + {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL}, + {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL}, + {CHECK_FG, CHECK_BG, CHECK_HL}, + {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL}, + {UARROW_FG, UARROW_BG, UARROW_HL}, + {DARROW_FG, DARROW_BG, DARROW_HL}, +}; /* color_table */ + +/* + * Set window to attribute 'attr' + */ +void +attr_clear (WINDOW * win, int height, int width, chtype attr) +{ + int i, j; + + wattrset (win, attr); + for (i = 0; i < height; i++) { + wmove (win, i, 0); + for (j = 0; j < width; j++) + waddch (win, ' '); + } + touchwin (win); +} + +void dialog_clear (void) +{ + attr_clear (stdscr, LINES, COLS, screen_attr); + /* Display background title if it exists ... - SLH */ + if (backtitle != NULL) { + int i; + + wattrset (stdscr, screen_attr); + mvwaddstr (stdscr, 0, 1, (char *)backtitle); + wmove (stdscr, 1, 1); + for (i = 1; i < COLS - 1; i++) + waddch (stdscr, ACS_HLINE); + } + wnoutrefresh (stdscr); +} + +/* + * Do some initialization for dialog + */ +void +init_dialog (void) +{ + initscr (); /* Init curses */ + keypad (stdscr, TRUE); + cbreak (); + noecho (); + + + if (use_colors) /* Set up colors */ + color_setup (); + + + dialog_clear (); +} + +/* + * Setup for color display + */ +void +color_setup (void) +{ + int i; + + if (has_colors ()) { /* Terminal supports color? */ + start_color (); + + /* Initialize color pairs */ + for (i = 0; i < ATTRIBUTE_COUNT; i++) + init_pair (i + 1, color_table[i][0], color_table[i][1]); + + /* Setup color attributes */ + for (i = 0; i < ATTRIBUTE_COUNT; i++) + attributes[i] = C_ATTR (color_table[i][2], i + 1); + } +} + +/* + * End using dialog functions. + */ +void +end_dialog (void) +{ + endwin (); +} + + +/* + * Print a string of text in a window, automatically wrap around to the + * next line if the string is too long to fit on one line. Newline + * characters '\n' are replaced by spaces. We start on a new line + * if there is no room for at least 4 nonblanks following a double-space. + */ +void +print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x) +{ + int newl, cur_x, cur_y; + int i, prompt_len, room, wlen; + char tempstr[MAX_LEN + 1], *word, *sp, *sp2; + + strcpy (tempstr, prompt); + + prompt_len = strlen(tempstr); + + /* + * Remove newlines + */ + for(i=0; i room || + (newl && wlen < 4 && sp && wlen+1+strlen(sp) > room + && (!(sp2 = index(sp, ' ')) || wlen+1+(sp2-sp) > room))) { + cur_y++; + cur_x = x; + } + wmove (win, cur_y, cur_x); + waddstr (win, word); + getyx (win, cur_y, cur_x); + cur_x++; + if (sp && *sp == ' ') { + cur_x++; /* double space */ + while (*++sp == ' '); + newl = 1; + } else + newl = 0; + word = sp; + } + } +} + +/* + * Print a button + */ +void +print_button (WINDOW * win, const char *label, int y, int x, int selected) +{ + int i, temp; + + wmove (win, y, x); + wattrset (win, selected ? button_active_attr : button_inactive_attr); + waddstr (win, "<"); + temp = strspn (label, " "); + label += temp; + wattrset (win, selected ? button_label_active_attr + : button_label_inactive_attr); + for (i = 0; i < temp; i++) + waddch (win, ' '); + wattrset (win, selected ? button_key_active_attr + : button_key_inactive_attr); + waddch (win, label[0]); + wattrset (win, selected ? button_label_active_attr + : button_label_inactive_attr); + waddstr (win, (char *)label + 1); + wattrset (win, selected ? button_active_attr : button_inactive_attr); + waddstr (win, ">"); + wmove (win, y, x + temp + 1); +} + +/* + * Draw a rectangular box with line drawing characters + */ +void +draw_box (WINDOW * win, int y, int x, int height, int width, + chtype box, chtype border) +{ + int i, j; + + wattrset (win, 0); + for (i = 0; i < height; i++) { + wmove (win, y + i, x); + for (j = 0; j < width; j++) + if (!i && !j) + waddch (win, border | ACS_ULCORNER); + else if (i == height - 1 && !j) + waddch (win, border | ACS_LLCORNER); + else if (!i && j == width - 1) + waddch (win, box | ACS_URCORNER); + else if (i == height - 1 && j == width - 1) + waddch (win, box | ACS_LRCORNER); + else if (!i) + waddch (win, border | ACS_HLINE); + else if (i == height - 1) + waddch (win, box | ACS_HLINE); + else if (!j) + waddch (win, border | ACS_VLINE); + else if (j == width - 1) + waddch (win, box | ACS_VLINE); + else + waddch (win, box | ' '); + } +} + +/* + * Draw shadows along the right and bottom edge to give a more 3D look + * to the boxes + */ +void +draw_shadow (WINDOW * win, int y, int x, int height, int width) +{ + int i; + + if (has_colors ()) { /* Whether terminal supports color? */ + wattrset (win, shadow_attr); + wmove (win, y + height, x + 2); + for (i = 0; i < width; i++) + waddch (win, winch (win) & A_CHARTEXT); + for (i = y + 1; i < y + height + 1; i++) { + wmove (win, i, x + width); + waddch (win, winch (win) & A_CHARTEXT); + waddch (win, winch (win) & A_CHARTEXT); + } + wnoutrefresh (win); + } +} + +/* + * Return the position of the first alphabetic character in a string. + */ +int +first_alpha(const char *string, const char *exempt) +{ + int i, in_paren=0, c; + + for (i = 0; i < strlen(string); i++) { + c = tolower(string[i]); + + if (strchr("<[(", c)) ++in_paren; + if (strchr(">])", c) && in_paren > 0) --in_paren; + + if ((! in_paren) && isalpha(c) && + strchr(exempt, c) == 0) + return i; + } + + return 0; +} + +/* + * Get the first selected item in the dialog_list_item list. + */ +struct dialog_list_item * +first_sel_item(int item_no, struct dialog_list_item ** items) +{ + int i; + + for (i = 0; i < item_no; i++) { + if (items[i]->selected) + return items[i]; + } + + return NULL; +} diff --git a/busybox/scripts/config/yesno.c b/busybox/scripts/config/yesno.c new file mode 100644 index 000000000..11fcc25f5 --- /dev/null +++ b/busybox/scripts/config/yesno.c @@ -0,0 +1,118 @@ +/* + * yesno.c -- implements the yes/no box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +/* + * Display termination buttons + */ +static void +print_buttons(WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 10; + int y = height - 2; + + print_button (dialog, " Yes ", y, x, selected == 0); + print_button (dialog, " No ", y, x + 13, selected == 1); + + wmove(dialog, y, x+1 + 13*selected ); + wrefresh (dialog); +} + +/* + * Display a dialog box with two buttons - Yes and No + */ +int +dialog_yesno (const char *title, const char *prompt, int height, int width) +{ + int i, x, y, key = 0, button = 0; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + print_buttons(dialog, height, width, 0); + + while (key != ESC) { + key = wgetch (dialog); + switch (key) { + case 'Y': + case 'y': + delwin (dialog); + return 0; + case 'N': + case 'n': + delwin (dialog); + return 1; + + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (dialog); + break; + case ' ': + case '\n': + delwin (dialog); + return button; + case ESC: + break; + } + } + + delwin (dialog); + return -1; /* ESC pressed */ +} diff --git a/busybox/scripts/config/zconf.l b/busybox/scripts/config/zconf.l new file mode 100644 index 000000000..55517b287 --- /dev/null +++ b/busybox/scripts/config/zconf.l @@ -0,0 +1,366 @@ +%option backup nostdinit noyywrap never-interactive full ecs +%option 8bit backup nodefault perf-report perf-report +%x COMMAND HELP STRING PARAM +%{ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define START_STRSIZE 16 + +char *text; +static char *text_ptr; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static struct buffer *zconf_endfile(void); + +void new_string(void) +{ + text = malloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_ptr = text; + text_size = 0; + *text_ptr = 0; +} + +void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + text = realloc(text, new_size); + text_asize = new_size; + text_ptr = text + text_size; + } + memcpy(text_ptr, str, size); + text_ptr += size; + text_size += size; + *text_ptr = 0; +} + +void alloc_string(const char *str, int size) +{ + text = malloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} +%} + +ws [ \n\t] +n [A-Za-z0-9_] + +%% + int str = 0; + int ts, i; + +[ \t]*#.*\n current_file->lineno++; +[ \t]*#.* + +[ \t]*\n current_file->lineno++; return T_EOL; + +[ \t]+ { + BEGIN(COMMAND); +} + +. { + unput(yytext[0]); + BEGIN(COMMAND); +} + + +{ + "mainmenu" BEGIN(PARAM); return T_MAINMENU; + "menu" BEGIN(PARAM); return T_MENU; + "endmenu" BEGIN(PARAM); return T_ENDMENU; + "source" BEGIN(PARAM); return T_SOURCE; + "choice" BEGIN(PARAM); return T_CHOICE; + "endchoice" BEGIN(PARAM); return T_ENDCHOICE; + "comment" BEGIN(PARAM); return T_COMMENT; + "config" BEGIN(PARAM); return T_CONFIG; + "menuconfig" BEGIN(PARAM); return T_MENUCONFIG; + "help" BEGIN(PARAM); return T_HELP; + "if" BEGIN(PARAM); return T_IF; + "endif" BEGIN(PARAM); return T_ENDIF; + "depends" BEGIN(PARAM); return T_DEPENDS; + "requires" BEGIN(PARAM); return T_REQUIRES; + "optional" BEGIN(PARAM); return T_OPTIONAL; + "default" BEGIN(PARAM); return T_DEFAULT; + "prompt" BEGIN(PARAM); return T_PROMPT; + "tristate" BEGIN(PARAM); return T_TRISTATE; + "def_tristate" BEGIN(PARAM); return T_DEF_TRISTATE; + "bool" BEGIN(PARAM); return T_BOOLEAN; + "boolean" BEGIN(PARAM); return T_BOOLEAN; + "def_bool" BEGIN(PARAM); return T_DEF_BOOLEAN; + "def_boolean" BEGIN(PARAM); return T_DEF_BOOLEAN; + "int" BEGIN(PARAM); return T_INT; + "hex" BEGIN(PARAM); return T_HEX; + "string" BEGIN(PARAM); return T_STRING; + "select" BEGIN(PARAM); return T_SELECT; + "enable" BEGIN(PARAM); return T_SELECT; + "range" BEGIN(PARAM); return T_RANGE; + {n}+ { + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + . + \n current_file->lineno++; BEGIN(INITIAL); +} + +{ + "&&" return T_AND; + "||" return T_OR; + "(" return T_OPEN_PAREN; + ")" return T_CLOSE_PAREN; + "!" return T_NOT; + "=" return T_EQUAL; + "!=" return T_UNEQUAL; + "if" return T_IF; + "on" return T_ON; + \"|\' { + str = yytext[0]; + new_string(); + BEGIN(STRING); + } + \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; + --- /* ignore */ + ({n}|[-/.])+ { + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + #.* /* comment */ + \\\n current_file->lineno++; + . + <> { + BEGIN(INITIAL); + } +} + +{ + [^'"\\\n]+/\n { + append_string(yytext, yyleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + [^'"\\\n]+ { + append_string(yytext, yyleng); + } + \\.?/\n { + append_string(yytext + 1, yyleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + \\.? { + append_string(yytext + 1, yyleng - 1); + } + \'|\" { + if (str == yytext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(yytext, 1); + } + \n { + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + <> { + BEGIN(INITIAL); + } +} + +{ + [ \t]+ { + ts = 0; + for (i = 0; i < yyleng; i++) { + if (yytext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + [ \t]*\n/[^ \t\n] { + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + [ \t]*\n { + current_file->lineno++; + append_string("\n", 1); + } + [^ \t\n].* { + append_string(yytext, yyleng); + if (!first_ts) + first_ts = last_ts; + } + <> { + zconf_endhelp(); + return T_HELPTEXT; + } +} + +<> { + if (current_buf) { + zconf_endfile(); + return T_EOF; + } + fclose(yyin); + yyterminate(); +} + +%% +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + yyin = zconf_fopen(name); + if (!yyin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = malloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; + current_file->flags = FILE_BUSY; +} + +void zconf_nextfile(const char *name) +{ + struct file *file = file_lookup(name); + struct buffer *buf = malloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + yyin = zconf_fopen(name); + if (!yyin) { + printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name); + exit(1); + } + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + if (file->flags & FILE_BUSY) { + printf("recursive scan (%s)?\n", name); + exit(1); + } + if (file->flags & FILE_SCANNED) { + printf("file %s already scanned?\n", name); + exit(1); + } + file->flags |= FILE_BUSY; + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static struct buffer *zconf_endfile(void) +{ + struct buffer *parent; + + current_file->flags |= FILE_SCANNED; + current_file->flags &= ~FILE_BUSY; + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; + + return parent; +} + +int zconf_lineno(void) +{ + if (current_buf) + return current_file->lineno - 1; + else + return 0; +} + +char *zconf_curname(void) +{ + if (current_buf) + return current_file->name; + else + return ""; +} diff --git a/busybox/scripts/config/zconf.tab.c_shipped b/busybox/scripts/config/zconf.tab.c_shipped new file mode 100644 index 000000000..a5f69a026 --- /dev/null +++ b/busybox/scripts/config/zconf.tab.c_shipped @@ -0,0 +1,2127 @@ +/* A Bison parser, made by GNU Bison 1.875a. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* If NAME_PREFIX is specified substitute the variables and functions + names. */ +#define yyparse zconfparse +#define yylex zconflex +#define yyerror zconferror +#define yylval zconflval +#define yychar zconfchar +#define yydebug zconfdebug +#define yynerrs zconfnerrs + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + T_MAINMENU = 258, + T_MENU = 259, + T_ENDMENU = 260, + T_SOURCE = 261, + T_CHOICE = 262, + T_ENDCHOICE = 263, + T_COMMENT = 264, + T_CONFIG = 265, + T_MENUCONFIG = 266, + T_HELP = 267, + T_HELPTEXT = 268, + T_IF = 269, + T_ENDIF = 270, + T_DEPENDS = 271, + T_REQUIRES = 272, + T_OPTIONAL = 273, + T_PROMPT = 274, + T_DEFAULT = 275, + T_TRISTATE = 276, + T_DEF_TRISTATE = 277, + T_BOOLEAN = 278, + T_DEF_BOOLEAN = 279, + T_STRING = 280, + T_INT = 281, + T_HEX = 282, + T_WORD = 283, + T_WORD_QUOTE = 284, + T_UNEQUAL = 285, + T_EOF = 286, + T_EOL = 287, + T_CLOSE_PAREN = 288, + T_OPEN_PAREN = 289, + T_ON = 290, + T_SELECT = 291, + T_RANGE = 292, + T_OR = 293, + T_AND = 294, + T_EQUAL = 295, + T_NOT = 296 + }; +#endif +#define T_MAINMENU 258 +#define T_MENU 259 +#define T_ENDMENU 260 +#define T_SOURCE 261 +#define T_CHOICE 262 +#define T_ENDCHOICE 263 +#define T_COMMENT 264 +#define T_CONFIG 265 +#define T_MENUCONFIG 266 +#define T_HELP 267 +#define T_HELPTEXT 268 +#define T_IF 269 +#define T_ENDIF 270 +#define T_DEPENDS 271 +#define T_REQUIRES 272 +#define T_OPTIONAL 273 +#define T_PROMPT 274 +#define T_DEFAULT 275 +#define T_TRISTATE 276 +#define T_DEF_TRISTATE 277 +#define T_BOOLEAN 278 +#define T_DEF_BOOLEAN 279 +#define T_STRING 280 +#define T_INT 281 +#define T_HEX 282 +#define T_WORD 283 +#define T_WORD_QUOTE 284 +#define T_UNEQUAL 285 +#define T_EOF 286 +#define T_EOL 287 +#define T_CLOSE_PAREN 288 +#define T_OPEN_PAREN 289 +#define T_ON 290 +#define T_SELECT 291 +#define T_RANGE 292 +#define T_OR 293 +#define T_AND 294 +#define T_EQUAL 295 +#define T_NOT 296 + + + + +/* Copy the first part of user declarations. */ + + +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(int token, int starttoken, int endtoken); + +struct symbol *symbol_hash[257]; + +#define YYERROR_VERBOSE + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) + +typedef union YYSTYPE { + int token; + char *string; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; +} YYSTYPE; +/* Line 191 of yacc.c. */ + +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +#define LKC_DIRECT_LINK +#include "lkc.h" + + +/* Line 214 of yacc.c. */ + + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# else +# ifndef YYSTACK_USE_ALLOCA +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC malloc +# define YYSTACK_FREE free +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 201 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 42 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 41 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 104 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 182 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 296 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned short yyprhs[] = +{ + 0, 0, 3, 4, 7, 9, 11, 13, 17, 19, + 21, 23, 26, 28, 30, 32, 34, 36, 38, 42, + 45, 49, 52, 53, 56, 59, 62, 65, 69, 74, + 78, 83, 87, 91, 95, 100, 105, 110, 116, 119, + 122, 124, 128, 131, 132, 135, 138, 141, 144, 149, + 153, 157, 160, 165, 166, 169, 173, 175, 179, 182, + 183, 186, 189, 192, 196, 199, 201, 205, 208, 209, + 212, 215, 218, 222, 226, 228, 232, 235, 238, 241, + 242, 245, 248, 253, 257, 261, 262, 265, 267, 269, + 272, 275, 278, 280, 282, 283, 286, 288, 292, 296, + 300, 303, 307, 311, 313 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 43, 0, -1, -1, 43, 44, -1, 45, -1, 55, + -1, 66, -1, 3, 77, 79, -1, 5, -1, 15, + -1, 8, -1, 1, 79, -1, 61, -1, 71, -1, + 47, -1, 49, -1, 69, -1, 79, -1, 10, 28, + 32, -1, 46, 50, -1, 11, 28, 32, -1, 48, + 50, -1, -1, 50, 51, -1, 50, 75, -1, 50, + 73, -1, 50, 32, -1, 21, 76, 32, -1, 22, + 81, 80, 32, -1, 23, 76, 32, -1, 24, 81, + 80, 32, -1, 26, 76, 32, -1, 27, 76, 32, + -1, 25, 76, 32, -1, 19, 77, 80, 32, -1, + 20, 81, 80, 32, -1, 36, 28, 80, 32, -1, + 37, 82, 82, 80, 32, -1, 7, 32, -1, 52, + 56, -1, 78, -1, 53, 58, 54, -1, 53, 58, + -1, -1, 56, 57, -1, 56, 75, -1, 56, 73, + -1, 56, 32, -1, 19, 77, 80, 32, -1, 21, + 76, 32, -1, 23, 76, 32, -1, 18, 32, -1, + 20, 28, 80, 32, -1, -1, 58, 45, -1, 14, + 81, 32, -1, 78, -1, 59, 62, 60, -1, 59, + 62, -1, -1, 62, 45, -1, 62, 66, -1, 62, + 55, -1, 4, 77, 32, -1, 63, 74, -1, 78, + -1, 64, 67, 65, -1, 64, 67, -1, -1, 67, + 45, -1, 67, 66, -1, 67, 55, -1, 67, 1, + 32, -1, 6, 77, 32, -1, 68, -1, 9, 77, + 32, -1, 70, 74, -1, 12, 32, -1, 72, 13, + -1, -1, 74, 75, -1, 74, 32, -1, 16, 35, + 81, 32, -1, 16, 81, 32, -1, 17, 81, 32, + -1, -1, 77, 80, -1, 28, -1, 29, -1, 5, + 79, -1, 8, 79, -1, 15, 79, -1, 32, -1, + 31, -1, -1, 14, 81, -1, 82, -1, 82, 40, + 82, -1, 82, 30, 82, -1, 34, 81, 33, -1, + 41, 81, -1, 81, 38, 81, -1, 81, 39, 81, + -1, 28, -1, 29, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned short yyrline[] = +{ + 0, 94, 94, 95, 98, 99, 100, 101, 102, 103, + 104, 105, 109, 110, 111, 112, 113, 114, 120, 128, + 134, 142, 152, 154, 155, 156, 157, 160, 166, 173, + 179, 186, 192, 198, 204, 210, 216, 222, 230, 239, + 245, 254, 255, 261, 263, 264, 265, 266, 269, 275, + 281, 287, 293, 299, 301, 306, 315, 324, 325, 331, + 333, 334, 335, 340, 347, 353, 362, 363, 369, 371, + 372, 373, 374, 377, 383, 390, 397, 404, 410, 417, + 418, 419, 422, 427, 432, 440, 442, 447, 448, 451, + 452, 453, 457, 457, 459, 460, 463, 464, 465, 466, + 467, 468, 469, 472, 473 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU", + "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG", + "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS", + "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_DEFAULT", "T_TRISTATE", + "T_DEF_TRISTATE", "T_BOOLEAN", "T_DEF_BOOLEAN", "T_STRING", "T_INT", + "T_HEX", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL", "T_EOF", "T_EOL", + "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_ON", "T_SELECT", "T_RANGE", "T_OR", + "T_AND", "T_EQUAL", "T_NOT", "$accept", "input", "block", + "common_block", "config_entry_start", "config_stmt", + "menuconfig_entry_start", "menuconfig_stmt", "config_option_list", + "config_option", "choice", "choice_entry", "choice_end", "choice_stmt", + "choice_option_list", "choice_option", "choice_block", "if", "if_end", + "if_stmt", "if_block", "menu", "menu_entry", "menu_end", "menu_stmt", + "menu_block", "source", "source_stmt", "comment", "comment_stmt", + "help_start", "help", "depends_list", "depends", "prompt_stmt_opt", + "prompt", "end", "nl_or_eof", "if_expr", "expr", "symbol", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 42, 43, 43, 44, 44, 44, 44, 44, 44, + 44, 44, 45, 45, 45, 45, 45, 45, 46, 47, + 48, 49, 50, 50, 50, 50, 50, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 52, 53, + 54, 55, 55, 56, 56, 56, 56, 56, 57, 57, + 57, 57, 57, 58, 58, 59, 60, 61, 61, 62, + 62, 62, 62, 63, 64, 65, 66, 66, 67, 67, + 67, 67, 67, 68, 69, 70, 71, 72, 73, 74, + 74, 74, 75, 75, 75, 76, 76, 77, 77, 78, + 78, 78, 79, 79, 80, 80, 81, 81, 81, 81, + 81, 81, 81, 82, 82 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 0, 2, 1, 1, 1, 3, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, + 3, 2, 0, 2, 2, 2, 2, 3, 4, 3, + 4, 3, 3, 3, 4, 4, 4, 5, 2, 2, + 1, 3, 2, 0, 2, 2, 2, 2, 4, 3, + 3, 2, 4, 0, 2, 3, 1, 3, 2, 0, + 2, 2, 2, 3, 2, 1, 3, 2, 0, 2, + 2, 2, 3, 3, 1, 3, 2, 2, 2, 0, + 2, 2, 4, 3, 3, 0, 2, 1, 1, 2, + 2, 2, 1, 1, 0, 2, 1, 3, 3, 3, + 2, 3, 3, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 2, 0, 1, 0, 0, 0, 8, 0, 0, 10, + 0, 0, 0, 0, 9, 93, 92, 3, 4, 22, + 14, 22, 15, 43, 53, 5, 59, 12, 79, 68, + 6, 74, 16, 79, 13, 17, 11, 87, 88, 0, + 0, 0, 38, 0, 0, 0, 103, 104, 0, 0, + 0, 96, 19, 21, 39, 42, 58, 64, 0, 76, + 7, 63, 73, 75, 18, 20, 0, 100, 55, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 85, 0, + 85, 0, 85, 85, 85, 26, 0, 0, 23, 0, + 25, 24, 0, 0, 0, 85, 85, 47, 44, 46, + 45, 0, 0, 0, 54, 41, 40, 60, 62, 57, + 61, 56, 81, 80, 0, 69, 71, 66, 70, 65, + 99, 101, 102, 98, 97, 77, 0, 0, 0, 94, + 94, 0, 94, 94, 0, 94, 0, 0, 0, 94, + 0, 78, 51, 94, 94, 0, 0, 89, 90, 91, + 72, 0, 83, 84, 0, 0, 0, 27, 86, 0, + 29, 0, 33, 31, 32, 0, 94, 0, 0, 49, + 50, 82, 95, 34, 35, 28, 30, 36, 0, 48, + 52, 37 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const short yydefgoto[] = +{ + -1, 1, 17, 18, 19, 20, 21, 22, 52, 88, + 23, 24, 105, 25, 54, 98, 55, 26, 109, 27, + 56, 28, 29, 117, 30, 58, 31, 32, 33, 34, + 89, 90, 57, 91, 131, 132, 106, 35, 155, 50, + 51 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -99 +static const short yypact[] = +{ + -99, 48, -99, 38, 46, 46, -99, 46, -29, -99, + 46, -17, -3, -11, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, -99, -99, -99, -99, -99, 38, + 12, 15, -99, 18, 51, 62, -99, -99, -11, -11, + 4, -24, 138, 138, 160, 121, 110, -4, 81, -4, + -99, -99, -99, -99, -99, -99, -19, -99, -99, -11, + -11, 70, 70, 73, 32, -11, 46, -11, 46, -11, + 46, -11, 46, 46, 46, -99, 36, 70, -99, 95, + -99, -99, 96, 46, 106, 46, 46, -99, -99, -99, + -99, 38, 38, 38, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, 112, -99, -99, -99, -99, -99, + -99, 117, -99, -99, -99, -99, -11, 33, 65, 131, + 1, 119, 131, 1, 136, 1, 153, 154, 155, 131, + 70, -99, -99, 131, 131, 156, 157, -99, -99, -99, + -99, 101, -99, -99, -11, 158, 159, -99, -99, 161, + -99, 162, -99, -99, -99, 163, 131, 164, 165, -99, + -99, -99, 99, -99, -99, -99, -99, -99, 166, -99, + -99, -99 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const short yypgoto[] = +{ + -99, -99, -99, 111, -99, -99, -99, -99, 178, -99, + -99, -99, -99, 91, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, 115, -99, -99, -99, -99, -99, + -99, 146, 168, 89, 27, 0, 126, -1, -98, -48, + -63 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -68 +static const short yytable[] = +{ + 66, 67, 36, 42, 39, 40, 71, 41, 123, 124, + 43, 44, 74, 75, 120, 154, 72, 46, 47, 69, + 70, 121, 122, 48, 140, 45, 127, 128, 112, 130, + 49, 133, 156, 135, 158, 159, 68, 161, 60, 69, + 70, 165, 69, 70, 61, 167, 168, 62, 2, 3, + 63, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 46, 47, 13, 14, 139, 152, 48, 126, 178, 15, + 16, 69, 70, 49, 37, 38, 129, 166, 151, 15, + 16, -67, 114, 64, -67, 5, 101, 7, 8, 102, + 10, 11, 12, 143, 65, 13, 103, 153, 46, 47, + 147, 148, 149, 69, 70, 125, 172, 134, 141, 136, + 137, 138, 15, 16, 5, 101, 7, 8, 102, 10, + 11, 12, 145, 146, 13, 103, 101, 7, 142, 102, + 10, 11, 12, 171, 144, 13, 103, 69, 70, 69, + 70, 15, 16, 100, 150, 154, 113, 108, 113, 116, + 73, 157, 15, 16, 74, 75, 70, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 104, 107, 160, 115, + 85, 110, 73, 118, 86, 87, 74, 75, 92, 93, + 94, 95, 111, 96, 119, 162, 163, 164, 169, 170, + 173, 174, 97, 175, 176, 177, 179, 180, 181, 53, + 99, 59 +}; + +static const unsigned char yycheck[] = +{ + 48, 49, 3, 32, 4, 5, 30, 7, 71, 72, + 10, 28, 16, 17, 33, 14, 40, 28, 29, 38, + 39, 69, 70, 34, 87, 28, 74, 75, 32, 77, + 41, 79, 130, 81, 132, 133, 32, 135, 39, 38, + 39, 139, 38, 39, 32, 143, 144, 32, 0, 1, + 32, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 28, 29, 14, 15, 28, 32, 34, 35, 166, 31, + 32, 38, 39, 41, 28, 29, 76, 140, 126, 31, + 32, 0, 1, 32, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 93, 32, 14, 15, 32, 28, 29, + 101, 102, 103, 38, 39, 32, 154, 80, 13, 82, + 83, 84, 31, 32, 4, 5, 6, 7, 8, 9, + 10, 11, 95, 96, 14, 15, 5, 6, 32, 8, + 9, 10, 11, 32, 28, 14, 15, 38, 39, 38, + 39, 31, 32, 54, 32, 14, 57, 56, 59, 58, + 12, 32, 31, 32, 16, 17, 39, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 55, 56, 32, 58, + 32, 56, 12, 58, 36, 37, 16, 17, 18, 19, + 20, 21, 56, 23, 58, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 21, + 54, 33 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 43, 0, 1, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 14, 15, 31, 32, 44, 45, 46, + 47, 48, 49, 52, 53, 55, 59, 61, 63, 64, + 66, 68, 69, 70, 71, 79, 79, 28, 29, 77, + 77, 77, 32, 77, 28, 28, 28, 29, 34, 41, + 81, 82, 50, 50, 56, 58, 62, 74, 67, 74, + 79, 32, 32, 32, 32, 32, 81, 81, 32, 38, + 39, 30, 40, 12, 16, 17, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 32, 36, 37, 51, 72, + 73, 75, 18, 19, 20, 21, 23, 32, 57, 73, + 75, 5, 8, 15, 45, 54, 78, 45, 55, 60, + 66, 78, 32, 75, 1, 45, 55, 65, 66, 78, + 33, 81, 81, 82, 82, 32, 35, 81, 81, 77, + 81, 76, 77, 81, 76, 81, 76, 76, 76, 28, + 82, 13, 32, 77, 28, 76, 76, 79, 79, 79, + 32, 81, 32, 32, 14, 80, 80, 32, 80, 80, + 32, 80, 32, 32, 32, 80, 82, 80, 80, 32, + 32, 32, 81, 32, 32, 32, 32, 32, 80, 32, + 32, 32 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + Current.first_line = Rhs[1].first_line; \ + Current.first_column = Rhs[1].first_column; \ + Current.last_line = Rhs[N].last_line; \ + Current.last_column = Rhs[N].last_column; +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (cinluded). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short *bottom, short *top) +#else +static void +yy_stack_print (bottom, top) + short *bottom; + short *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylineno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylineno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 8: + + { zconfprint("unexpected 'endmenu' statement"); ;} + break; + + case 9: + + { zconfprint("unexpected 'endif' statement"); ;} + break; + + case 10: + + { zconfprint("unexpected 'endchoice' statement"); ;} + break; + + case 11: + + { zconfprint("syntax error"); yyerrok; ;} + break; + + case 18: + + { + struct symbol *sym = sym_lookup(yyvsp[-1].string, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string); +;} + break; + + case 19: + + { + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 20: + + { + struct symbol *sym = sym_lookup(yyvsp[-1].string, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string); +;} + break; + + case 21: + + { + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 27: + + { + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 28: + + { + menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr); + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 29: + + { + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 30: + + { + menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr); + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 31: + + { + menu_set_type(S_INT); + printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 32: + + { + menu_set_type(S_HEX); + printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 33: + + { + menu_set_type(S_STRING); + printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 34: + + { + menu_add_prompt(P_PROMPT, yyvsp[-2].string, yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 35: + + { + menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 36: + + { + menu_add_symbol(P_SELECT, sym_lookup(yyvsp[-2].string, 0), yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 37: + + { + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,yyvsp[-3].symbol, yyvsp[-2].symbol), yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 38: + + { + struct symbol *sym = sym_lookup(NULL, 0); + sym->flags |= SYMBOL_CHOICE; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 39: + + { + menu_end_entry(); + menu_add_menu(); +;} + break; + + case 40: + + { + if (zconf_endtoken(yyvsp[0].token, T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 42: + + { + printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +;} + break; + + case 48: + + { + menu_add_prompt(P_PROMPT, yyvsp[-2].string, yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 49: + + { + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 50: + + { + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 51: + + { + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 52: + + { + menu_add_symbol(P_DEFAULT, sym_lookup(yyvsp[-2].string, 0), yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 55: + + { + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep(yyvsp[-1].expr); + menu_end_entry(); + menu_add_menu(); +;} + break; + + case 56: + + { + if (zconf_endtoken(yyvsp[0].token, T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 58: + + { + printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +;} + break; + + case 63: + + { + menu_add_entry(NULL); + menu_add_prop(P_MENU, yyvsp[-1].string, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 64: + + { + menu_end_entry(); + menu_add_menu(); +;} + break; + + case 65: + + { + if (zconf_endtoken(yyvsp[0].token, T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 67: + + { + printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +;} + break; + + case 72: + + { zconfprint("invalid menu option"); yyerrok; ;} + break; + + case 73: + + { + yyval.string = yyvsp[-1].string; + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string); +;} + break; + + case 74: + + { + zconf_nextfile(yyvsp[0].string); +;} + break; + + case 75: + + { + menu_add_entry(NULL); + menu_add_prop(P_COMMENT, yyvsp[-1].string, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 76: + + { + menu_end_entry(); +;} + break; + + case 77: + + { + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +;} + break; + + case 78: + + { + current_entry->sym->help = yyvsp[0].string; +;} + break; + + case 82: + + { + menu_add_dep(yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 83: + + { + menu_add_dep(yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 84: + + { + menu_add_dep(yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 86: + + { + menu_add_prop(P_PROMPT, yyvsp[-1].string, NULL, yyvsp[0].expr); +;} + break; + + case 89: + + { yyval.token = T_ENDMENU; ;} + break; + + case 90: + + { yyval.token = T_ENDCHOICE; ;} + break; + + case 91: + + { yyval.token = T_ENDIF; ;} + break; + + case 94: + + { yyval.expr = NULL; ;} + break; + + case 95: + + { yyval.expr = yyvsp[0].expr; ;} + break; + + case 96: + + { yyval.expr = expr_alloc_symbol(yyvsp[0].symbol); ;} + break; + + case 97: + + { yyval.expr = expr_alloc_comp(E_EQUAL, yyvsp[-2].symbol, yyvsp[0].symbol); ;} + break; + + case 98: + + { yyval.expr = expr_alloc_comp(E_UNEQUAL, yyvsp[-2].symbol, yyvsp[0].symbol); ;} + break; + + case 99: + + { yyval.expr = yyvsp[-1].expr; ;} + break; + + case 100: + + { yyval.expr = expr_alloc_one(E_NOT, yyvsp[0].expr); ;} + break; + + case 101: + + { yyval.expr = expr_alloc_two(E_OR, yyvsp[-2].expr, yyvsp[0].expr); ;} + break; + + case 102: + + { yyval.expr = expr_alloc_two(E_AND, yyvsp[-2].expr, yyvsp[0].expr); ;} + break; + + case 103: + + { yyval.symbol = sym_lookup(yyvsp[0].string, 0); free(yyvsp[0].string); ;} + break; + + case 104: + + { yyval.symbol = sym_lookup(yyvsp[0].string, 1); free(yyvsp[0].string); ;} + break; + + + } + +/* Line 999 of yacc.c. */ + + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + char *yymsg; + int yyx, yycount; + + yycount = 0; + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + yysize += yystrlen (yytname[yyx]) + 15, yycount++; + yysize += yystrlen ("syntax error, unexpected ") + 1; + yysize += yystrlen (yytname[yytype]); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yycount = 0; + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); + yyx++) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + const char *yyq = ! yycount ? ", expecting " : " or "; + yyp = yystpcpy (yyp, yyq); + yyp = yystpcpy (yyp, yytname[yyx]); + yycount++; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + /* Return failure if at end of input. */ + if (yychar == YYEOF) + { + /* Pop the error token. */ + YYPOPSTACK; + /* Pop the rest of the stack. */ + while (yyss < yyssp) + { + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + YYPOPSTACK; + } + YYABORT; + } + + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*----------------------------------------------------. +| yyerrlab1 -- error raised explicitly by an action. | +`----------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + yyvsp--; + yystate = *--yyssp; + + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + + + + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + menu_init(); + modules_sym = sym_lookup("MODULES", 0); + rootmenu.prompt = menu_add_prop(P_MENU, "BusyBox Configuration", NULL, NULL); + + //zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (!(sym->flags & SYMBOL_CHECKED) && sym_check_deps(sym)) + printf("\n"); + else + sym->flags |= SYMBOL_CHECK_DONE; + } + + sym_change_count = 1; +} + +const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + } + return ""; +} + +static bool zconf_endtoken(int token, int starttoken, int endtoken) +{ + if (token != endtoken) { + zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken)); + zconfprint("location of the '%s'", zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno() + 1); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "choice\n"); + else + fprintf(out, "config %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (sym->help) { + int len = strlen(sym->help); + while (sym->help[--len] == '\n') + sym->help[len] = 0; + fprintf(out, " help\n%s\n", sym->help); + } + fputc('\n', out); +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + fputs("\n", out); + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "lex.zconf.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" + + diff --git a/busybox/scripts/config/zconf.tab.h_shipped b/busybox/scripts/config/zconf.tab.h_shipped new file mode 100644 index 000000000..3b191ef59 --- /dev/null +++ b/busybox/scripts/config/zconf.tab.h_shipped @@ -0,0 +1,125 @@ +/* A Bison parser, made from zconf.y, by GNU bison 1.75. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +#ifndef BISON_ZCONF_TAB_H +# define BISON_ZCONF_TAB_H + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + T_MAINMENU = 258, + T_MENU = 259, + T_ENDMENU = 260, + T_SOURCE = 261, + T_CHOICE = 262, + T_ENDCHOICE = 263, + T_COMMENT = 264, + T_CONFIG = 265, + T_HELP = 266, + T_HELPTEXT = 267, + T_IF = 268, + T_ENDIF = 269, + T_DEPENDS = 270, + T_REQUIRES = 271, + T_OPTIONAL = 272, + T_PROMPT = 273, + T_DEFAULT = 274, + T_TRISTATE = 275, + T_BOOLEAN = 276, + T_INT = 277, + T_HEX = 278, + T_WORD = 279, + T_STRING = 280, + T_UNEQUAL = 281, + T_EOF = 282, + T_EOL = 283, + T_CLOSE_PAREN = 284, + T_OPEN_PAREN = 285, + T_ON = 286, + T_OR = 287, + T_AND = 288, + T_EQUAL = 289, + T_NOT = 290 + }; +#endif +#define T_MAINMENU 258 +#define T_MENU 259 +#define T_ENDMENU 260 +#define T_SOURCE 261 +#define T_CHOICE 262 +#define T_ENDCHOICE 263 +#define T_COMMENT 264 +#define T_CONFIG 265 +#define T_HELP 266 +#define T_HELPTEXT 267 +#define T_IF 268 +#define T_ENDIF 269 +#define T_DEPENDS 270 +#define T_REQUIRES 271 +#define T_OPTIONAL 272 +#define T_PROMPT 273 +#define T_DEFAULT 274 +#define T_TRISTATE 275 +#define T_BOOLEAN 276 +#define T_INT 277 +#define T_HEX 278 +#define T_WORD 279 +#define T_STRING 280 +#define T_UNEQUAL 281 +#define T_EOF 282 +#define T_EOL 283 +#define T_CLOSE_PAREN 284 +#define T_OPEN_PAREN 285 +#define T_ON 286 +#define T_OR 287 +#define T_AND 288 +#define T_EQUAL 289 +#define T_NOT 290 + + + + +#ifndef YYSTYPE +#line 33 "zconf.y" +typedef union { + int token; + char *string; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; +} yystype; +/* Line 1281 of /usr/share/bison/yacc.c. */ +#line 118 "zconf.tab.h" +# define YYSTYPE yystype +#endif + +extern YYSTYPE zconflval; + + +#endif /* not BISON_ZCONF_TAB_H */ + diff --git a/busybox/scripts/config/zconf.y b/busybox/scripts/config/zconf.y new file mode 100644 index 000000000..658495cda --- /dev/null +++ b/busybox/scripts/config/zconf.y @@ -0,0 +1,687 @@ +%{ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(int token, int starttoken, int endtoken); + +struct symbol *symbol_hash[257]; + +#define YYERROR_VERBOSE +%} +%expect 40 + +%union +{ + int token; + char *string; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; +} + +%token T_MAINMENU +%token T_MENU +%token T_ENDMENU +%token T_SOURCE +%token T_CHOICE +%token T_ENDCHOICE +%token T_COMMENT +%token T_CONFIG +%token T_MENUCONFIG +%token T_HELP +%token T_HELPTEXT +%token T_IF +%token T_ENDIF +%token T_DEPENDS +%token T_REQUIRES +%token T_OPTIONAL +%token T_PROMPT +%token T_DEFAULT +%token T_TRISTATE +%token T_DEF_TRISTATE +%token T_BOOLEAN +%token T_DEF_BOOLEAN +%token T_STRING +%token T_INT +%token T_HEX +%token T_WORD +%token T_WORD_QUOTE +%token T_UNEQUAL +%token T_EOF +%token T_EOL +%token T_CLOSE_PAREN +%token T_OPEN_PAREN +%token T_ON +%token T_SELECT +%token T_RANGE + +%left T_OR +%left T_AND +%left T_EQUAL T_UNEQUAL +%nonassoc T_NOT + +%type prompt +%type source +%type symbol +%type expr +%type if_expr +%type end + +%{ +#define LKC_DIRECT_LINK +#include "lkc.h" +%} +%% +input: /* empty */ + | input block +; + +block: common_block + | choice_stmt + | menu_stmt + | T_MAINMENU prompt nl_or_eof + | T_ENDMENU { zconfprint("unexpected 'endmenu' statement"); } + | T_ENDIF { zconfprint("unexpected 'endif' statement"); } + | T_ENDCHOICE { zconfprint("unexpected 'endchoice' statement"); } + | error nl_or_eof { zconfprint("syntax error"); yyerrok; } +; + +common_block: + if_stmt + | comment_stmt + | config_stmt + | menuconfig_stmt + | source_stmt + | nl_or_eof +; + + +/* config/menuconfig entry */ + +config_entry_start: T_CONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +config_stmt: config_entry_start config_option_list +{ + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +menuconfig_stmt: menuconfig_entry_start config_option_list +{ + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +config_option_list: + /* empty */ + | config_option_list config_option + | config_option_list depends + | config_option_list help + | config_option_list T_EOL +; + +config_option: T_TRISTATE prompt_stmt_opt T_EOL +{ + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEF_TRISTATE expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_BOOLEAN prompt_stmt_opt T_EOL +{ + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEF_BOOLEAN expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_INT prompt_stmt_opt T_EOL +{ + menu_set_type(S_INT); + printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_HEX prompt_stmt_opt T_EOL +{ + menu_set_type(S_HEX); + printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_STRING prompt_stmt_opt T_EOL +{ + menu_set_type(S_STRING); + printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEFAULT expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_SELECT T_WORD if_expr T_EOL +{ + menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_RANGE symbol symbol if_expr T_EOL +{ + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +}; + +/* choice entry */ + +choice: T_CHOICE T_EOL +{ + struct symbol *sym = sym_lookup(NULL, 0); + sym->flags |= SYMBOL_CHOICE; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +}; + +choice_entry: choice choice_option_list +{ + menu_end_entry(); + menu_add_menu(); +}; + +choice_end: end +{ + if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +}; + +choice_stmt: + choice_entry choice_block choice_end + | choice_entry choice_block +{ + printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +}; + +choice_option_list: + /* empty */ + | choice_option_list choice_option + | choice_option_list depends + | choice_option_list help + | choice_option_list T_EOL +; + +choice_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_TRISTATE prompt_stmt_opt T_EOL +{ + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_BOOLEAN prompt_stmt_opt T_EOL +{ + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_OPTIONAL T_EOL +{ + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_DEFAULT T_WORD if_expr T_EOL +{ + menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); +}; + +choice_block: + /* empty */ + | choice_block common_block +; + +/* if entry */ + +if: T_IF expr T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep($2); + menu_end_entry(); + menu_add_menu(); +}; + +if_end: end +{ + if (zconf_endtoken($1, T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +}; + +if_stmt: + if if_block if_end + | if if_block +{ + printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +}; + +if_block: + /* empty */ + | if_block common_block + | if_block menu_stmt + | if_block choice_stmt +; + +/* menu entry */ + +menu: T_MENU prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prop(P_MENU, $2, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +}; + +menu_entry: menu depends_list +{ + menu_end_entry(); + menu_add_menu(); +}; + +menu_end: end +{ + if (zconf_endtoken($1, T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +}; + +menu_stmt: + menu_entry menu_block menu_end + | menu_entry menu_block +{ + printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +}; + +menu_block: + /* empty */ + | menu_block common_block + | menu_block menu_stmt + | menu_block choice_stmt + | menu_block error T_EOL { zconfprint("invalid menu option"); yyerrok; } +; + +source: T_SOURCE prompt T_EOL +{ + $$ = $2; + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +source_stmt: source +{ + zconf_nextfile($1); +}; + +/* comment entry */ + +comment: T_COMMENT prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prop(P_COMMENT, $2, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +}; + +comment_stmt: comment depends_list +{ + menu_end_entry(); +}; + +/* help option */ + +help_start: T_HELP T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +}; + +help: help_start T_HELPTEXT +{ + current_entry->sym->help = $2; +}; + +/* depends option */ + +depends_list: /* empty */ + | depends_list depends + | depends_list T_EOL +; + +depends: T_DEPENDS T_ON expr T_EOL +{ + menu_add_dep($3); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +} + | T_DEPENDS expr T_EOL +{ + menu_add_dep($2); + printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno()); +} + | T_REQUIRES expr T_EOL +{ + menu_add_dep($2); + printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno()); +}; + +/* prompt statement */ + +prompt_stmt_opt: + /* empty */ + | prompt if_expr +{ + menu_add_prop(P_PROMPT, $1, NULL, $2); +}; + +prompt: T_WORD + | T_WORD_QUOTE +; + +end: T_ENDMENU nl_or_eof { $$ = T_ENDMENU; } + | T_ENDCHOICE nl_or_eof { $$ = T_ENDCHOICE; } + | T_ENDIF nl_or_eof { $$ = T_ENDIF; } +; + +nl_or_eof: + T_EOL | T_EOF; + +if_expr: /* empty */ { $$ = NULL; } + | T_IF expr { $$ = $2; } +; + +expr: symbol { $$ = expr_alloc_symbol($1); } + | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } + | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } + | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } + | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } + | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } + | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } +; + +symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); } + | T_WORD_QUOTE { $$ = sym_lookup($1, 1); free($1); } +; + +%% + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + menu_init(); + modules_sym = sym_lookup("MODULES", 0); + rootmenu.prompt = menu_add_prop(P_MENU, "BusyBox Configuration", NULL, NULL); + + //zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (!(sym->flags & SYMBOL_CHECKED) && sym_check_deps(sym)) + printf("\n"); + else + sym->flags |= SYMBOL_CHECK_DONE; + } + + sym_change_count = 1; +} + +const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + } + return ""; +} + +static bool zconf_endtoken(int token, int starttoken, int endtoken) +{ + if (token != endtoken) { + zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken)); + zconfprint("location of the '%s'", zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno() + 1); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "choice\n"); + else + fprintf(out, "config %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (sym->help) { + int len = strlen(sym->help); + while (sym->help[--len] == '\n') + sym->help[len] = 0; + fprintf(out, " help\n%s\n", sym->help); + } + fputc('\n', out); +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + fputs("\n", out); + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "lex.zconf.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" diff --git a/busybox/scripts/mkdep.c b/busybox/scripts/mkdep.c new file mode 100644 index 000000000..ae3cc74e0 --- /dev/null +++ b/busybox/scripts/mkdep.c @@ -0,0 +1,628 @@ +/* + * Originally by Linus Torvalds. + * Smart CONFIG_* processing by Werner Almesberger, Michael Chastain. + * + * Usage: mkdep cflags -- file ... + * + * Read source files and output makefile dependency lines for them. + * I make simple dependency lines for #include <*.h> and #include "*.h". + * I also find instances of CONFIG_FOO and generate dependencies + * like include/config/foo.h. + * + * 1 August 1999, Michael Elizabeth Chastain, + * - Keith Owens reported a bug in smart config processing. There used + * to be an optimization for "#define CONFIG_FOO ... #ifdef CONFIG_FOO", + * so that the file would not depend on CONFIG_FOO because the file defines + * this symbol itself. But this optimization is bogus! Consider this code: + * "#if 0 \n #define CONFIG_FOO \n #endif ... #ifdef CONFIG_FOO". Here + * the definition is inactivated, but I still used it. It turns out this + * actually happens a few times in the kernel source. The simple way to + * fix this problem is to remove this particular optimization. + * + * 2.3.99-pre1, Andrew Morton + * - Changed so that 'filename.o' depends upon 'filename.[cS]'. This is so that + * missing source files are noticed, rather than silently ignored. + * + * 2.4.2-pre3, Keith Owens + * - Accept cflags followed by '--' followed by filenames. mkdep extracts -I + * options from cflags and looks in the specified directories as well as the + * defaults. Only -I is supported, no attempt is made to handle -idirafter, + * -isystem, -I- etc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + + +char depname[512]; +int hasdep; + +struct path_struct { + int len; + char *buffer; +}; +struct path_struct *path_array; +int paths; + + +/* Current input file */ +static const char *g_filename; + +/* + * This records all the configuration options seen. + * In perl this would be a hash, but here it's a long string + * of values separated by newlines. This is simple and + * extremely fast. + */ +char * str_config = NULL; +int size_config = 0; +int len_config = 0; + +static void +do_depname(void) +{ + if (!hasdep) { + hasdep = 1; + if (g_filename) { + /* Source file (*.[cS]) */ + printf("%s:", depname); + printf(" %s", g_filename); + } else { + /* header file (*.h) */ + printf("dep_%s +=", depname); + } + } +} + +/* + * Grow the configuration string to a desired length. + * Usually the first growth is plenty. + */ +void grow_config(int len) +{ + while (len_config + len > size_config) { + if (size_config == 0) + size_config = 2048; + str_config = realloc(str_config, size_config *= 2); + if (str_config == NULL) + { perror("malloc config"); exit(1); } + } +} + + + +/* + * Lookup a value in the configuration string. + */ +int is_defined_config(const char * name, int len) +{ + const char * pconfig; + const char * plast = str_config + len_config - len; + for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) { + if (pconfig[ -1] == '\n' + && pconfig[len] == '\n' + && !memcmp(pconfig, name, len)) + return 1; + } + return 0; +} + + + +/* + * Add a new value to the configuration string. + */ +void define_config(const char * name, int len) +{ + grow_config(len + 1); + + memcpy(str_config+len_config, name, len); + len_config += len; + str_config[len_config++] = '\n'; +} + + + +/* + * Clear the set of configuration strings. + */ +void clear_config(void) +{ + len_config = 0; + define_config("", 0); +} + + + +/* + * This records all the precious .h filenames. No need for a hash, + * it's a long string of values enclosed in tab and newline. + */ +char * str_precious = NULL; +int size_precious = 0; +int len_precious = 0; + + + +/* + * Grow the precious string to a desired length. + * Usually the first growth is plenty. + */ +void grow_precious(int len) +{ + while (len_precious + len > size_precious) { + if (size_precious == 0) + size_precious = 2048; + str_precious = realloc(str_precious, size_precious *= 2); + if (str_precious == NULL) + { perror("malloc"); exit(1); } + } +} + + + +/* + * Add a new value to the precious string. + */ +void define_precious(const char * filename) +{ + int len = strlen(filename); + grow_precious(len + 4); + *(str_precious+len_precious++) = '\t'; + memcpy(str_precious+len_precious, filename, len); + len_precious += len; + memcpy(str_precious+len_precious, " \\\n", 3); + len_precious += 3; +} + + + +/* + * Handle an #include line. + */ +void handle_include(int start, const char * name, int len) +{ + struct path_struct *path; + int i; + + if (len == 14 && !memcmp(name, "include/config.h", len)) + return; + + if (len >= 7 && !memcmp(name, "config/", 7)) + define_config(name+7, len-7-2); + + for (i = start, path = path_array+start; i < paths; ++i, ++path) { + memcpy(path->buffer+path->len, name, len); + path->buffer[path->len+len] = '\0'; + if (access(path->buffer, F_OK) == 0) { + do_depname(); + printf(" \\\n %s $(dep_%s)", path->buffer, path->buffer); + return; + } + } + +} + + + +/* + * Add a path to the list of include paths. + */ +void add_path(const char * name) +{ + struct path_struct *path; + char resolved_path[PATH_MAX+1]; + const char *name2; + + if (strcmp(name, ".")) { + name2 = realpath(name, resolved_path); + if (!name2) { + fprintf(stderr, "realpath(%s) failed, %m\n", name); + exit(1); + } + } + else { + name2 = ""; + } + + path_array = realloc(path_array, (++paths)*sizeof(*path_array)); + if (!path_array) { + fprintf(stderr, "cannot expand path_arry\n"); + exit(1); + } + + path = path_array+paths-1; + path->len = strlen(name2); + path->buffer = malloc(path->len+1+256+1); + if (!path->buffer) { + fprintf(stderr, "cannot allocate path buffer\n"); + exit(1); + } + strcpy(path->buffer, name2); + if (path->len && *(path->buffer+path->len-1) != '/') { + *(path->buffer+path->len) = '/'; + *(path->buffer+(++(path->len))) = '\0'; + } +} + + + +/* + * Record the use of a CONFIG_* word. + */ +void use_config(const char * name, int len) +{ + char *pc; + int i; + + pc = path_array[paths-1].buffer + path_array[paths-1].len; + memcpy(pc, "config/", 7); + pc += 7; + + for (i = 0; i < len; i++) { + char c = name[i]; + if (isupper((int)c)) c = tolower((int)c); + if (c == '_') c = '/'; + pc[i] = c; + } + pc[len] = '\0'; + + if (is_defined_config(pc, len)) + return; + + define_config(pc, len); + + do_depname(); + printf(" \\\n $(wildcard %s.h)", path_array[paths-1].buffer); +} + + + +/* + * Macros for stunningly fast map-based character access. + * __buf is a register which holds the current word of the input. + * Thus, there is one memory access per sizeof(unsigned long) characters. + */ + +#if defined(__alpha__) || defined(__i386__) || defined(__ia64__) || defined(__x86_64__) || defined(__MIPSEL__) \ + || defined(__arm__) +#define LE_MACHINE +#endif + +#ifdef LE_MACHINE +#define next_byte(x) (x >>= 8) +#define current ((unsigned char) __buf) +#else +#define next_byte(x) (x <<= 8) +#define current (__buf >> 8*(sizeof(unsigned long)-1)) +#endif + +#define GETNEXT { \ + next_byte(__buf); \ + if ((unsigned long) next % sizeof(unsigned long) == 0) { \ + if (next >= end) \ + break; \ + __buf = * (unsigned long *) next; \ + } \ + next++; \ +} + +/* + * State machine macros. + */ +#define CASE(c,label) if (current == c) goto label +#define NOTCASE(c,label) if (current != c) goto label + +/* + * Yet another state machine speedup. + */ +#define MAX2(a,b) ((a)>(b)?(a):(b)) +#define MIN2(a,b) ((a)<(b)?(a):(b)) +#define MAX5(a,b,c,d,e) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,e))))) +#define MIN5(a,b,c,d,e) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,e))))) + + + +/* + * The state machine looks for (approximately) these Perl regular expressions: + * + * m|\/\*.*?\*\/| + * m|\/\/.*| + * m|'.*?'| + * m|".*?"| + * m|#\s*include\s*"(.*?)"| + * m|#\s*include\s*<(.*?>"| + * m|#\s*(?define|undef)\s*CONFIG_(\w*)| + * m|(?!\w)CONFIG_| + * + * About 98% of the CPU time is spent here, and most of that is in + * the 'start' paragraph. Because the current characters are + * in a register, the start loop usually eats 4 or 8 characters + * per memory read. The MAX5 and MIN5 tests dispose of most + * input characters with 1 or 2 comparisons. + */ +void state_machine(const char * map, const char * end) +{ + const char * next = map; + const char * map_dot; + unsigned long __buf = 0; + + for (;;) { +start: + GETNEXT +__start: + if (current > MAX5('/','\'','"','#','C')) goto start; + if (current < MIN5('/','\'','"','#','C')) goto start; + CASE('/', slash); + CASE('\'', squote); + CASE('"', dquote); + CASE('#', pound); + CASE('C', cee); + goto start; + +/* // */ +slash_slash: + GETNEXT + CASE('\n', start); + NOTCASE('\\', slash_slash); + GETNEXT + goto slash_slash; + +/* / */ +slash: + GETNEXT + CASE('/', slash_slash); + NOTCASE('*', __start); +slash_star_dot_star: + GETNEXT +__slash_star_dot_star: + NOTCASE('*', slash_star_dot_star); + GETNEXT + NOTCASE('/', __slash_star_dot_star); + goto start; + +/* '.*?' */ +squote: + GETNEXT + CASE('\'', start); + NOTCASE('\\', squote); + GETNEXT + goto squote; + +/* ".*?" */ +dquote: + GETNEXT + CASE('"', start); + NOTCASE('\\', dquote); + GETNEXT + goto dquote; + +/* #\s* */ +pound: + GETNEXT + CASE(' ', pound); + CASE('\t', pound); + CASE('i', pound_i); + CASE('d', pound_d); + CASE('u', pound_u); + goto __start; + +/* #\s*i */ +pound_i: + GETNEXT NOTCASE('n', __start); + GETNEXT NOTCASE('c', __start); + GETNEXT NOTCASE('l', __start); + GETNEXT NOTCASE('u', __start); + GETNEXT NOTCASE('d', __start); + GETNEXT NOTCASE('e', __start); + goto pound_include; + +/* #\s*include\s* */ +pound_include: + GETNEXT + CASE(' ', pound_include); + CASE('\t', pound_include); + map_dot = next; + CASE('"', pound_include_dquote); + CASE('<', pound_include_langle); + goto __start; + +/* #\s*include\s*"(.*)" */ +pound_include_dquote: + GETNEXT + CASE('\n', start); + NOTCASE('"', pound_include_dquote); + handle_include(0, map_dot, next - map_dot - 1); + goto start; + +/* #\s*include\s*<(.*)> */ +pound_include_langle: + GETNEXT + CASE('\n', start); + NOTCASE('>', pound_include_langle); + handle_include(1, map_dot, next - map_dot - 1); + goto start; + +/* #\s*d */ +pound_d: + GETNEXT NOTCASE('e', __start); + GETNEXT NOTCASE('f', __start); + GETNEXT NOTCASE('i', __start); + GETNEXT NOTCASE('n', __start); + GETNEXT NOTCASE('e', __start); + goto pound_define_undef; + +/* #\s*u */ +pound_u: + GETNEXT NOTCASE('n', __start); + GETNEXT NOTCASE('d', __start); + GETNEXT NOTCASE('e', __start); + GETNEXT NOTCASE('f', __start); + goto pound_define_undef; + +/* + * #\s*(define|undef)\s*CONFIG_(\w*) + * + * this does not define the word, because it could be inside another + * conditional (#if 0). But I do parse the word so that this instance + * does not count as a use. -- mec + */ +pound_define_undef: + GETNEXT + CASE(' ', pound_define_undef); + CASE('\t', pound_define_undef); + + NOTCASE('C', __start); + GETNEXT NOTCASE('O', __start); + GETNEXT NOTCASE('N', __start); + GETNEXT NOTCASE('F', __start); + GETNEXT NOTCASE('I', __start); + GETNEXT NOTCASE('G', __start); + GETNEXT NOTCASE('_', __start); + + map_dot = next; +pound_define_undef_CONFIG_word: + GETNEXT + if (isalnum(current) || current == '_') + goto pound_define_undef_CONFIG_word; + goto __start; + +/* \= map+2 && (isalnum((int)next[-2]) || next[-2] == '_')) + goto start; + GETNEXT NOTCASE('O', __start); + GETNEXT NOTCASE('N', __start); + GETNEXT NOTCASE('F', __start); + GETNEXT NOTCASE('I', __start); + GETNEXT NOTCASE('G', __start); + GETNEXT NOTCASE('_', __start); + + map_dot = next; +cee_CONFIG_word: + GETNEXT + if (isalnum(current) || current == '_') + goto cee_CONFIG_word; + use_config(map_dot, next - map_dot - 1); + goto __start; + } +} + + + +/* + * Generate dependencies for one file. + */ +void do_depend(const char * filename) +{ + int mapsize; + int pagesizem1 = getpagesize()-1; + int fd; + struct stat st; + char * map; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + return; + } + + fstat(fd, &st); + if (st.st_size == 0) { + fprintf(stderr,"%s is empty\n",filename); + close(fd); + return; + } + + mapsize = st.st_size; + mapsize = (mapsize+pagesizem1) & ~pagesizem1; + map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0); + if ((long) map == -1) { + perror("mkdep: mmap"); + close(fd); + return; + } + if ((unsigned long) map % sizeof(unsigned long) != 0) + { + fprintf(stderr, "do_depend: map not aligned\n"); + exit(1); + } + + hasdep = 0; + clear_config(); + state_machine(map, map+st.st_size); + if (hasdep) { + puts(""); + } + + munmap(map, mapsize); + close(fd); +} + + + +/* + * Generate dependencies for all files. + */ +int main(int argc, char **argv) +{ + int len; + const char *hpath; + + hpath = getenv("TOPDIR"); + if (!hpath) { + fputs("mkdep: TOPDIR not set in environment. " + "Don't bypass the top level Makefile.\n", stderr); + return 1; + } + + add_path("."); /* for #include "..." */ + + while (++argv, --argc > 0) { + if (strncmp(*argv, "-I", 2) == 0) { + if (*((*argv)+2)) { + add_path((*argv)+2); + } + else { + ++argv; + --argc; + add_path(*argv); + } + } + else if (strcmp(*argv, "--") == 0) { + break; + } + } + + add_path(hpath); /* must be last entry, for config files */ + + while (--argc > 0) { + const char * filename = *++argv; + g_filename = 0; + len = strlen(filename); + memcpy(depname, filename, len+1); + if (len > 2 && filename[len-2] == '.') { + if (filename[len-1] == 'c' || filename[len-1] == 'S') { + depname[len-1] = 'o'; + g_filename = filename; + } + } + do_depend(filename); + } + if (len_precious) { + *(str_precious+len_precious) = '\0'; + printf(".PRECIOUS:%s\n", str_precious); + } + return 0; +} diff --git a/busybox/scripts/split-include.c b/busybox/scripts/split-include.c new file mode 100644 index 000000000..624a0d62b --- /dev/null +++ b/busybox/scripts/split-include.c @@ -0,0 +1,226 @@ +/* + * split-include.c + * + * Copyright abandoned, Michael Chastain, . + * This is a C version of syncdep.pl by Werner Almesberger. + * + * This program takes autoconf.h as input and outputs a directory full + * of one-line include files, merging onto the old values. + * + * Think of the configuration options as key-value pairs. Then there + * are five cases: + * + * key old value new value action + * + * KEY-1 VALUE-1 VALUE-1 leave file alone + * KEY-2 VALUE-2A VALUE-2B write VALUE-2B into file + * KEY-3 - VALUE-3 write VALUE-3 into file + * KEY-4 VALUE-4 - write an empty file + * KEY-5 (empty) - leave old empty file alone + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define ERROR_EXIT(strExit) \ + { \ + const int errnoSave = errno; \ + fprintf(stderr, "%s: ", str_my_name); \ + errno = errnoSave; \ + perror((strExit)); \ + exit(1); \ + } + + + +int main(int argc, const char * argv []) +{ + const char * str_my_name; + const char * str_file_autoconf; + const char * str_dir_config; + + FILE * fp_config; + FILE * fp_target; + FILE * fp_find; + + int buffer_size; + + char * line; + char * old_line; + char * list_target; + char * ptarget; + + struct stat stat_buf; + + /* Check arg count. */ + if (argc != 3) + { + fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]); + exit(1); + } + + str_my_name = argv[0]; + str_file_autoconf = argv[1]; + str_dir_config = argv[2]; + + /* Find a buffer size. */ + if (stat(str_file_autoconf, &stat_buf) != 0) + ERROR_EXIT(str_file_autoconf); + buffer_size = 2 * stat_buf.st_size + 4096; + + /* Allocate buffers. */ + if ( (line = malloc(buffer_size)) == NULL + || (old_line = malloc(buffer_size)) == NULL + || (list_target = malloc(buffer_size)) == NULL ) + ERROR_EXIT(str_file_autoconf); + + /* Open autoconfig file. */ + if ((fp_config = fopen(str_file_autoconf, "r")) == NULL) + ERROR_EXIT(str_file_autoconf); + + /* Make output directory if needed. */ + if (stat(str_dir_config, &stat_buf) != 0) + { + if (mkdir(str_dir_config, 0755) != 0) + ERROR_EXIT(str_dir_config); + } + + /* Change to output directory. */ + if (chdir(str_dir_config) != 0) + ERROR_EXIT(str_dir_config); + + /* Put initial separator into target list. */ + ptarget = list_target; + *ptarget++ = '\n'; + + /* Read config lines. */ + while (fgets(line, buffer_size, fp_config)) + { + const char * str_config; + int is_same; + int itarget; + + if (line[0] != '#') + continue; + if ((str_config = strstr(line, "CONFIG_")) == NULL) + continue; + + /* Make the output file name. */ + str_config += sizeof("CONFIG_") - 1; + for (itarget = 0; !isspace(str_config[itarget]); itarget++) + { + char c = str_config[itarget]; + if (isupper(c)) c = tolower(c); + if (c == '_') c = '/'; + ptarget[itarget] = c; + } + ptarget[itarget++] = '.'; + ptarget[itarget++] = 'h'; + ptarget[itarget++] = '\0'; + + /* Check for existing file. */ + is_same = 0; + if ((fp_target = fopen(ptarget, "r")) != NULL) + { + fgets(old_line, buffer_size, fp_target); + if (fclose(fp_target) != 0) + ERROR_EXIT(ptarget); + if (!strcmp(line, old_line)) + is_same = 1; + } + + if (!is_same) + { + /* Auto-create directories. */ + int islash; + for (islash = 0; islash < itarget; islash++) + { + if (ptarget[islash] == '/') + { + ptarget[islash] = '\0'; + if (stat(ptarget, &stat_buf) != 0 + && mkdir(ptarget, 0755) != 0) + ERROR_EXIT( ptarget ); + ptarget[islash] = '/'; + } + } + + /* Write the file. */ + if ((fp_target = fopen(ptarget, "w" )) == NULL) + ERROR_EXIT(ptarget); + fputs(line, fp_target); + if (ferror(fp_target) || fclose(fp_target) != 0) + ERROR_EXIT(ptarget); + } + + /* Update target list */ + ptarget += itarget; + *(ptarget-1) = '\n'; + } + + /* + * Close autoconfig file. + * Terminate the target list. + */ + if (fclose(fp_config) != 0) + ERROR_EXIT(str_file_autoconf); + *ptarget = '\0'; + + /* + * Fix up existing files which have no new value. + * This is Case 4 and Case 5. + * + * I re-read the tree and filter it against list_target. + * This is crude. But it avoids data copies. Also, list_target + * is compact and contiguous, so it easily fits into cache. + * + * Notice that list_target contains strings separated by \n, + * with a \n before the first string and after the last. + * fgets gives the incoming names a terminating \n. + * So by having an initial \n, strstr will find exact matches. + */ + + fp_find = popen("find * -type f -name \"*.h\" -print", "r"); + if (fp_find == 0) + ERROR_EXIT( "find" ); + + line[0] = '\n'; + while (fgets(line+1, buffer_size, fp_find)) + { + if (strstr(list_target, line) == NULL) + { + /* + * This is an old file with no CONFIG_* flag in autoconf.h. + */ + + /* First strip the \n. */ + line[strlen(line)-1] = '\0'; + + /* Grab size. */ + if (stat(line+1, &stat_buf) != 0) + ERROR_EXIT(line); + + /* If file is not empty, make it empty and give it a fresh date. */ + if (stat_buf.st_size != 0) + { + if ((fp_target = fopen(line+1, "w")) == NULL) + ERROR_EXIT(line); + if (fclose(fp_target) != 0) + ERROR_EXIT(line); + } + } + } + + if (pclose(fp_find) != 0) + ERROR_EXIT("find"); + + return 0; +} diff --git a/busybox/shell/Config.in b/busybox/shell/Config.in new file mode 100644 index 000000000..bdba40d3b --- /dev/null +++ b/busybox/shell/Config.in @@ -0,0 +1,229 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Another Bourne-like Shell" + +choice + prompt "Choose your default shell" + default CONFIG_FEATURE_SH_IS_NONE + help + Choose a shell. The ash shell is the most bash compatible + and full featured one. + +config CONFIG_FEATURE_SH_IS_ASH + select CONFIG_ASH + bool "ash" + +config CONFIG_FEATURE_SH_IS_HUSH + select CONFIG_HUSH + bool "hush" + +config CONFIG_FEATURE_SH_IS_LASH + select CONFIG_LASH + bool "lash" + +config CONFIG_FEATURE_SH_IS_MSH + select CONFIG_MSH + bool "msh" + +config CONFIG_FEATURE_SH_IS_NONE + bool "none" + +endchoice + +config CONFIG_ASH + bool "ash" + default y + help + Tha 'ash' shell adds about 60k in the default configuration and is + the most complete and most pedantically correct shell included with + busybox. This shell is actually a derivative of the Debian 'dash' + shell (by Herbert Xu), which was created by porting the 'ash' shell + (written by Kenneth Almquist) from NetBSD. + +comment "Ash Shell Options" + depends on CONFIG_ASH + +config CONFIG_ASH_JOB_CONTROL + bool " Enable Job control" + default y + depends on CONFIG_ASH + help + Enable job control in the ash shell. + +config CONFIG_ASH_ALIAS + bool " Enable alias support" + default y + depends on CONFIG_ASH + help + Enable alias support in the ash shell. + +config CONFIG_ASH_MATH_SUPPORT + bool " Enable Posix math support" + default y + depends on CONFIG_ASH + help + Enable math support in the ash shell. + +config CONFIG_ASH_MATH_SUPPORT_64 + bool " Extend Posix math support to 64 bit" + default n + depends on CONFIG_ASH_MATH_SUPPORT + help + Enable 64-bit math support in the ash shell. This will make + the shell slightly larger, but will allow computation with very + large numbers. + +config CONFIG_ASH_GETOPTS + bool " Enable getopts builtin to parse positional parameters" + default n + depends on CONFIG_ASH + help + Enable getopts builtin in the ash shell. + +config CONFIG_ASH_CMDCMD + bool " Enable cmdcmd to override shell builtins" + default n + depends on CONFIG_ASH + help + Enable support for the ash 'command' builtin, which allows + you to run the specified command with the specified arguments, + even when there is an ash builtin command with the same name. + +config CONFIG_ASH_MAIL + bool " Check for new mail on interactive shells" + default y + depends on CONFIG_ASH + help + Enable "check for new mail" in the ash shell. + +config CONFIG_ASH_OPTIMIZE_FOR_SIZE + bool " Optimize for size instead of speed" + default y + depends on CONFIG_ASH + help + Compile ash for reduced size at price of speed. + +config CONFIG_ASH_RANDOM_SUPPORT + bool " Enable pseudorandom generator and variable $RANDOM" + default n + depends on CONFIG_ASH + help + Enable pseudorandom generator and dynamic variable "$RANDOM". + Each read of "$RANDOM" will generate a new pseudorandom value. + You can reset the generator by using a specified start value. + After "unset RANDOM" then generator will switch off and this + variable will no longer have special treatment. + +config CONFIG_HUSH + bool "hush" + default n + help + hush is a very small shell (just 18k) and it has fairly complete + Bourne shell grammar. It even handles all the normal flow control + options such as if/then/elif/else/fi, for/in/do/done, while loops, + etc. + + It does not handle case/esac, select, function, here documents ( << + word ), arithmetic expansion, aliases, brace expansion, tilde + expansion, &> and >& redirection of stdout+stderr, etc. + + +config CONFIG_LASH + bool "lash" + default n + help + lash is the very smallest shell (adds just 10k) and it is quite + usable as a command prompt, but it is not suitable for any but the + most trivial scripting (such as an initrd that calls insmod a few + times) since it does not understand any Bourne shell grammar. It + does handle pipes, redirects, and job control though. Adding in + command editing makes it a very nice lightweight command prompt. + + +config CONFIG_MSH + bool "msh" + default n + help + The minix shell (adds just 30k) is quite complete and handles things + like for/do/done, case/esac and all the things you expect a Bourne + shell to do. It is not always pedantically correct about Bourne + shell grammar (try running the shell testscript "tests/sh.testcases" + on it and compare vs bash) but for most things it works quite well. + It also uses only vfork, so it can be used on uClinux systems. + +comment "Bourne Shell Options" + depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH + +config CONFIG_FEATURE_SH_EXTRA_QUIET + bool "Hide message on interactive shell startup" + default n + depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH + help + Remove the busybox introduction when starting a shell. + +config CONFIG_FEATURE_SH_STANDALONE_SHELL + bool "Standalone shell" + default n + depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH + help + This option causes the selected busybox shell to use busybox applets + in preference to executables in the PATH whenever possible. For + example, entering the command 'ifconfig' into the shell would cause + busybox to use the ifconfig busybox applet. Specifying the fully + qualified executable name, such as '/sbin/ifconfig' will still + execute the /sbin/ifconfig executable on the filesystem. This option + is generally used when creating a staticly linked version of busybox + for use as a rescue shell, in the event that you screw up your system. + + Note that when using this option, the shell will attempt to directly + run '/bin/busybox'. If you do not have the busybox binary sitting in + that exact location with that exact name, this option will not work at + all. + +config CONFIG_FEATURE_COMMAND_EDITING + bool "command line editing" + default n + depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH + help + Enable command editing in shell. + +config CONFIG_FEATURE_COMMAND_HISTORY + int "history size" + default 15 + depends on CONFIG_FEATURE_COMMAND_EDITING + help + Specify command history size in shell. + +config CONFIG_FEATURE_COMMAND_SAVEHISTORY + bool "history saving" + default n + depends on CONFIG_ASH && CONFIG_FEATURE_COMMAND_EDITING + help + Enable history saving in ash shell. + +config CONFIG_FEATURE_COMMAND_TAB_COMPLETION + bool "tab completion" + default n + depends on CONFIG_FEATURE_COMMAND_EDITING + help + Enable tab completion in shell. + +config CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION + bool "username completion" + default n + depends on CONFIG_FEATURE_COMMAND_TAB_COMPLETION + help + Enable username completion in shell. + +config CONFIG_FEATURE_SH_FANCY_PROMPT + bool "Fancy shell prompts" + default n + depends on CONFIG_FEATURE_COMMAND_EDITING + help + Setting this option allows for prompts to use things like \w and + \$ and also using escape codes. + +endmenu diff --git a/busybox/shell/Makefile b/busybox/shell/Makefile new file mode 100644 index 000000000..bd1dad6f3 --- /dev/null +++ b/busybox/shell/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_builddir=.. +srcdir=$(top_srcdir)/shell +SHELL_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/shell/Makefile.in b/busybox/shell/Makefile.in new file mode 100644 index 000000000..61b2846ac --- /dev/null +++ b/busybox/shell/Makefile.in @@ -0,0 +1,40 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 + + +SHELL_AR:=shell.a +ifndef $(SHELL_DIR) +SHELL_DIR:=$(top_builddir)/shell/ +endif +srcdir=$(top_srcdir)/shell + +SHELLT-y:= +SHELLT-$(CONFIG_ASH) += ash.o +SHELLT-$(CONFIG_HUSH) += hush.o +SHELLT-$(CONFIG_LASH) += lash.o +SHELLT-$(CONFIG_MSH) += msh.o +SHELLT-$(CONFIG_FEATURE_COMMAND_EDITING) += cmdedit.o + +libraries-y+=$(SHELL_DIR)$(SHELL_AR) + +$(SHELL_DIR)$(SHELL_AR): $(patsubst %,$(SHELL_DIR)%, $(SHELLT-y)) + $(AR) -ro $@ $(patsubst %,$(SHELL_DIR)%, $(SHELLT-y)) + +$(SHELL_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/shell/ash.c b/busybox/shell/ash.c new file mode 100644 index 000000000..d9ea2b0f3 --- /dev/null +++ b/busybox/shell/ash.c @@ -0,0 +1,13586 @@ +/* vi: set sw=4 ts=4: */ +/* + * ash shell port for busybox + * + * Copyright (c) 1989, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Copyright (c) 1997-2003 Herbert Xu + * was re-ported from NetBSD and debianized. + * + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Original BSD copyright notice is retained at the end of this file. + */ + +/* + * rewrite arith.y to micro stack based cryptic algorithm by + * Copyright (c) 2001 Aaron Lehmann + * + * Modified by Paul Mundt (c) 2004 to support + * dynamic variables. + * + * Modified by Vladimir Oleynik (c) 2001-2004 to be + * used in busybox and size optimizations, + * rewrote arith (see notes to this), added locale support, + * rewrote dynamic variables. + * + */ + + +/* + * The follow should be set to reflect the type of system you have: + * JOBS -> 1 if you have Berkeley job control, 0 otherwise. + * define SYSV if you are running under System V. + * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) + * define DEBUG=2 to compile in and turn on debugging. + * + * When debugging is on, debugging info will be written to ./trace and + * a quit signal will generate a core dump. + */ + + + +#define IFS_BROKEN + +#define PROFILE 0 + +#ifdef DEBUG +#define _GNU_SOURCE +#endif + +#include +#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 +#include + + +#include "busybox.h" +#include "pwd_.h" + +#ifdef CONFIG_ASH_JOB_CONTROL +#define JOBS 1 +#else +#undef JOBS +#endif + +#if JOBS +#include +#endif + +#include "cmdedit.h" + +#ifdef __GLIBC__ +/* glibc sucks */ +static int *dash_errno; +#undef errno +#define errno (*dash_errno) +#endif + +#if defined(__uClinux__) +#error "Do not even bother, ash will not run on uClinux" +#endif + +#ifdef DEBUG +#define _DIAGASSERT(assert_expr) assert(assert_expr) +#else +#define _DIAGASSERT(assert_expr) +#endif + + +#ifdef CONFIG_ASH_ALIAS +/* $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $ */ + +#define ALIASINUSE 1 +#define ALIASDEAD 2 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +static struct alias *lookupalias(const char *, int); +static int aliascmd(int, char **); +static int unaliascmd(int, char **); +static void rmaliases(void); +static int unalias(const char *); +static void printalias(const struct alias *); +#endif + +/* $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $ */ + + +static void setpwd(const char *, int); + +/* $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $ */ + + +/* + * Types of operations (passed to the errmsg routine). + */ + + +static const char not_found_msg[] = "%s: not found"; + + +#define E_OPEN "No such file" /* opening a file */ +#define E_CREAT "Directory nonexistent" /* creating a file */ +#define E_EXEC not_found_msg+4 /* executing a program */ + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +struct jmploc { + jmp_buf loc; +}; + +static struct jmploc *handler; +static int exception; +static volatile int suppressint; +static volatile sig_atomic_t intpending; + +static int exerrno; /* Last exec error, error for EXEXEC */ + +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXSHELLPROC 2 /* execute a shell procedure */ +#define EXEXEC 3 /* command execution failed */ +#define EXEXIT 4 /* exit the shell */ +#define EXSIG 5 /* trapped signal in wait(1) */ + + +/* do we generate EXSIG events */ +static int exsig; +/* last pending signal */ +static volatile sig_atomic_t pendingsigs; + +/* + * These macros allow the user to suspend the handling of interrupt signals + * over a period of time. This is similar to SIGHOLD to or sigblock, but + * much more efficient and portable. (But hacking the kernel is so much + * more fun than worrying about efficiency and portability. :-)) + */ + +#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); }) +#define INTOFF \ + ({ \ + suppressint++; \ + xbarrier(); \ + 0; \ + }) +#define SAVEINT(v) ((v) = suppressint) +#define RESTOREINT(v) \ + ({ \ + xbarrier(); \ + if ((suppressint = (v)) == 0 && intpending) onint(); \ + 0; \ + }) +#define EXSIGON() \ + ({ \ + exsig++; \ + xbarrier(); \ + if (pendingsigs) \ + exraise(EXSIG); \ + 0; \ + }) +/* EXSIG is turned off by evalbltin(). */ + + +static void exraise(int) __attribute__((__noreturn__)); +static void onint(void) __attribute__((__noreturn__)); + +static void error(const char *, ...) __attribute__((__noreturn__)); +static void exerror(int, const char *, ...) __attribute__((__noreturn__)); + +static void sh_warnx(const char *, ...); + +#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE +static void +inton(void) { + if (--suppressint == 0 && intpending) { + onint(); + } +} +#define INTON inton() +static void forceinton(void) +{ + suppressint = 0; + if (intpending) + onint(); +} +#define FORCEINTON forceinton() +#else +#define INTON \ + ({ \ + xbarrier(); \ + if (--suppressint == 0 && intpending) onint(); \ + 0; \ + }) +#define FORCEINTON \ + ({ \ + xbarrier(); \ + suppressint = 0; \ + if (intpending) onint(); \ + 0; \ + }) +#endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */ + +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ + +#if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__) +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) +#endif + +/* $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $ */ + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +/* + * expandarg() flags + */ +#define EXP_FULL 0x1 /* perform word splitting & file globbing */ +#define EXP_TILDE 0x2 /* do normal tilde expansion */ +#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ +#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ +#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ +#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ +#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ +#define EXP_WORD 0x80 /* expand word in parameter expansion */ +#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ + + +union node; +static void expandarg(union node *, struct arglist *, int); +#define rmescapes(p) _rmescapes((p), 0) +static char *_rmescapes(char *, int); +static int casematch(union node *, char *); + +#ifdef CONFIG_ASH_MATH_SUPPORT +static void expari(int); +#endif + +/* $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $ */ + +static char *commandname; /* currently executing command */ +static struct strlist *cmdenviron; /* environment for builtin command */ +static int exitstatus; /* exit status of last command */ +static int back_exitstatus; /* exit status of backquoted command */ + + +struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ + char *buf; /* buffer */ + int nleft; /* number of chars in buffer */ + struct job *jp; /* job structure for command */ +}; + +/* + * This file was generated by the mknodes program. + */ + +#define NCMD 0 +#define NPIPE 1 +#define NREDIR 2 +#define NBACKGND 3 +#define NSUBSHELL 4 +#define NAND 5 +#define NOR 6 +#define NSEMI 7 +#define NIF 8 +#define NWHILE 9 +#define NUNTIL 10 +#define NFOR 11 +#define NCASE 12 +#define NCLIST 13 +#define NDEFUN 14 +#define NARG 15 +#define NTO 16 +#define NCLOBBER 17 +#define NFROM 18 +#define NFROMTO 19 +#define NAPPEND 20 +#define NTOFD 21 +#define NFROMFD 22 +#define NHERE 23 +#define NXHERE 24 +#define NNOT 25 + + + +struct ncmd { + int type; + union node *assign; + union node *args; + union node *redirect; +}; + + +struct npipe { + int type; + int backgnd; + struct nodelist *cmdlist; +}; + + +struct nredir { + int type; + union node *n; + union node *redirect; +}; + + +struct nbinary { + int type; + union node *ch1; + union node *ch2; +}; + + +struct nif { + int type; + union node *test; + union node *ifpart; + union node *elsepart; +}; + + +struct nfor { + int type; + union node *args; + union node *body; + char *var; +}; + + +struct ncase { + int type; + union node *expr; + union node *cases; +}; + + +struct nclist { + int type; + union node *next; + union node *pattern; + union node *body; +}; + + +struct narg { + int type; + union node *next; + char *text; + struct nodelist *backquote; +}; + + +struct nfile { + int type; + union node *next; + int fd; + union node *fname; + char *expfname; +}; + + +struct ndup { + int type; + union node *next; + int fd; + int dupfd; + union node *vname; +}; + + +struct nhere { + int type; + union node *next; + int fd; + union node *doc; +}; + + +struct nnot { + int type; + union node *com; +}; + + +union node { + int type; + struct ncmd ncmd; + struct npipe npipe; + struct nredir nredir; + struct nbinary nbinary; + struct nif nif; + struct nfor nfor; + struct ncase ncase; + struct nclist nclist; + struct narg narg; + struct nfile nfile; + struct ndup ndup; + struct nhere nhere; + struct nnot nnot; +}; + + +struct nodelist { + struct nodelist *next; + union node *n; +}; + + +struct funcnode { + int count; + union node n; +}; + + +static void freefunc(struct funcnode *); +/* $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $ */ + +/* control characters in argument strings */ +#define CTL_FIRST '\201' /* first 'special' character */ +#define CTLESC '\201' /* escape next character */ +#define CTLVAR '\202' /* variable defn */ +#define CTLENDVAR '\203' +#define CTLBACKQ '\204' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ +/* CTLBACKQ | CTLQUOTE == '\205' */ +#define CTLARI '\206' /* arithmetic expression */ +#define CTLENDARI '\207' +#define CTLQUOTEMARK '\210' +#define CTL_LAST '\210' /* last 'special' character */ + +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 0x0f /* type of variable substitution */ +#define VSNUL 0x10 /* colon--treat the empty string as unset */ +#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ + +/* values of VSTYPE field */ +#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ +#define VSMINUS 0x2 /* ${var-text} */ +#define VSPLUS 0x3 /* ${var+text} */ +#define VSQUESTION 0x4 /* ${var?message} */ +#define VSASSIGN 0x5 /* ${var=text} */ +#define VSTRIMRIGHT 0x6 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ +#define VSTRIMLEFT 0x8 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ +#define VSLENGTH 0xa /* ${#var} */ + +/* values of checkkwd variable */ +#define CHKALIAS 0x1 +#define CHKKWD 0x2 +#define CHKNL 0x4 + +#define IBUFSIZ (BUFSIZ + 1) + +/* + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL, so we use the address of a variable that + * happens to be handy. + */ +static int plinno = 1; /* input line number */ + +/* number of characters left in input buffer */ +static int parsenleft; /* copy of parsefile->nleft */ +static int parselleft; /* copy of parsefile->lleft */ + +/* next character in input buffer */ +static char *parsenextc; /* copy of parsefile->nextc */ + +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; +#ifdef CONFIG_ASH_ALIAS + struct alias *ap; /* if push was associated with an alias */ +#endif + char *string; /* remember the string since it may change */ +}; + +struct parsefile { + struct parsefile *prev; /* preceding file on stack */ + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in this line */ + int lleft; /* number of chars left in this buffer */ + char *nextc; /* next char in buffer */ + char *buf; /* input buffer */ + struct strpush *strpush; /* for pushing strings at this level */ + struct strpush basestrpush; /* so pushing one is fast */ +}; + +static struct parsefile basepf; /* top level input file */ +static char basebuf[IBUFSIZ]; /* buffer for top level input file */ +static struct parsefile *parsefile = &basepf; /* current input file */ + + +static int tokpushback; /* last token pushed back */ +#define NEOF ((union node *)&tokpushback) +static int parsebackquote; /* nonzero if we are inside backquotes */ +static int doprompt; /* if set, prompt the user */ +static int needprompt; /* true if interactive and at start of line */ +static int lasttoken; /* last token read */ +static char *wordtext; /* text of last word returned by readtoken */ +static int checkkwd; +static struct nodelist *backquotelist; +static union node *redirnode; +static struct heredoc *heredoc; +static int quoteflag; /* set if (part of) last token was quoted */ +static int startlinno; /* line # where last token started */ + +static union node *parsecmd(int); +static void fixredir(union node *, const char *, int); +static const char *const *findkwd(const char *); +static char *endofname(const char *); + +/* $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */ + +typedef void *pointer; + +static char nullstr[1]; /* zero length string */ +static const char spcstr[] = " "; +static const char snlfmt[] = "%s\n"; +static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; +static const char illnum[] = "Illegal number: %s"; +static const char homestr[] = "HOME"; + +#ifdef DEBUG +#define TRACE(param) trace param +#define TRACEV(param) tracev param +#else +#define TRACE(param) +#define TRACEV(param) +#endif + +#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) +#define __builtin_expect(x, expected_value) (x) +#endif + +#define xlikely(x) __builtin_expect((x),1) + + +#define TEOF 0 +#define TNL 1 +#define TREDIR 2 +#define TWORD 3 +#define TSEMI 4 +#define TBACKGND 5 +#define TAND 6 +#define TOR 7 +#define TPIPE 8 +#define TLP 9 +#define TRP 10 +#define TENDCASE 11 +#define TENDBQUOTE 12 +#define TNOT 13 +#define TCASE 14 +#define TDO 15 +#define TDONE 16 +#define TELIF 17 +#define TELSE 18 +#define TESAC 19 +#define TFI 20 +#define TFOR 21 +#define TIF 22 +#define TIN 23 +#define TTHEN 24 +#define TUNTIL 25 +#define TWHILE 26 +#define TBEGIN 27 +#define TEND 28 + +/* first char is indicating which tokens mark the end of a list */ +static const char *const tokname_array[] = { + "\1end of file", + "\0newline", + "\0redirection", + "\0word", + "\0;", + "\0&", + "\0&&", + "\0||", + "\0|", + "\0(", + "\1)", + "\1;;", + "\1`", +#define KWDOFFSET 13 + /* the following are keywords */ + "\0!", + "\0case", + "\1do", + "\1done", + "\1elif", + "\1else", + "\1esac", + "\1fi", + "\0for", + "\0if", + "\0in", + "\1then", + "\0until", + "\0while", + "\0{", + "\1}", +}; + +static const char *tokname(int tok) +{ + static char buf[16]; + + if (tok >= TSEMI) + buf[0] = '"'; + sprintf(buf + (tok >= TSEMI), "%s%c", + tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0)); + return buf; +} + +/* $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $ */ + +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ + +#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1) +/* + * It appears that grabstackstr() will barf with such alignments + * because stalloc() will return a string allocated in a new stackblock. + */ +#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) + +/* + * This file was generated by the mksyntax program. + */ + + +/* Syntax classes */ +#define CWORD 0 /* character is nothing special */ +#define CNL 1 /* newline character */ +#define CBACK 2 /* a backslash character */ +#define CSQUOTE 3 /* single quote */ +#define CDQUOTE 4 /* double quote */ +#define CENDQUOTE 5 /* a terminating quote */ +#define CBQUOTE 6 /* backwards single quote */ +#define CVAR 7 /* a dollar sign */ +#define CENDVAR 8 /* a '}' character */ +#define CLP 9 /* a left paren in arithmetic */ +#define CRP 10 /* a right paren in arithmetic */ +#define CENDFILE 11 /* end of file */ +#define CCTL 12 /* like CWORD, except it must be escaped */ +#define CSPCL 13 /* these terminate a word */ +#define CIGN 14 /* character should be ignored */ + +#ifdef CONFIG_ASH_ALIAS +#define SYNBASE 130 +#define PEOF -130 +#define PEOA -129 +#define PEOA_OR_PEOF PEOA +#else +#define SYNBASE 129 +#define PEOF -129 +#define PEOA_OR_PEOF PEOF +#endif + +#define is_digit(c) ((unsigned)((c) - '0') <= 9) +#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) +#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) + +/* + * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise + * (assuming ascii char codes, as the original implementation did) + */ +#define is_special(c) \ + ( (((unsigned int)c) - 33 < 32) \ + && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1)) + +#define digit_val(c) ((c) - '0') + +/* + * This file was generated by the mksyntax program. + */ + +#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE +#define USE_SIT_FUNCTION +#endif + +/* number syntax index */ +#define BASESYNTAX 0 /* not in quotes */ +#define DQSYNTAX 1 /* in double quotes */ +#define SQSYNTAX 2 /* in single quotes */ +#define ARISYNTAX 3 /* in arithmetic */ + +#ifdef CONFIG_ASH_MATH_SUPPORT +static const char S_I_T[][4] = { +#ifdef CONFIG_ASH_ALIAS + {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */ +#endif + {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */ + {CNL, CNL, CNL, CNL}, /* 2, \n */ + {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */ + {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */ + {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */ + {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */ + {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */ + {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */ + {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */ + {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */ + {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */ +#ifndef USE_SIT_FUNCTION + {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */ + {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */ + {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */ +#endif +}; +#else +static const char S_I_T[][3] = { +#ifdef CONFIG_ASH_ALIAS + {CSPCL, CIGN, CIGN}, /* 0, PEOA */ +#endif + {CSPCL, CWORD, CWORD}, /* 1, ' ' */ + {CNL, CNL, CNL}, /* 2, \n */ + {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */ + {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */ + {CVAR, CVAR, CWORD}, /* 5, $ */ + {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */ + {CSPCL, CWORD, CWORD}, /* 7, ( */ + {CSPCL, CWORD, CWORD}, /* 8, ) */ + {CBACK, CBACK, CCTL}, /* 9, \ */ + {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */ + {CENDVAR, CENDVAR, CWORD}, /* 11, } */ +#ifndef USE_SIT_FUNCTION + {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */ + {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */ + {CCTL, CCTL, CCTL} /* 14, CTLESC ... */ +#endif +}; +#endif /* CONFIG_ASH_MATH_SUPPORT */ + +#ifdef USE_SIT_FUNCTION + +#define U_C(c) ((unsigned char)(c)) + +static int SIT(int c, int syntax) +{ + static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; +#ifdef CONFIG_ASH_ALIAS + static const char syntax_index_table[] = { + 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ + 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ + 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ + 11, 3 /* "}~" */ + }; +#else + static const char syntax_index_table[] = { + 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ + 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ + 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ + 10, 2 /* "}~" */ + }; +#endif + const char *s; + int indx; + + if (c == PEOF) /* 2^8+2 */ + return CENDFILE; +#ifdef CONFIG_ASH_ALIAS + if (c == PEOA) /* 2^8+1 */ + indx = 0; + else +#endif + if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK)) + return CCTL; + else { + s = strchr(spec_symbls, c); + if (s == 0 || *s == 0) + return CWORD; + indx = syntax_index_table[(s - spec_symbls)]; + } + return S_I_T[indx][syntax]; +} + +#else /* USE_SIT_FUNCTION */ + +#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax] + +#ifdef CONFIG_ASH_ALIAS +#define CSPCL_CIGN_CIGN_CIGN 0 +#define CSPCL_CWORD_CWORD_CWORD 1 +#define CNL_CNL_CNL_CNL 2 +#define CWORD_CCTL_CCTL_CWORD 3 +#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4 +#define CVAR_CVAR_CWORD_CVAR 5 +#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6 +#define CSPCL_CWORD_CWORD_CLP 7 +#define CSPCL_CWORD_CWORD_CRP 8 +#define CBACK_CBACK_CCTL_CBACK 9 +#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10 +#define CENDVAR_CENDVAR_CWORD_CENDVAR 11 +#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12 +#define CWORD_CWORD_CWORD_CWORD 13 +#define CCTL_CCTL_CCTL_CCTL 14 +#else +#define CSPCL_CWORD_CWORD_CWORD 0 +#define CNL_CNL_CNL_CNL 1 +#define CWORD_CCTL_CCTL_CWORD 2 +#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3 +#define CVAR_CVAR_CWORD_CVAR 4 +#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5 +#define CSPCL_CWORD_CWORD_CLP 6 +#define CSPCL_CWORD_CWORD_CRP 7 +#define CBACK_CBACK_CCTL_CBACK 8 +#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9 +#define CENDVAR_CENDVAR_CWORD_CENDVAR 10 +#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11 +#define CWORD_CWORD_CWORD_CWORD 12 +#define CCTL_CCTL_CCTL_CCTL 13 +#endif + +static const char syntax_index_table[258] = { + /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */ + /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, +#ifdef CONFIG_ASH_ALIAS + /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN, +#endif + /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD, + /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL, + /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL, + /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL, + /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL, + /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL, + /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL, + /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, + /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, + /* 11 -119 */ CWORD_CWORD_CWORD_CWORD, + /* 12 -118 */ CWORD_CWORD_CWORD_CWORD, + /* 13 -117 */ CWORD_CWORD_CWORD_CWORD, + /* 14 -116 */ CWORD_CWORD_CWORD_CWORD, + /* 15 -115 */ CWORD_CWORD_CWORD_CWORD, + /* 16 -114 */ CWORD_CWORD_CWORD_CWORD, + /* 17 -113 */ CWORD_CWORD_CWORD_CWORD, + /* 18 -112 */ CWORD_CWORD_CWORD_CWORD, + /* 19 -111 */ CWORD_CWORD_CWORD_CWORD, + /* 20 -110 */ CWORD_CWORD_CWORD_CWORD, + /* 21 -109 */ CWORD_CWORD_CWORD_CWORD, + /* 22 -108 */ CWORD_CWORD_CWORD_CWORD, + /* 23 -107 */ CWORD_CWORD_CWORD_CWORD, + /* 24 -106 */ CWORD_CWORD_CWORD_CWORD, + /* 25 -105 */ CWORD_CWORD_CWORD_CWORD, + /* 26 -104 */ CWORD_CWORD_CWORD_CWORD, + /* 27 -103 */ CWORD_CWORD_CWORD_CWORD, + /* 28 -102 */ CWORD_CWORD_CWORD_CWORD, + /* 29 -101 */ CWORD_CWORD_CWORD_CWORD, + /* 30 -100 */ CWORD_CWORD_CWORD_CWORD, + /* 31 -99 */ CWORD_CWORD_CWORD_CWORD, + /* 32 -98 */ CWORD_CWORD_CWORD_CWORD, + /* 33 -97 */ CWORD_CWORD_CWORD_CWORD, + /* 34 -96 */ CWORD_CWORD_CWORD_CWORD, + /* 35 -95 */ CWORD_CWORD_CWORD_CWORD, + /* 36 -94 */ CWORD_CWORD_CWORD_CWORD, + /* 37 -93 */ CWORD_CWORD_CWORD_CWORD, + /* 38 -92 */ CWORD_CWORD_CWORD_CWORD, + /* 39 -91 */ CWORD_CWORD_CWORD_CWORD, + /* 40 -90 */ CWORD_CWORD_CWORD_CWORD, + /* 41 -89 */ CWORD_CWORD_CWORD_CWORD, + /* 42 -88 */ CWORD_CWORD_CWORD_CWORD, + /* 43 -87 */ CWORD_CWORD_CWORD_CWORD, + /* 44 -86 */ CWORD_CWORD_CWORD_CWORD, + /* 45 -85 */ CWORD_CWORD_CWORD_CWORD, + /* 46 -84 */ CWORD_CWORD_CWORD_CWORD, + /* 47 -83 */ CWORD_CWORD_CWORD_CWORD, + /* 48 -82 */ CWORD_CWORD_CWORD_CWORD, + /* 49 -81 */ CWORD_CWORD_CWORD_CWORD, + /* 50 -80 */ CWORD_CWORD_CWORD_CWORD, + /* 51 -79 */ CWORD_CWORD_CWORD_CWORD, + /* 52 -78 */ CWORD_CWORD_CWORD_CWORD, + /* 53 -77 */ CWORD_CWORD_CWORD_CWORD, + /* 54 -76 */ CWORD_CWORD_CWORD_CWORD, + /* 55 -75 */ CWORD_CWORD_CWORD_CWORD, + /* 56 -74 */ CWORD_CWORD_CWORD_CWORD, + /* 57 -73 */ CWORD_CWORD_CWORD_CWORD, + /* 58 -72 */ CWORD_CWORD_CWORD_CWORD, + /* 59 -71 */ CWORD_CWORD_CWORD_CWORD, + /* 60 -70 */ CWORD_CWORD_CWORD_CWORD, + /* 61 -69 */ CWORD_CWORD_CWORD_CWORD, + /* 62 -68 */ CWORD_CWORD_CWORD_CWORD, + /* 63 -67 */ CWORD_CWORD_CWORD_CWORD, + /* 64 -66 */ CWORD_CWORD_CWORD_CWORD, + /* 65 -65 */ CWORD_CWORD_CWORD_CWORD, + /* 66 -64 */ CWORD_CWORD_CWORD_CWORD, + /* 67 -63 */ CWORD_CWORD_CWORD_CWORD, + /* 68 -62 */ CWORD_CWORD_CWORD_CWORD, + /* 69 -61 */ CWORD_CWORD_CWORD_CWORD, + /* 70 -60 */ CWORD_CWORD_CWORD_CWORD, + /* 71 -59 */ CWORD_CWORD_CWORD_CWORD, + /* 72 -58 */ CWORD_CWORD_CWORD_CWORD, + /* 73 -57 */ CWORD_CWORD_CWORD_CWORD, + /* 74 -56 */ CWORD_CWORD_CWORD_CWORD, + /* 75 -55 */ CWORD_CWORD_CWORD_CWORD, + /* 76 -54 */ CWORD_CWORD_CWORD_CWORD, + /* 77 -53 */ CWORD_CWORD_CWORD_CWORD, + /* 78 -52 */ CWORD_CWORD_CWORD_CWORD, + /* 79 -51 */ CWORD_CWORD_CWORD_CWORD, + /* 80 -50 */ CWORD_CWORD_CWORD_CWORD, + /* 81 -49 */ CWORD_CWORD_CWORD_CWORD, + /* 82 -48 */ CWORD_CWORD_CWORD_CWORD, + /* 83 -47 */ CWORD_CWORD_CWORD_CWORD, + /* 84 -46 */ CWORD_CWORD_CWORD_CWORD, + /* 85 -45 */ CWORD_CWORD_CWORD_CWORD, + /* 86 -44 */ CWORD_CWORD_CWORD_CWORD, + /* 87 -43 */ CWORD_CWORD_CWORD_CWORD, + /* 88 -42 */ CWORD_CWORD_CWORD_CWORD, + /* 89 -41 */ CWORD_CWORD_CWORD_CWORD, + /* 90 -40 */ CWORD_CWORD_CWORD_CWORD, + /* 91 -39 */ CWORD_CWORD_CWORD_CWORD, + /* 92 -38 */ CWORD_CWORD_CWORD_CWORD, + /* 93 -37 */ CWORD_CWORD_CWORD_CWORD, + /* 94 -36 */ CWORD_CWORD_CWORD_CWORD, + /* 95 -35 */ CWORD_CWORD_CWORD_CWORD, + /* 96 -34 */ CWORD_CWORD_CWORD_CWORD, + /* 97 -33 */ CWORD_CWORD_CWORD_CWORD, + /* 98 -32 */ CWORD_CWORD_CWORD_CWORD, + /* 99 -31 */ CWORD_CWORD_CWORD_CWORD, + /* 100 -30 */ CWORD_CWORD_CWORD_CWORD, + /* 101 -29 */ CWORD_CWORD_CWORD_CWORD, + /* 102 -28 */ CWORD_CWORD_CWORD_CWORD, + /* 103 -27 */ CWORD_CWORD_CWORD_CWORD, + /* 104 -26 */ CWORD_CWORD_CWORD_CWORD, + /* 105 -25 */ CWORD_CWORD_CWORD_CWORD, + /* 106 -24 */ CWORD_CWORD_CWORD_CWORD, + /* 107 -23 */ CWORD_CWORD_CWORD_CWORD, + /* 108 -22 */ CWORD_CWORD_CWORD_CWORD, + /* 109 -21 */ CWORD_CWORD_CWORD_CWORD, + /* 110 -20 */ CWORD_CWORD_CWORD_CWORD, + /* 111 -19 */ CWORD_CWORD_CWORD_CWORD, + /* 112 -18 */ CWORD_CWORD_CWORD_CWORD, + /* 113 -17 */ CWORD_CWORD_CWORD_CWORD, + /* 114 -16 */ CWORD_CWORD_CWORD_CWORD, + /* 115 -15 */ CWORD_CWORD_CWORD_CWORD, + /* 116 -14 */ CWORD_CWORD_CWORD_CWORD, + /* 117 -13 */ CWORD_CWORD_CWORD_CWORD, + /* 118 -12 */ CWORD_CWORD_CWORD_CWORD, + /* 119 -11 */ CWORD_CWORD_CWORD_CWORD, + /* 120 -10 */ CWORD_CWORD_CWORD_CWORD, + /* 121 -9 */ CWORD_CWORD_CWORD_CWORD, + /* 122 -8 */ CWORD_CWORD_CWORD_CWORD, + /* 123 -7 */ CWORD_CWORD_CWORD_CWORD, + /* 124 -6 */ CWORD_CWORD_CWORD_CWORD, + /* 125 -5 */ CWORD_CWORD_CWORD_CWORD, + /* 126 -4 */ CWORD_CWORD_CWORD_CWORD, + /* 127 -3 */ CWORD_CWORD_CWORD_CWORD, + /* 128 -2 */ CWORD_CWORD_CWORD_CWORD, + /* 129 -1 */ CWORD_CWORD_CWORD_CWORD, + /* 130 0 */ CWORD_CWORD_CWORD_CWORD, + /* 131 1 */ CWORD_CWORD_CWORD_CWORD, + /* 132 2 */ CWORD_CWORD_CWORD_CWORD, + /* 133 3 */ CWORD_CWORD_CWORD_CWORD, + /* 134 4 */ CWORD_CWORD_CWORD_CWORD, + /* 135 5 */ CWORD_CWORD_CWORD_CWORD, + /* 136 6 */ CWORD_CWORD_CWORD_CWORD, + /* 137 7 */ CWORD_CWORD_CWORD_CWORD, + /* 138 8 */ CWORD_CWORD_CWORD_CWORD, + /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD, + /* 140 10 "\n" */ CNL_CNL_CNL_CNL, + /* 141 11 */ CWORD_CWORD_CWORD_CWORD, + /* 142 12 */ CWORD_CWORD_CWORD_CWORD, + /* 143 13 */ CWORD_CWORD_CWORD_CWORD, + /* 144 14 */ CWORD_CWORD_CWORD_CWORD, + /* 145 15 */ CWORD_CWORD_CWORD_CWORD, + /* 146 16 */ CWORD_CWORD_CWORD_CWORD, + /* 147 17 */ CWORD_CWORD_CWORD_CWORD, + /* 148 18 */ CWORD_CWORD_CWORD_CWORD, + /* 149 19 */ CWORD_CWORD_CWORD_CWORD, + /* 150 20 */ CWORD_CWORD_CWORD_CWORD, + /* 151 21 */ CWORD_CWORD_CWORD_CWORD, + /* 152 22 */ CWORD_CWORD_CWORD_CWORD, + /* 153 23 */ CWORD_CWORD_CWORD_CWORD, + /* 154 24 */ CWORD_CWORD_CWORD_CWORD, + /* 155 25 */ CWORD_CWORD_CWORD_CWORD, + /* 156 26 */ CWORD_CWORD_CWORD_CWORD, + /* 157 27 */ CWORD_CWORD_CWORD_CWORD, + /* 158 28 */ CWORD_CWORD_CWORD_CWORD, + /* 159 29 */ CWORD_CWORD_CWORD_CWORD, + /* 160 30 */ CWORD_CWORD_CWORD_CWORD, + /* 161 31 */ CWORD_CWORD_CWORD_CWORD, + /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD, + /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD, + /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD, + /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD, + /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR, + /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD, + /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD, + /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD, + /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP, + /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP, + /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD, + /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD, + /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD, + /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD, + /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD, + /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD, + /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD, + /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD, + /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD, + /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD, + /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD, + /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD, + /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD, + /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD, + /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD, + /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD, + /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD, + /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD, + /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD, + /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD, + /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD, + /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD, + /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD, + /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD, + /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD, + /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD, + /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD, + /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD, + /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD, + /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD, + /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD, + /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD, + /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD, + /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD, + /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD, + /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD, + /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD, + /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD, + /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD, + /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD, + /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD, + /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD, + /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD, + /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD, + /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD, + /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD, + /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD, + /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD, + /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD, + /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD, + /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK, + /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD, + /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD, + /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD, + /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE, + /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD, + /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD, + /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD, + /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD, + /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD, + /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD, + /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD, + /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD, + /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD, + /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD, + /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD, + /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD, + /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD, + /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD, + /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD, + /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD, + /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD, + /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD, + /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD, + /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD, + /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD, + /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD, + /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD, + /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD, + /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD, + /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD, + /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD, + /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD, + /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR, + /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD, + /* 257 127 */ CWORD_CWORD_CWORD_CWORD, +}; + +#endif /* USE_SIT_FUNCTION */ + +/* $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $ */ + + +#define ATABSIZE 39 + +static int funcblocksize; /* size of structures in function */ +static int funcstringsize; /* size of strings in node */ +static pointer funcblock; /* block to allocate function from */ +static char *funcstring; /* block to allocate strings from */ + +static const short nodesize[26] = { + SHELL_ALIGN(sizeof (struct ncmd)), + SHELL_ALIGN(sizeof (struct npipe)), + SHELL_ALIGN(sizeof (struct nredir)), + SHELL_ALIGN(sizeof (struct nredir)), + SHELL_ALIGN(sizeof (struct nredir)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nif)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nbinary)), + SHELL_ALIGN(sizeof (struct nfor)), + SHELL_ALIGN(sizeof (struct ncase)), + SHELL_ALIGN(sizeof (struct nclist)), + SHELL_ALIGN(sizeof (struct narg)), + SHELL_ALIGN(sizeof (struct narg)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct nfile)), + SHELL_ALIGN(sizeof (struct ndup)), + SHELL_ALIGN(sizeof (struct ndup)), + SHELL_ALIGN(sizeof (struct nhere)), + SHELL_ALIGN(sizeof (struct nhere)), + SHELL_ALIGN(sizeof (struct nnot)), +}; + + +static void calcsize(union node *); +static void sizenodelist(struct nodelist *); +static union node *copynode(union node *); +static struct nodelist *copynodelist(struct nodelist *); +static char *nodesavestr(char *); + + + +static void evalstring(char *); +union node; /* BLETCH for ansi C */ +static void evaltree(union node *, int); +static void evalbackcmd(union node *, struct backcmd *); + +/* in_function returns nonzero if we are currently evaluating a function */ +#define in_function() funcnest +static int evalskip; /* set if we are skipping commands */ +static int skipcount; /* number of levels to skip */ +static int funcnest; /* depth of function calls */ + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 +#define SKIPFILE 4 + +/* + * This file was generated by the mkbuiltins program. + */ + +#ifdef JOBS +static int bgcmd(int, char **); +#endif +static int breakcmd(int, char **); +static int cdcmd(int, char **); +#ifdef CONFIG_ASH_CMDCMD +static int commandcmd(int, char **); +#endif +static int dotcmd(int, char **); +static int evalcmd(int, char **); +static int execcmd(int, char **); +static int exitcmd(int, char **); +static int exportcmd(int, char **); +static int falsecmd(int, char **); +#ifdef JOBS +static int fgcmd(int, char **); +#endif +#ifdef CONFIG_ASH_GETOPTS +static int getoptscmd(int, char **); +#endif +static int hashcmd(int, char **); +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET +static int helpcmd(int argc, char **argv); +#endif +#ifdef JOBS +static int jobscmd(int, char **); +#endif +#ifdef CONFIG_ASH_MATH_SUPPORT +static int letcmd(int, char **); +#endif +static int localcmd(int, char **); +static int pwdcmd(int, char **); +static int readcmd(int, char **); +static int returncmd(int, char **); +static int setcmd(int, char **); +static int shiftcmd(int, char **); +static int timescmd(int, char **); +static int trapcmd(int, char **); +static int truecmd(int, char **); +static int typecmd(int, char **); +static int umaskcmd(int, char **); +static int unsetcmd(int, char **); +static int waitcmd(int, char **); +static int ulimitcmd(int, char **); +#ifdef JOBS +static int killcmd(int, char **); +#endif + +/* $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */ + +#ifdef CONFIG_ASH_MAIL +static void chkmail(void); +static void changemail(const char *); +#endif + +/* $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $ */ + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDFUNCTION 1 /* command is a shell function */ +#define CMDBUILTIN 2 /* command is a shell builtin */ + +struct builtincmd { + const char *name; + int (*builtin)(int, char **); + /* unsigned flags; */ +}; + +#ifdef CONFIG_ASH_CMDCMD +# ifdef JOBS +# ifdef CONFIG_ASH_ALIAS +# define COMMANDCMD (builtincmd + 7) +# define EXECCMD (builtincmd + 10) +# else +# define COMMANDCMD (builtincmd + 6) +# define EXECCMD (builtincmd + 9) +# endif +# else /* ! JOBS */ +# ifdef CONFIG_ASH_ALIAS +# define COMMANDCMD (builtincmd + 6) +# define EXECCMD (builtincmd + 9) +# else +# define COMMANDCMD (builtincmd + 5) +# define EXECCMD (builtincmd + 8) +# endif +# endif /* JOBS */ +#else /* ! CONFIG_ASH_CMDCMD */ +# ifdef JOBS +# ifdef CONFIG_ASH_ALIAS +# define EXECCMD (builtincmd + 9) +# else +# define EXECCMD (builtincmd + 8) +# endif +# else /* ! JOBS */ +# ifdef CONFIG_ASH_ALIAS +# define EXECCMD (builtincmd + 8) +# else +# define EXECCMD (builtincmd + 7) +# endif +# endif /* JOBS */ +#endif /* CONFIG_ASH_CMDCMD */ + +#define BUILTIN_NOSPEC "0" +#define BUILTIN_SPECIAL "1" +#define BUILTIN_REGULAR "2" +#define BUILTIN_SPEC_REG "3" +#define BUILTIN_ASSIGN "4" +#define BUILTIN_SPEC_ASSG "5" +#define BUILTIN_REG_ASSG "6" +#define BUILTIN_SPEC_REG_ASSG "7" + +#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1) +#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2) + +static const struct builtincmd builtincmd[] = { + { BUILTIN_SPEC_REG ".", dotcmd }, + { BUILTIN_SPEC_REG ":", truecmd }, +#ifdef CONFIG_ASH_ALIAS + { BUILTIN_REG_ASSG "alias", aliascmd }, +#endif +#ifdef JOBS + { BUILTIN_REGULAR "bg", bgcmd }, +#endif + { BUILTIN_SPEC_REG "break", breakcmd }, + { BUILTIN_REGULAR "cd", cdcmd }, + { BUILTIN_NOSPEC "chdir", cdcmd }, +#ifdef CONFIG_ASH_CMDCMD + { BUILTIN_REGULAR "command", commandcmd }, +#endif + { BUILTIN_SPEC_REG "continue", breakcmd }, + { BUILTIN_SPEC_REG "eval", evalcmd }, + { BUILTIN_SPEC_REG "exec", execcmd }, + { BUILTIN_SPEC_REG "exit", exitcmd }, + { BUILTIN_SPEC_REG_ASSG "export", exportcmd }, + { BUILTIN_REGULAR "false", falsecmd }, +#ifdef JOBS + { BUILTIN_REGULAR "fg", fgcmd }, +#endif +#ifdef CONFIG_ASH_GETOPTS + { BUILTIN_REGULAR "getopts", getoptscmd }, +#endif + { BUILTIN_NOSPEC "hash", hashcmd }, +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET + { BUILTIN_NOSPEC "help", helpcmd }, +#endif +#ifdef JOBS + { BUILTIN_REGULAR "jobs", jobscmd }, + { BUILTIN_REGULAR "kill", killcmd }, +#endif +#ifdef CONFIG_ASH_MATH_SUPPORT + { BUILTIN_NOSPEC "let", letcmd }, +#endif + { BUILTIN_ASSIGN "local", localcmd }, + { BUILTIN_NOSPEC "pwd", pwdcmd }, + { BUILTIN_REGULAR "read", readcmd }, + { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd }, + { BUILTIN_SPEC_REG "return", returncmd }, + { BUILTIN_SPEC_REG "set", setcmd }, + { BUILTIN_SPEC_REG "shift", shiftcmd }, + { BUILTIN_SPEC_REG "times", timescmd }, + { BUILTIN_SPEC_REG "trap", trapcmd }, + { BUILTIN_REGULAR "true", truecmd }, + { BUILTIN_NOSPEC "type", typecmd }, + { BUILTIN_NOSPEC "ulimit", ulimitcmd }, + { BUILTIN_REGULAR "umask", umaskcmd }, +#ifdef CONFIG_ASH_ALIAS + { BUILTIN_REGULAR "unalias", unaliascmd }, +#endif + { BUILTIN_SPEC_REG "unset", unsetcmd }, + { BUILTIN_REGULAR "wait", waitcmd }, +}; + +#define NUMBUILTINS (sizeof (builtincmd) / sizeof (struct builtincmd) ) + + + +struct cmdentry { + int cmdtype; + union param { + int index; + const struct builtincmd *cmd; + struct funcnode *func; + } u; +}; + + +/* action to find_command() */ +#define DO_ERR 0x01 /* prints errors */ +#define DO_ABS 0x02 /* checks absolute paths */ +#define DO_NOFUNC 0x04 /* don't return shell functions, for command */ +#define DO_ALTPATH 0x08 /* using alternate path */ +#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ + +static const char *pathopt; /* set by padvance */ + +static void shellexec(char **, const char *, int) + __attribute__((__noreturn__)); +static char *padvance(const char **, const char *); +static void find_command(char *, struct cmdentry *, int, const char *); +static struct builtincmd *find_builtin(const char *); +static void hashcd(void); +static void changepath(const char *); +static void defun(char *, union node *); +static void unsetfunc(const char *); + +#ifdef CONFIG_ASH_MATH_SUPPORT_64 +typedef int64_t arith_t; +#else +typedef long arith_t; +#endif + +#ifdef CONFIG_ASH_MATH_SUPPORT +static arith_t dash_arith(const char *); +static arith_t arith(const char *expr, int *perrcode); +#endif + +#ifdef CONFIG_ASH_RANDOM_SUPPORT +static unsigned long rseed; +static void change_random(const char *); +# ifndef DYNAMIC_VAR +# define DYNAMIC_VAR +# endif +#endif + +/* $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */ + +static void reset(void); + +/* $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $ */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is statically allocated */ +#define VTEXTFIXED 0x08 /* text is statically allocated */ +#define VSTACK 0x10 /* text is allocated on the stack */ +#define VUNSET 0x20 /* the variable is not set */ +#define VNOFUNC 0x40 /* don't call the callback function */ +#define VNOSET 0x80 /* do not set variable - just readonly test */ +#define VNOSAVE 0x100 /* when text is on the heap before setvareq */ +#ifdef DYNAMIC_VAR +# define VDYNAMIC 0x200 /* dynamic variable */ +# else +# define VDYNAMIC 0 +#endif + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + const char *text; /* name=value */ + void (*func)(const char *); /* function to be called when */ + /* the variable gets set/unset */ +}; + +struct localvar { + struct localvar *next; /* next local variable in list */ + struct var *vp; /* the variable that was made local */ + int flags; /* saved flags */ + const char *text; /* saved text */ +}; + + +static struct localvar *localvars; + +/* + * Shell variables. + */ + +#ifdef CONFIG_ASH_GETOPTS +static void getoptsreset(const char *); +#endif + +#ifdef CONFIG_LOCALE_SUPPORT +#include +static void change_lc_all(const char *value); +static void change_lc_ctype(const char *value); +#endif + + +#define VTABSIZE 39 + +static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin"; +#ifdef IFS_BROKEN +static const char defifsvar[] = "IFS= \t\n"; +#define defifs (defifsvar + 4) +#else +static const char defifs[] = " \t\n"; +#endif + + +static struct var varinit[] = { +#ifdef IFS_BROKEN + { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 }, +#else + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 }, +#endif + +#ifdef CONFIG_ASH_MAIL + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail }, + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, +#endif + + { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath }, + { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 }, + { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, + { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, +#ifdef CONFIG_ASH_GETOPTS + { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, +#endif +#ifdef CONFIG_ASH_RANDOM_SUPPORT + {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random }, +#endif +#ifdef CONFIG_LOCALE_SUPPORT + {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all }, + {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype }, +#endif +#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY + {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL }, +#endif +}; + +#define vifs varinit[0] +#ifdef CONFIG_ASH_MAIL +#define vmail (&vifs)[1] +#define vmpath (&vmail)[1] +#else +#define vmpath vifs +#endif +#define vpath (&vmpath)[1] +#define vps1 (&vpath)[1] +#define vps2 (&vps1)[1] +#define vps4 (&vps2)[1] +#define voptind (&vps4)[1] +#ifdef CONFIG_ASH_GETOPTS +#define vrandom (&voptind)[1] +#else +#define vrandom (&vps4)[1] +#endif +#define defpath (defpathvar + 5) + +/* + * The following macros access the values of the above variables. + * They have to skip over the name. They return the null string + * for unset variables. + */ + +#define ifsval() (vifs.text + 4) +#define ifsset() ((vifs.flags & VUNSET) == 0) +#define mailval() (vmail.text + 5) +#define mpathval() (vmpath.text + 9) +#define pathval() (vpath.text + 5) +#define ps1val() (vps1.text + 4) +#define ps2val() (vps2.text + 4) +#define ps4val() (vps4.text + 4) +#define optindval() (voptind.text + 7) + +#define mpathset() ((vmpath.flags & VUNSET) == 0) + +static void setvar(const char *, const char *, int); +static void setvareq(char *, int); +static void listsetvar(struct strlist *, int); +static char *lookupvar(const char *); +static char *bltinlookup(const char *); +static char **listvars(int, int, char ***); +#define environment() listvars(VEXPORT, VUNSET, 0) +static int showvars(const char *, int, int); +static void poplocalvars(void); +static int unsetvar(const char *); +#ifdef CONFIG_ASH_GETOPTS +static int setvarsafe(const char *, const char *, int); +#endif +static int varcmp(const char *, const char *); +static struct var **hashvar(const char *); + + +static inline int varequal(const char *a, const char *b) { + return !varcmp(a, b); +} + + +static int loopnest; /* current loop nesting level */ + +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ + + +struct redirtab { + struct redirtab *next; + int renamed[10]; + int nullredirs; +}; + +static struct redirtab *redirlist; +static int nullredirs; + +extern char **environ; + +/* $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $ */ + + +static void outstr(const char *, FILE *); +static void outcslow(int, FILE *); +static void flushall(void); +static void flusherr(void); +static int out1fmt(const char *, ...) + __attribute__((__format__(__printf__,1,2))); +static int fmtstr(char *, size_t, const char *, ...) + __attribute__((__format__(__printf__,3,4))); + +static int preverrout_fd; /* save fd2 before print debug if xflag is set. */ + + +static void out1str(const char *p) +{ + outstr(p, stdout); +} + +static void out2str(const char *p) +{ + outstr(p, stderr); + flusherr(); +} + +/* + * Initialization code. + */ + +/* + * This routine initializes the builtin variables. + */ + +static inline void +initvar(void) +{ + struct var *vp; + struct var *end; + struct var **vpp; + + /* + * PS1 depends on uid + */ +#if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT) + vps1.text = "PS1=\\w \\$ "; +#else + if (!geteuid()) + vps1.text = "PS1=# "; +#endif + vp = varinit; + end = vp + sizeof(varinit) / sizeof(varinit[0]); + do { + vpp = hashvar(vp->text); + vp->next = *vpp; + *vpp = vp; + } while (++vp < end); +} + +static inline void +init(void) +{ + + /* from input.c: */ + { + basepf.nextc = basepf.buf = basebuf; + } + + /* from trap.c: */ + { + signal(SIGCHLD, SIG_DFL); + } + + /* from var.c: */ + { + char **envp; + char ppid[32]; + + initvar(); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } + + snprintf(ppid, sizeof(ppid), "%d", (int) getppid()); + setvar("PPID", ppid, 0); + setpwd(0, 0); + } +} + +/* PEOF (the end of file marker) */ + +/* + * The input line number. Input.c just defines this variable, and saves + * and restores it when files are pushed and popped. The user of this + * package must set its value. + */ + +static int pgetc(void); +static int pgetc2(void); +static int preadbuffer(void); +static void pungetc(void); +static void pushstring(char *, void *); +static void popstring(void); +static void setinputfile(const char *, int); +static void setinputfd(int, int); +static void setinputstring(char *); +static void popfile(void); +static void popallfiles(void); +static void closescript(void); + + +/* $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $ */ + + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + +/* mode flags for showjob(s) */ +#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ +#define SHOW_PID 0x04 /* include process pid */ +#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ + + +/* + * A job structure contains information about a job. A job is either a + * single process or a set of processes contained in a pipeline. In the + * latter case, pidlist will be non-NULL, and will point to a -1 terminated + * array of pids. + */ + +struct procstat { + pid_t pid; /* process id */ + int status; /* last process status from wait() */ + char *cmd; /* text of command being run */ +}; + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ +#if JOBS + int stopstatus; /* status of a stopped job */ +#endif + uint32_t + nprocs: 16, /* number of processes */ + state: 8, +#define JOBRUNNING 0 /* at least one proc running */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ +#if JOBS + sigint: 1, /* job was killed by SIGINT */ + jobctl: 1, /* job running under job control */ +#endif + waited: 1, /* true if this entry has been waited for */ + used: 1, /* true if this entry is in used */ + changed: 1; /* true if status has changed */ + struct job *prev_job; /* previous job */ +}; + +static pid_t backgndpid; /* pid of last background process */ +static int job_warning; /* user was warned about stopped jobs */ +#if JOBS +static int jobctl; /* true if doing job control */ +#endif + +static struct job *makejob(union node *, int); +static int forkshell(struct job *, union node *, int); +static int waitforjob(struct job *); +static int stoppedjobs(void); + +#if ! JOBS +#define setjobctl(on) /* do nothing */ +#else +static void setjobctl(int); +static void showjobs(FILE *, int); +#endif + +/* $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $ */ + + +/* pid of main shell */ +static int rootpid; +/* true if we aren't a child of the main shell */ +static int rootshell; + +static void readcmdfile(char *); +static void cmdloop(int); + +/* $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $ */ + + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + size_t stacknleft; + struct stackmark *marknext; +}; + +/* minimum size of a block */ +#define MINSIZE SHELL_ALIGN(504) + +struct stack_block { + struct stack_block *prev; + char space[MINSIZE]; +}; + +static struct stack_block stackbase; +static struct stack_block *stackp = &stackbase; +static struct stackmark *markp; +static char *stacknxt = stackbase.space; +static size_t stacknleft = MINSIZE; +static char *sstrend = stackbase.space + MINSIZE; +static int herefd = -1; + + +static pointer ckmalloc(size_t); +static pointer ckrealloc(pointer, size_t); +static char *savestr(const char *); +static pointer stalloc(size_t); +static void stunalloc(pointer); +static void setstackmark(struct stackmark *); +static void popstackmark(struct stackmark *); +static void growstackblock(void); +static void *growstackstr(void); +static char *makestrspace(size_t, char *); +static char *stnputs(const char *, size_t, char *); +static char *stputs(const char *, char *); + + +static inline char *_STPUTC(char c, char *p) { + if (p == sstrend) + p = growstackstr(); + *p++ = c; + return p; +} + +#define stackblock() ((void *)stacknxt) +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) ((p) = stackblock()) +#define STPUTC(c, p) ((p) = _STPUTC((c), (p))) +#define CHECKSTRSPACE(n, p) \ + ({ \ + char *q = (p); \ + size_t l = (n); \ + size_t m = sstrend - q; \ + if (l > m) \ + (p) = makestrspace(l, q); \ + 0; \ + }) +#define USTPUTC(c, p) (*p++ = (c)) +#define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0')) +#define STUNPUTC(p) (--p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount)) + +#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock()) +#define ungrabstackstr(s, p) stunalloc((s)) +#define stackstrend() ((void *)sstrend) + +#define ckfree(p) free((pointer)(p)) + +/* $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $ */ + + +#define DOLATSTRLEN 4 + +static char *prefix(const char *, const char *); +static int number(const char *); +static int is_number(const char *); +static char *single_quote(const char *); +static char *sstrdup(const char *); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) +#define scopy(s1, s2) ((void)strcpy(s2, s1)) + +/* $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */ + +struct shparam { + int nparam; /* # of positional parameters (without $0) */ + unsigned char malloc; /* if parameter list dynamically allocated */ + char **p; /* parameter list */ +#ifdef CONFIG_ASH_GETOPTS + int optind; /* next parameter to be processed by getopts */ + int optoff; /* used by getopts */ +#endif +}; + + +#define eflag optlist[0] +#define fflag optlist[1] +#define Iflag optlist[2] +#define iflag optlist[3] +#define mflag optlist[4] +#define nflag optlist[5] +#define sflag optlist[6] +#define xflag optlist[7] +#define vflag optlist[8] +#define Cflag optlist[9] +#define aflag optlist[10] +#define bflag optlist[11] +#define uflag optlist[12] +#define qflag optlist[13] + +#ifdef DEBUG +#define nolog optlist[14] +#define debug optlist[15] +#define NOPTS 16 +#else +#define NOPTS 14 +#endif + +/* $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */ + + +static const char *const optletters_optnames[NOPTS] = { + "e" "errexit", + "f" "noglob", + "I" "ignoreeof", + "i" "interactive", + "m" "monitor", + "n" "noexec", + "s" "stdin", + "x" "xtrace", + "v" "verbose", + "C" "noclobber", + "a" "allexport", + "b" "notify", + "u" "nounset", + "q" "quietprofile", +#ifdef DEBUG + "\0" "nolog", + "\0" "debug", +#endif +}; + +#define optletters(n) optletters_optnames[(n)][0] +#define optnames(n) (&optletters_optnames[(n)][1]) + + +static char optlist[NOPTS]; + + +static char *arg0; /* value of $0 */ +static struct shparam shellparam; /* $@ current positional parameters */ +static char **argptr; /* argument list for builtin commands */ +static char *optionarg; /* set by nextopt (like getopt) */ +static char *optptr; /* used by nextopt */ + +static char *minusc; /* argument to -c option */ + + +static void procargs(int, char **); +static void optschanged(void); +static void setparam(char **); +static void freeparam(volatile struct shparam *); +static int shiftcmd(int, char **); +static int setcmd(int, char **); +static int nextopt(const char *); + +/* $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $ */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_SAVEFD2 03 /* set preverrout */ + +union node; +static void redirect(union node *, int); +static void popredir(int); +static void clearredir(int); +static int copyfd(int, int); +static int redirectsafe(union node *, int); + +/* $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $ */ + + +#ifdef DEBUG +static void showtree(union node *); +static void trace(const char *, ...); +static void tracev(const char *, va_list); +static void trargs(char **); +static void trputc(int); +static void trputs(const char *); +static void opentrace(void); +#endif + +/* $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $ */ + + +/* trap handler commands */ +static char *trap[NSIG]; +/* current value of signal */ +static char sigmode[NSIG - 1]; +/* indicates specified signal received */ +static char gotsig[NSIG - 1]; + +static void clear_traps(void); +static void setsignal(int); +static void ignoresig(int); +static void onsig(int); +static void dotrap(void); +static void setinteractive(int); +static void exitshell(void) __attribute__((__noreturn__)); +static int decode_signal(const char *, int); + +/* + * This routine is called when an error or an interrupt occurs in an + * interactive shell and control is returned to the main command loop. + */ + +static void +reset(void) +{ + /* from eval.c: */ + { + evalskip = 0; + loopnest = 0; + funcnest = 0; + } + + /* from input.c: */ + { + parselleft = parsenleft = 0; /* clear input buffer */ + popallfiles(); + } + + /* from parser.c: */ + { + tokpushback = 0; + checkkwd = 0; + } + + /* from redir.c: */ + { + clearredir(0); + } + +} + +#ifdef CONFIG_ASH_ALIAS +static struct alias *atab[ATABSIZE]; + +static void setalias(const char *, const char *); +static struct alias *freealias(struct alias *); +static struct alias **__lookupalias(const char *); + +static void +setalias(const char *name, const char *val) +{ + struct alias *ap, **app; + + app = __lookupalias(name); + ap = *app; + INTOFF; + if (ap) { + if (!(ap->flag & ALIASINUSE)) { + ckfree(ap->val); + } + ap->val = savestr(val); + ap->flag &= ~ALIASDEAD; + } else { + /* not found */ + ap = ckmalloc(sizeof (struct alias)); + ap->name = savestr(name); + ap->val = savestr(val); + ap->flag = 0; + ap->next = 0; + *app = ap; + } + INTON; +} + +static int +unalias(const char *name) +{ + struct alias **app; + + app = __lookupalias(name); + + if (*app) { + INTOFF; + *app = freealias(*app); + INTON; + return (0); + } + + return (1); +} + +static void +rmaliases(void) +{ + struct alias *ap, **app; + int i; + + INTOFF; + for (i = 0; i < ATABSIZE; i++) { + app = &atab[i]; + for (ap = *app; ap; ap = *app) { + *app = freealias(*app); + if (ap == *app) { + app = &ap->next; + } + } + } + INTON; +} + +static struct alias * +lookupalias(const char *name, int check) +{ + struct alias *ap = *__lookupalias(name); + + if (check && ap && (ap->flag & ALIASINUSE)) + return (NULL); + return (ap); +} + +/* + * TODO - sort output + */ +static int +aliascmd(int argc, char **argv) +{ + char *n, *v; + int ret = 0; + struct alias *ap; + + if (argc == 1) { + int i; + + for (i = 0; i < ATABSIZE; i++) + for (ap = atab[i]; ap; ap = ap->next) { + printalias(ap); + } + return (0); + } + while ((n = *++argv) != NULL) { + if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ + if ((ap = *__lookupalias(n)) == NULL) { + fprintf(stderr, "%s: %s not found\n", "alias", n); + ret = 1; + } else + printalias(ap); + } else { + *v++ = '\0'; + setalias(n, v); + } + } + + return (ret); +} + +static int +unaliascmd(int argc, char **argv) +{ + int i; + + while ((i = nextopt("a")) != '\0') { + if (i == 'a') { + rmaliases(); + return (0); + } + } + for (i = 0; *argptr; argptr++) { + if (unalias(*argptr)) { + fprintf(stderr, "%s: %s not found\n", "unalias", *argptr); + i = 1; + } + } + + return (i); +} + +static struct alias * +freealias(struct alias *ap) { + struct alias *next; + + if (ap->flag & ALIASINUSE) { + ap->flag |= ALIASDEAD; + return ap; + } + + next = ap->next; + ckfree(ap->name); + ckfree(ap->val); + ckfree(ap); + return next; +} + +static void +printalias(const struct alias *ap) { + out1fmt("%s=%s\n", ap->name, single_quote(ap->val)); +} + +static struct alias ** +__lookupalias(const char *name) { + unsigned int hashval; + struct alias **app; + const char *p; + unsigned int ch; + + p = name; + + ch = (unsigned char)*p; + hashval = ch << 4; + while (ch) { + hashval += ch; + ch = (unsigned char)*++p; + } + app = &atab[hashval % ATABSIZE]; + + for (; *app; app = &(*app)->next) { + if (equal(name, (*app)->name)) { + break; + } + } + + return app; +} +#endif /* CONFIG_ASH_ALIAS */ + + +/* $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */ + +/* + * The cd and pwd commands. + */ + +#define CD_PHYSICAL 1 +#define CD_PRINT 2 + +static int docd(const char *, int); +static int cdopt(void); + +static char *curdir = nullstr; /* current working directory */ +static char *physdir = nullstr; /* physical working directory */ + +static int +cdopt(void) +{ + int flags = 0; + int i, j; + + j = 'L'; + while ((i = nextopt("LP"))) { + if (i != j) { + flags ^= CD_PHYSICAL; + j = i; + } + } + + return flags; +} + +static int +cdcmd(int argc, char **argv) +{ + const char *dest; + const char *path; + const char *p; + char c; + struct stat statb; + int flags; + + flags = cdopt(); + dest = *argptr; + if (!dest) + dest = bltinlookup(homestr); + else if (dest[0] == '-' && dest[1] == '\0') { + dest = bltinlookup("OLDPWD"); + if ( !dest ) goto out; + flags |= CD_PRINT; + goto step7; + } + if (!dest) + dest = nullstr; + if (*dest == '/') + goto step7; + if (*dest == '.') { + c = dest[1]; +dotdot: + switch (c) { + case '\0': + case '/': + goto step6; + case '.': + c = dest[2]; + if (c != '.') + goto dotdot; + } + } + if (!*dest) + dest = "."; + if (!(path = bltinlookup("CDPATH"))) { +step6: +step7: + p = dest; + goto docd; + } + do { + c = *path; + p = padvance(&path, dest); + if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { + if (c && c != ':') + flags |= CD_PRINT; +docd: + if (!docd(p, flags)) + goto out; + break; + } + } while (path); + error("can't cd to %s", dest); + /* NOTREACHED */ +out: + if (flags & CD_PRINT) + out1fmt(snlfmt, curdir); + return 0; +} + + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. + */ + +static inline const char * +updatepwd(const char *dir) +{ + char *new; + char *p; + char *cdcomppath; + const char *lim; + + cdcomppath = sstrdup(dir); + STARTSTACKSTR(new); + if (*dir != '/') { + if (curdir == nullstr) + return 0; + new = stputs(curdir, new); + } + new = makestrspace(strlen(dir) + 2, new); + lim = stackblock() + 1; + if (*dir != '/') { + if (new[-1] != '/') + USTPUTC('/', new); + if (new > lim && *lim == '/') + lim++; + } else { + USTPUTC('/', new); + cdcomppath++; + if (dir[1] == '/' && dir[2] != '/') { + USTPUTC('/', new); + cdcomppath++; + lim++; + } + } + p = strtok(cdcomppath, "/"); + while (p) { + switch(*p) { + case '.': + if (p[1] == '.' && p[2] == '\0') { + while (new > lim) { + STUNPUTC(new); + if (new[-1] == '/') + break; + } + break; + } else if (p[1] == '\0') + break; + /* fall through */ + default: + new = stputs(p, new); + USTPUTC('/', new); + } + p = strtok(0, "/"); + } + if (new > lim) + STUNPUTC(new); + *new = 0; + return stackblock(); +} + +/* + * Actually do the chdir. We also call hashcd to let the routines in exec.c + * know that the current directory has changed. + */ + +static int +docd(const char *dest, int flags) +{ + const char *dir = 0; + int err; + + TRACE(("docd(\"%s\", %d) called\n", dest, flags)); + + INTOFF; + if (!(flags & CD_PHYSICAL)) { + dir = updatepwd(dest); + if (dir) + dest = dir; + } + err = chdir(dest); + if (err) + goto out; + setpwd(dir, 1); + hashcd(); +out: + INTON; + return err; +} + +/* + * Find out what the current directory is. If we already know the current + * directory, this routine returns immediately. + */ +static inline char * +getpwd(void) +{ + char *dir = getcwd(0, 0); + return dir ? dir : nullstr; +} + +static int +pwdcmd(int argc, char **argv) +{ + int flags; + const char *dir = curdir; + + flags = cdopt(); + if (flags) { + if (physdir == nullstr) + setpwd(dir, 0); + dir = physdir; + } + out1fmt(snlfmt, dir); + return 0; +} + +static void +setpwd(const char *val, int setold) +{ + char *oldcur, *dir; + + oldcur = dir = curdir; + + if (setold) { + setvar("OLDPWD", oldcur, VEXPORT); + } + INTOFF; + if (physdir != nullstr) { + if (physdir != oldcur) + free(physdir); + physdir = nullstr; + } + if (oldcur == val || !val) { + char *s = getpwd(); + physdir = s; + if (!val) + dir = s; + } else + dir = savestr(val); + if (oldcur != dir && oldcur != nullstr) { + free(oldcur); + } + curdir = dir; + INTON; + setvar("PWD", dir, VEXPORT); +} + +/* $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */ + +/* + * Errors and exceptions. + */ + +/* + * Code to handle exceptions in C. + */ + + + +static void exverror(int, const char *, va_list) + __attribute__((__noreturn__)); + +/* + * Called to raise an exception. Since C doesn't include exceptions, we + * just do a longjmp to the exception handler. The type of exception is + * stored in the global variable "exception". + */ + +static void +exraise(int e) +{ +#ifdef DEBUG + if (handler == NULL) + abort(); +#endif + INTOFF; + + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. (The test for iflag is just + * defensive programming.) + */ + +static void +onint(void) { + int i; + + intpending = 0; + sigsetmask(0); + i = EXSIG; + if (gotsig[SIGINT - 1] && !trap[SIGINT]) { + if (!(rootshell && iflag)) { + signal(SIGINT, SIG_DFL); + raise(SIGINT); + } + i = EXINT; + } + exraise(i); + /* NOTREACHED */ +} + +static void +exvwarning(const char *msg, va_list ap) +{ + FILE *errs; + const char *name; + const char *fmt; + + errs = stderr; + name = arg0; + fmt = "%s: "; + if (commandname) { + name = commandname; + fmt = "%s: %d: "; + } + fprintf(errs, fmt, name, startlinno); + vfprintf(errs, msg, ap); + outcslow('\n', errs); +} + +/* + * Exverror is called to raise the error exception. If the second argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ +static void +exverror(int cond, const char *msg, va_list ap) +{ +#ifdef DEBUG + if (msg) { + TRACE(("exverror(%d, \"", cond)); + TRACEV((msg, ap)); + TRACE(("\") pid=%d\n", getpid())); + } else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); + if (msg) +#endif + exvwarning(msg, ap); + + flushall(); + exraise(cond); + /* NOTREACHED */ +} + + +static void +error(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + exverror(EXERROR, msg, ap); + /* NOTREACHED */ + va_end(ap); +} + + +static void +exerror(int cond, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + exverror(cond, msg, ap); + /* NOTREACHED */ + va_end(ap); +} + +/* + * error/warning routines for external builtins + */ + +static void +sh_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(fmt, ap); + va_end(ap); +} + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +static const char * +errmsg(int e, const char *em) +{ + if(e == ENOENT || e == ENOTDIR) { + + return em; + } + return strerror(e); +} + + +/* $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $ */ + +/* + * Evaluate a command. + */ + +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_BACKCMD 04 /* command executing within back quotes */ + + +static void evalloop(union node *, int); +static void evalfor(union node *, int); +static void evalcase(union node *, int); +static void evalsubshell(union node *, int); +static void expredir(union node *); +static void evalpipe(union node *, int); +static void evalcommand(union node *, int); +static int evalbltin(const struct builtincmd *, int, char **); +static int evalfun(struct funcnode *, int, char **, int); +static void prehash(union node *); +static int bltincmd(int, char **); + + +static const struct builtincmd bltin = { + "\0\0", bltincmd +}; + + +/* + * Called to reset things after an exception. + */ + +/* + * The eval command. + */ + +static int +evalcmd(int argc, char **argv) +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + concat = stputs(p, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + evalstring(p); + } + return exitstatus; +} + + +/* + * Execute a command or commands contained in a string. + */ + +static void +evalstring(char *s) +{ + union node *n; + struct stackmark smark; + + setstackmark(&smark); + setinputstring(s); + + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, 0); + popstackmark(&smark); + if (evalskip) + break; + } + popfile(); + popstackmark(&smark); +} + + + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ + +static void +evaltree(union node *n, int flags) +{ + int checkexit = 0; + void (*evalfn)(union node *, int); + unsigned isor; + int status; + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + goto out; + } + TRACE(("pid %d, evaltree(%p: %d, %d) called\n", + getpid(), n, n->type, flags)); + switch (n->type) { + default: +#ifdef DEBUG + out1fmt("Node type = %d\n", n->type); + fflush(stdout); + break; +#endif + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + status = !exitstatus; + goto setstatus; + case NREDIR: + expredir(n->nredir.redirect); + status = redirectsafe(n->nredir.redirect, REDIR_PUSH); + if (!status) { + evaltree(n->nredir.n, flags & EV_TESTED); + status = exitstatus; + } + popredir(0); + goto setstatus; + case NCMD: + evalfn = evalcommand; +checkexit: + if (eflag && !(flags & EV_TESTED)) + checkexit = ~0; + goto calleval; + case NFOR: + evalfn = evalfor; + goto calleval; + case NWHILE: + case NUNTIL: + evalfn = evalloop; + goto calleval; + case NSUBSHELL: + case NBACKGND: + evalfn = evalsubshell; + goto calleval; + case NPIPE: + evalfn = evalpipe; + goto checkexit; + case NCASE: + evalfn = evalcase; + goto calleval; + case NAND: + case NOR: + case NSEMI: +#if NAND + 1 != NOR +#error NAND + 1 != NOR +#endif +#if NOR + 1 != NSEMI +#error NOR + 1 != NSEMI +#endif + isor = n->type - NAND; + evaltree( + n->nbinary.ch1, + (flags | ((isor >> 1) - 1)) & EV_TESTED + ); + if (!exitstatus == isor) + break; + if (!evalskip) { + n = n->nbinary.ch2; +evaln: + evalfn = evaltree; +calleval: + evalfn(n, flags); + break; + } + break; + case NIF: + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + break; + if (exitstatus == 0) { + n = n->nif.ifpart; + goto evaln; + } else if (n->nif.elsepart) { + n = n->nif.elsepart; + goto evaln; + } + goto success; + case NDEFUN: + defun(n->narg.text, n->narg.next); +success: + status = 0; +setstatus: + exitstatus = status; + break; + } +out: + if (pendingsigs) + dotrap(); + if (flags & EV_EXIT || checkexit & exitstatus) + exraise(EXEXIT); +} + + +#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) +static +#endif +void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); + + +static void +evalloop(union node *n, int flags) +{ + int status; + + loopnest++; + status = 0; + flags &= EV_TESTED; + for (;;) { + int i; + + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { +skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + i = exitstatus; + if (n->type != NWHILE) + i = !i; + if (i != 0) + break; + evaltree(n->nbinary.ch2, flags); + status = exitstatus; + if (evalskip) + goto skipping; + } + loopnest--; + exitstatus = status; +} + + + +static void +evalfor(union node *n, int flags) +{ + struct arglist arglist; + union node *argp; + struct strlist *sp; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); + /* XXX */ + if (evalskip) + goto out; + } + *arglist.lastp = NULL; + + exitstatus = 0; + loopnest++; + flags &= EV_TESTED; + for (sp = arglist.list ; sp ; sp = sp->next) { + setvar(n->nfor.var, sp->text, 0); + evaltree(n->nfor.body, flags); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; +out: + popstackmark(&smark); +} + + + +static void +evalcase(union node *n, int flags) +{ + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + expandarg(n->ncase.expr, &arglist, EXP_TILDE); + exitstatus = 0; + for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.list->text)) { + if (evalskip == 0) { + evaltree(cp->nclist.body, flags); + } + goto out; + } + } + } +out: + popstackmark(&smark); +} + + + +/* + * Kick off a subshell to evaluate a tree. + */ + +static void +evalsubshell(union node *n, int flags) +{ + struct job *jp; + int backgnd = (n->type == NBACKGND); + int status; + + expredir(n->nredir.redirect); + if (!backgnd && flags & EV_EXIT && !trap[0]) + goto nofork; + INTOFF; + jp = makejob(n, 1); + if (forkshell(jp, n, backgnd) == 0) { + INTON; + flags |= EV_EXIT; + if (backgnd) + flags &=~ EV_TESTED; +nofork: + redirect(n->nredir.redirect, 0); + evaltreenr(n->nredir.n, flags); + /* never returns */ + } + status = 0; + if (! backgnd) + status = waitforjob(jp); + exitstatus = status; + INTON; +} + + + +/* + * Compute the names of the files in a redirection list. + */ + +static void +expredir(union node *n) +{ + union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + struct arglist fn; + fn.lastp = &fn.list; + switch (redir->type) { + case NFROMTO: + case NFROM: + case NTO: + case NCLOBBER: + case NAPPEND: + expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); + redir->nfile.expfname = fn.list->text; + break; + case NFROMFD: + case NTOFD: + if (redir->ndup.vname) { + expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); + fixredir(redir, fn.list->text, 1); + } + break; + } + } +} + + + +/* + * Evaluate a pipeline. All the processes in the pipeline are children + * of the process creating the pipeline. (This differs from some versions + * of the shell, which make the last process in a pipeline the parent + * of all the rest.) + */ + +static void +evalpipe(union node *n, int flags) +{ + struct job *jp; + struct nodelist *lp; + int pipelen; + int prevfd; + int pip[2]; + + TRACE(("evalpipe(0x%lx) called\n", (long)n)); + pipelen = 0; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) + pipelen++; + flags |= EV_EXIT; + INTOFF; + jp = makejob(n, pipelen); + prevfd = -1; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + prehash(lp->n); + pip[1] = -1; + if (lp->next) { + if (pipe(pip) < 0) { + close(prevfd); + error("Pipe call failed"); + } + } + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (pip[1] >= 0) { + close(pip[0]); + } + if (prevfd > 0) { + dup2(prevfd, 0); + close(prevfd); + } + if (pip[1] > 1) { + dup2(pip[1], 1); + close(pip[1]); + } + evaltreenr(lp->n, flags); + /* never returns */ + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + if (n->npipe.backgnd == 0) { + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + } + INTON; +} + + + +/* + * Execute a command inside back quotes. If it's a builtin command, we + * want to save its output in a block obtained from malloc. Otherwise + * we fork off a subprocess and get the output of the command via a pipe. + * Should be called with interrupts off. + */ + +static void +evalbackcmd(union node *n, struct backcmd *result) +{ + int saveherefd; + + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + goto out; + } + + saveherefd = herefd; + herefd = -1; + + { + int pip[2]; + struct job *jp; + + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob(n, 1); + if (forkshell(jp, n, FORK_NOJOB) == 0) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + eflag = 0; + evaltreenr(n, EV_EXIT); + /* NOTREACHED */ + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; + } + herefd = saveherefd; +out: + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", + result->fd, result->buf, result->nleft, result->jp)); +} + +#ifdef CONFIG_ASH_CMDCMD +static inline char ** +parse_command_args(char **argv, const char **path) +{ + char *cp, c; + + for (;;) { + cp = *++argv; + if (!cp) + return 0; + if (*cp++ != '-') + break; + if (!(c = *cp++)) + break; + if (c == '-' && !*cp) { + argv++; + break; + } + do { + switch (c) { + case 'p': + *path = defpath; + break; + default: + /* run 'typecmd' for other options */ + return 0; + } + } while ((c = *cp++)); + } + return argv; +} +#endif + + + +/* + * Execute a simple command. + */ + +static void +evalcommand(union node *cmd, int flags) +{ + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + const struct strlist *sp; + struct cmdentry cmdentry; + struct job *jp; + char *lastarg; + const char *path; + int spclbltin; + int cmd_is_exec; + int status; + char **nargv; + + /* First expand the arguments. */ + TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); + setstackmark(&smark); + back_exitstatus = 0; + + cmdentry.cmdtype = CMDBUILTIN; + cmdentry.u.cmd = &bltin; + varlist.lastp = &varlist.list; + *varlist.lastp = NULL; + arglist.lastp = &arglist.list; + *arglist.lastp = NULL; + + argc = 0; + for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) { + struct strlist **spp; + + spp = arglist.lastp; + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + for (sp = *spp; sp; sp = sp->next) + argc++; + } + + argv = nargv = stalloc(sizeof (char *) * (argc + 1)); + for (sp = arglist.list ; sp ; sp = sp->next) { + TRACE(("evalcommand arg: %s\n", sp->text)); + *nargv++ = sp->text; + } + *nargv = NULL; + + lastarg = NULL; + if (iflag && funcnest == 0 && argc > 0) + lastarg = nargv[-1]; + + preverrout_fd = 2; + expredir(cmd->ncmd.redirect); + status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2); + + path = vpath.text; + for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { + struct strlist **spp; + char *p; + + spp = varlist.lastp; + expandarg(argp, &varlist, EXP_VARTILDE); + + /* + * Modify the command lookup path, if a PATH= assignment + * is present + */ + p = (*spp)->text; + if (varequal(p, path)) + path = p; + } + + /* Print the command if xflag is set. */ + if (xflag) { + int n; + const char *p = " %s"; + + p++; + dprintf(preverrout_fd, p, ps4val()); + + sp = varlist.list; + for(n = 0; n < 2; n++) { + while (sp) { + dprintf(preverrout_fd, p, sp->text); + sp = sp->next; + if(*p == '%') { + p--; + } + } + sp = arglist.list; + } + bb_full_write(preverrout_fd, "\n", 1); + } + + cmd_is_exec = 0; + spclbltin = -1; + + /* Now locate the command. */ + if (argc) { + const char *oldpath; + int cmd_flag = DO_ERR; + + path += 5; + oldpath = path; + for (;;) { + find_command(argv[0], &cmdentry, cmd_flag, path); + if (cmdentry.cmdtype == CMDUNKNOWN) { + status = 127; + flusherr(); + goto bail; + } + + /* implement bltin and command here */ + if (cmdentry.cmdtype != CMDBUILTIN) + break; + if (spclbltin < 0) + spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd); + if (cmdentry.u.cmd == EXECCMD) + cmd_is_exec++; +#ifdef CONFIG_ASH_CMDCMD + if (cmdentry.u.cmd == COMMANDCMD) { + + path = oldpath; + nargv = parse_command_args(argv, &path); + if (!nargv) + break; + argc -= nargv - argv; + argv = nargv; + cmd_flag |= DO_NOFUNC; + } else +#endif + break; + } + } + + if (status) { + /* We have a redirection error. */ + if (spclbltin > 0) + exraise(EXERROR); +bail: + exitstatus = status; + goto out; + } + + /* Execute the command. */ + switch (cmdentry.cmdtype) { + default: + /* Fork off a child process if necessary. */ + if (!(flags & EV_EXIT) || trap[0]) { + INTOFF; + jp = makejob(cmd, 1); + if (forkshell(jp, cmd, FORK_FG) != 0) { + exitstatus = waitforjob(jp); + INTON; + break; + } + FORCEINTON; + } + listsetvar(varlist.list, VEXPORT|VSTACK); + shellexec(argv, path, cmdentry.u.index); + /* NOTREACHED */ + + case CMDBUILTIN: + cmdenviron = varlist.list; + if (cmdenviron) { + struct strlist *list = cmdenviron; + int i = VNOSET; + if (spclbltin > 0 || argc == 0) { + i = 0; + if (cmd_is_exec && argc > 1) + i = VEXPORT; + } + listsetvar(list, i); + } + if (evalbltin(cmdentry.u.cmd, argc, argv)) { + int exit_status; + int i, j; + + i = exception; + if (i == EXEXIT) + goto raise; + + exit_status = 2; + j = 0; + if (i == EXINT) + j = SIGINT; + if (i == EXSIG) + j = pendingsigs; + if (j) + exit_status = j + 128; + exitstatus = exit_status; + + if (i == EXINT || spclbltin > 0) { +raise: + longjmp(handler->loc, 1); + } + FORCEINTON; + } + break; + + case CMDFUNCTION: + listsetvar(varlist.list, 0); + if (evalfun(cmdentry.u.func, argc, argv, flags)) + goto raise; + break; + } + +out: + popredir(cmd_is_exec); + if (lastarg) + /* dsl: I think this is intended to be used to support + * '_' in 'vi' command mode during line editing... + * However I implemented that within libedit itself. + */ + setvar("_", lastarg, 0); + popstackmark(&smark); +} + +static int +evalbltin(const struct builtincmd *cmd, int argc, char **argv) { + char *volatile savecmdname; + struct jmploc *volatile savehandler; + struct jmploc jmploc; + int i; + + savecmdname = commandname; + if ((i = setjmp(jmploc.loc))) + goto cmddone; + savehandler = handler; + handler = &jmploc; + commandname = argv[0]; + argptr = argv + 1; + optptr = NULL; /* initialize nextopt */ + exitstatus = (*cmd->builtin)(argc, argv); + flushall(); +cmddone: + exitstatus |= ferror(stdout); + commandname = savecmdname; + exsig = 0; + handler = savehandler; + + return i; +} + +static int +evalfun(struct funcnode *func, int argc, char **argv, int flags) +{ + volatile struct shparam saveparam; + struct localvar *volatile savelocalvars; + struct jmploc *volatile savehandler; + struct jmploc jmploc; + int e; + + saveparam = shellparam; + savelocalvars = localvars; + if ((e = setjmp(jmploc.loc))) { + goto funcdone; + } + INTOFF; + savehandler = handler; + handler = &jmploc; + localvars = NULL; + shellparam.malloc = 0; + func->count++; + INTON; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; +#ifdef CONFIG_ASH_GETOPTS + shellparam.optind = 1; + shellparam.optoff = -1; +#endif + funcnest++; + evaltree(&func->n, flags & EV_TESTED); + funcnest--; +funcdone: + INTOFF; + freefunc(func); + poplocalvars(); + localvars = savelocalvars; + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + INTON; + if (evalskip == SKIPFUNC) { + evalskip = 0; + skipcount = 0; + } + return e; +} + + +static inline int +goodname(const char *p) +{ + return !*endofname(p); +} + +/* + * Search for a command. This is called before we fork so that the + * location of the command will be available in the parent as well as + * the child. The check for "goodname" is an overly conservative + * check that the name will not be subject to expansion. + */ + +static void +prehash(union node *n) +{ + struct cmdentry entry; + + if (n->type == NCMD && n->ncmd.args) + if (goodname(n->ncmd.args->narg.text)) + find_command(n->ncmd.args->narg.text, &entry, 0, + pathval()); +} + + + +/* + * Builtin commands. Builtin commands whose functions are closely + * tied to evaluation are implemented here. + */ + +/* + * No command given. + */ + +static int +bltincmd(int argc, char **argv) +{ + /* + * Preserve exitstatus of a previous possible redirection + * as POSIX mandates + */ + return back_exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +static int +breakcmd(int argc, char **argv) +{ + int n = argc > 1 ? number(argv[1]) : 1; + + if (n <= 0) + error(illnum, argv[1]); + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +static int +returncmd(int argc, char **argv) +{ + int ret = argc > 1 ? number(argv[1]) : exitstatus; + + if (funcnest) { + evalskip = SKIPFUNC; + skipcount = 1; + return ret; + } + else { + /* Do what ksh does; skip the rest of the file */ + evalskip = SKIPFILE; + skipcount = 1; + return ret; + } +} + + +static int +falsecmd(int argc, char **argv) +{ + return 1; +} + + +static int +truecmd(int argc, char **argv) +{ + return 0; +} + + +static int +execcmd(int argc, char **argv) +{ + if (argc > 1) { + iflag = 0; /* exit on error */ + mflag = 0; + optschanged(); + shellexec(argv + 1, pathval(), 0); + } + return 0; +} + + +/* $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $ */ + +/* + * When commands are first encountered, they are entered in a hash table. + * This ensures that a full path search will not have to be done for them + * on each invocation. + * + * We should investigate converting to a linear search, even though that + * would make the command name "hash" a misnomer. + */ + +#define CMDTABLESIZE 31 /* should be prime */ +#define ARB 1 /* actual size determined at run time */ + + + +struct tblentry { + struct tblentry *next; /* next entry in hash chain */ + union param param; /* definition of builtin function */ + short cmdtype; /* index identifying command */ + char rehash; /* if set, cd done since entry created */ + char cmdname[ARB]; /* name of command */ +}; + + +static struct tblentry *cmdtable[CMDTABLESIZE]; +static int builtinloc = -1; /* index in path of %builtin, or -1 */ + + +static void tryexec(char *, char **, char **); +static void clearcmdentry(int); +static struct tblentry *cmdlookup(const char *, int); +static void delete_cmd_entry(void); + + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + */ + +static void +shellexec(char **argv, const char *path, int idx) +{ + char *cmdname; + int e; + char **envp; + + clearredir(1); + envp = environment(); + if (strchr(argv[0], '/') != NULL +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + || find_applet_by_name(argv[0]) +#endif + ) { + tryexec(argv[0], argv, envp); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--idx < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp); + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + } + stunalloc(cmdname); + } + } + + /* Map to POSIX errors */ + switch (e) { + case EACCES: + exerrno = 126; + break; + case ENOENT: + exerrno = 127; + break; + default: + exerrno = 2; + break; + } + TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", + argv[0], e, suppressint )); + exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); + /* NOTREACHED */ +} + + +static void +tryexec(char *cmd, char **argv, char **envp) +{ + int repeated = 0; +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + int flg_bb = 0; + char *name = cmd; + + if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) { + flg_bb = 1; + } + if(flg_bb) { + char **ap; + char **new; + + *argv = name; + if(strcmp(name, "busybox")) { + for (ap = argv; *ap; ap++); + ap = new = xmalloc((ap - argv + 2) * sizeof(char *)); + *ap++ = cmd = "/bin/busybox"; + while ((*ap++ = *argv++)); + argv = new; + repeated++; + } else { + cmd = "/bin/busybox"; + } + } +#endif + +repeat: +#ifdef SYSV + do { + execve(cmd, argv, envp); + } while (errno == EINTR); +#else + execve(cmd, argv, envp); +#endif + if (repeated++) { + ckfree(argv); + } else if (errno == ENOEXEC) { + char **ap; + char **new; + + for (ap = argv; *ap; ap++) + ; + ap = new = ckmalloc((ap - argv + 2) * sizeof(char *)); + ap[1] = cmd; + *ap = cmd = (char *)DEFAULT_SHELL; + ap += 2; + argv++; + while ((*ap++ = *argv++)) + ; + argv = new; + goto repeat; + } +} + + + +/* + * Do a path search. The variable path (passed by reference) should be + * set to the start of the path before the first call; padvance will update + * this value as it proceeds. Successive calls to padvance will return + * the possible path expansions in sequence. If an option (indicated by + * a percent sign) appears in the path entry then the global variable + * pathopt will be set to point to it; otherwise pathopt will be set to + * NULL. + */ + +static char * +padvance(const char **path, const char *name) +{ + const char *p; + char *q; + const char *start; + size_t len; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start ; *p && *p != ':' && *p != '%' ; p++); + len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ + while (stackblocksize() < len) + growstackblock(); + q = stackblock(); + if (p != start) { + memcpy(q, start, p - start); + q += p - start; + *q++ = '/'; + } + strcpy(q, name); + pathopt = NULL; + if (*p == '%') { + pathopt = ++p; + while (*p && *p != ':') p++; + } + if (*p == ':') + *path = p + 1; + else + *path = NULL; + return stalloc(len); +} + + +/*** Command hashing code ***/ + +static void +printentry(struct tblentry *cmdp) +{ + int idx; + const char *path; + char *name; + + idx = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--idx >= 0); + out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); +} + + +static int +hashcmd(int argc, char **argv) +{ + struct tblentry **pp; + struct tblentry *cmdp; + int c; + struct cmdentry entry; + char *name; + + while ((c = nextopt("r")) != '\0') { + clearcmdentry(0); + return 0; + } + if (*argptr == NULL) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL) + printentry(cmdp); + } + } + return 0; + } + c = 0; + while ((name = *argptr) != NULL) { + if ((cmdp = cmdlookup(name, 0)) != NULL + && (cmdp->cmdtype == CMDNORMAL + || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) + delete_cmd_entry(); + find_command(name, &entry, DO_ERR, pathval()); + if (entry.cmdtype == CMDUNKNOWN) + c = 1; + argptr++; + } + return c; +} + + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +static void +find_command(char *name, struct cmdentry *entry, int act, const char *path) +{ + struct tblentry *cmdp; + int idx; + int prev; + char *fullname; + struct stat statb; + int e; + int updatetbl; + struct builtincmd *bcmd; + + /* If name contains a slash, don't use PATH or hash table */ + if (strchr(name, '/') != NULL) { + entry->u.index = -1; + if (act & DO_ABS) { + while (stat(name, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + entry->cmdtype = CMDUNKNOWN; + return; + } + } + entry->cmdtype = CMDNORMAL; + return; + } + +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + if (find_applet_by_name(name)) { + entry->cmdtype = CMDNORMAL; + entry->u.index = -1; + return; + } +#endif + + updatetbl = (path == pathval()); + if (!updatetbl) { + act |= DO_ALTPATH; + if (strstr(path, "%builtin") != NULL) + act |= DO_ALTBLTIN; + } + + /* If name is in the table, check answer will be ok */ + if ((cmdp = cmdlookup(name, 0)) != NULL) { + int bit; + + switch (cmdp->cmdtype) { + default: +#if DEBUG + abort(); +#endif + case CMDNORMAL: + bit = DO_ALTPATH; + break; + case CMDFUNCTION: + bit = DO_NOFUNC; + break; + case CMDBUILTIN: + bit = DO_ALTBLTIN; + break; + } + if (act & bit) { + updatetbl = 0; + cmdp = NULL; + } else if (cmdp->rehash == 0) + /* if not invalidated by cd, we're done */ + goto success; + } + + /* If %builtin not in path, check for builtin next */ + bcmd = find_builtin(name); + if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || ( + act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0 + ))) + goto builtin_success; + + /* We have to search path. */ + prev = -1; /* where to start */ + if (cmdp && cmdp->rehash) { /* doing a rehash */ + if (cmdp->cmdtype == CMDBUILTIN) + prev = builtinloc; + else + prev = cmdp->param.index; + } + + e = ENOENT; + idx = -1; +loop: + while ((fullname = padvance(&path, name)) != NULL) { + stunalloc(fullname); + idx++; + if (pathopt) { + if (prefix(pathopt, "builtin")) { + if (bcmd) + goto builtin_success; + continue; + } else if (!(act & DO_NOFUNC) && + prefix(pathopt, "func")) { + /* handled below */ + } else { + /* ignore unimplemented options */ + continue; + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && idx <= prev) { + if (idx < prev) + continue; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat(fullname, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + goto loop; + } + e = EACCES; /* if we fail, this will be the error */ + if (!S_ISREG(statb.st_mode)) + continue; + if (pathopt) { /* this is a %func directory */ + stalloc(strlen(fullname) + 1); + readcmdfile(fullname); + if ((cmdp = cmdlookup(name, 0)) == NULL || + cmdp->cmdtype != CMDFUNCTION) + error("%s not defined in %s", name, fullname); + stunalloc(fullname); + goto success; + } + TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); + if (!updatetbl) { + entry->cmdtype = CMDNORMAL; + entry->u.index = idx; + return; + } + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDNORMAL; + cmdp->param.index = idx; + INTON; + goto success; + } + + /* We failed. If there was an entry for this command, delete it */ + if (cmdp && updatetbl) + delete_cmd_entry(); + if (act & DO_ERR) + sh_warnx("%s: %s", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +builtin_success: + if (!updatetbl) { + entry->cmdtype = CMDBUILTIN; + entry->u.cmd = bcmd; + return; + } + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.cmd = bcmd; + INTON; +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; +} + + +/* + * Wrapper around strcmp for qsort/bsearch/... + */ +static int pstrcmp(const void *a, const void *b) +{ + return strcmp((const char *) a, (*(const char *const *) b) + 1); +} + +/* + * Search the table of builtin commands. + */ + +static struct builtincmd * +find_builtin(const char *name) +{ + struct builtincmd *bp; + + bp = bsearch( + name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd), + pstrcmp + ); + return bp; +} + + + +/* + * Called when a cd is done. Marks all commands so the next time they + * are executed they will be rehashed. + */ + +static void +hashcd(void) +{ + struct tblentry **pp; + struct tblentry *cmdp; + + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL || ( + cmdp->cmdtype == CMDBUILTIN && + !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) && + builtinloc > 0 + )) + cmdp->rehash = 1; + } + } +} + + + +/* + * Fix command hash table when PATH changed. + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. + * Called with interrupts off. + */ + +static void +changepath(const char *newval) +{ + const char *old, *new; + int idx; + int firstchange; + int idx_bltin; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + idx = 0; + idx_bltin = -1; + for (;;) { + if (*old != *new) { + firstchange = idx; + if ((*old == '\0' && *new == ':') + || (*old == ':' && *new == '\0')) + firstchange++; + old = new; /* ignore subsequent differences */ + } + if (*new == '\0') + break; + if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) + idx_bltin = idx; + if (*new == ':') { + idx++; + } + new++, old++; + } + if (builtinloc < 0 && idx_bltin >= 0) + builtinloc = idx_bltin; /* zap builtins */ + if (builtinloc >= 0 && idx_bltin < 0) + firstchange = 0; + clearcmdentry(firstchange); + builtinloc = idx_bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +static void +clearcmdentry(int firstchange) +{ + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if ((cmdp->cmdtype == CMDNORMAL && + cmdp->param.index >= firstchange) + || (cmdp->cmdtype == CMDBUILTIN && + builtinloc >= firstchange)) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + * + * Interrupts must be off if called with add != 0. + */ + +static struct tblentry **lastcmdentry; + + +static struct tblentry * +cmdlookup(const char *name, int add) +{ + unsigned int hashval; + const char *p; + struct tblentry *cmdp; + struct tblentry **pp; + + p = name; + hashval = (unsigned char)*p << 4; + while (*p) + hashval += (unsigned char)*p++; + hashval &= 0x7FFF; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB + + strlen(name) + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + strcpy(cmdp->cmdname, name); + } + lastcmdentry = pp; + return cmdp; +} + +/* + * Delete the command entry returned on the last lookup. + */ + +static void +delete_cmd_entry(void) +{ + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + if (cmdp->cmdtype == CMDFUNCTION) + freefunc(cmdp->param.func); + ckfree(cmdp); + INTON; +} + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name - except special builtins. + */ + +static inline void +addcmdentry(char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp; + + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + cmdp->rehash = 0; +} + +/* + * Make a copy of a parse tree. + */ + +static inline struct funcnode * +copyfunc(union node *n) +{ + struct funcnode *f; + size_t blocksize; + + funcblocksize = offsetof(struct funcnode, n); + funcstringsize = 0; + calcsize(n); + blocksize = funcblocksize; + f = ckmalloc(blocksize + funcstringsize); + funcblock = (char *) f + offsetof(struct funcnode, n); + funcstring = (char *) f + blocksize; + copynode(n); + f->count = 0; + return f; +} + +/* + * Define a shell function. + */ + +static void +defun(char *name, union node *func) +{ + struct cmdentry entry; + + INTOFF; + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(func); + addcmdentry(name, &entry); + INTON; +} + + +/* + * Delete a function if it exists. + */ + +static void +unsetfunc(const char *name) +{ + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && + cmdp->cmdtype == CMDFUNCTION) + delete_cmd_entry(); +} + +/* + * Locate and print what a word is... + */ + + +#ifdef CONFIG_ASH_CMDCMD +static int +describe_command(char *command, int describe_command_verbose) +#else +#define describe_command_verbose 1 +static int +describe_command(char *command) +#endif +{ + struct cmdentry entry; + struct tblentry *cmdp; +#ifdef CONFIG_ASH_ALIAS + const struct alias *ap; +#endif + const char *path = pathval(); + + if (describe_command_verbose) { + out1str(command); + } + + /* First look at the keywords */ + if (findkwd(command)) { + out1str(describe_command_verbose ? " is a shell keyword" : command); + goto out; + } + +#ifdef CONFIG_ASH_ALIAS + /* Then look at the aliases */ + if ((ap = lookupalias(command, 0)) != NULL) { + if (describe_command_verbose) { + out1fmt(" is an alias for %s", ap->val); + } else { + out1str("alias "); + printalias(ap); + return 0; + } + goto out; + } +#endif + /* Then check if it is a tracked alias */ + if ((cmdp = cmdlookup(command, 0)) != NULL) { + entry.cmdtype = cmdp->cmdtype; + entry.u = cmdp->param; + } else { + /* Finally use brute force */ + find_command(command, &entry, DO_ABS, path); + } + + switch (entry.cmdtype) { + case CMDNORMAL: { + int j = entry.u.index; + char *p; + if (j == -1) { + p = command; + } else { + do { + p = padvance(&path, command); + stunalloc(p); + } while (--j >= 0); + } + if (describe_command_verbose) { + out1fmt(" is%s %s", + (cmdp ? " a tracked alias for" : nullstr), p + ); + } else { + out1str(p); + } + break; + } + + case CMDFUNCTION: + if (describe_command_verbose) { + out1str(" is a shell function"); + } else { + out1str(command); + } + break; + + case CMDBUILTIN: + if (describe_command_verbose) { + out1fmt(" is a %sshell builtin", + IS_BUILTIN_SPECIAL(entry.u.cmd) ? + "special " : nullstr + ); + } else { + out1str(command); + } + break; + + default: + if (describe_command_verbose) { + out1str(": not found\n"); + } + return 127; + } + +out: + outstr("\n", stdout); + return 0; +} + +static int +typecmd(int argc, char **argv) +{ + int i; + int err = 0; + + for (i = 1; i < argc; i++) { +#ifdef CONFIG_ASH_CMDCMD + err |= describe_command(argv[i], 1); +#else + err |= describe_command(argv[i]); +#endif + } + return err; +} + +#ifdef CONFIG_ASH_CMDCMD +static int +commandcmd(int argc, char **argv) +{ + int c; + int default_path = 0; + int verify_only = 0; + int verbose_verify_only = 0; + + while ((c = nextopt("pvV")) != '\0') + switch (c) { + default: +#ifdef DEBUG + fprintf(stderr, +"command: nextopt returned character code 0%o\n", c); + return EX_SOFTWARE; +#endif + case 'p': + default_path = 1; + break; + case 'v': + verify_only = 1; + break; + case 'V': + verbose_verify_only = 1; + break; + } + + if (default_path + verify_only + verbose_verify_only > 1 || + !*argptr) { + fprintf(stderr, + "command [-p] command [arg ...]\n" + "command {-v|-V} command\n"); + return EX_USAGE; + } + + if (verify_only || verbose_verify_only) { + return describe_command(*argptr, verbose_verify_only); + } + + return 0; +} +#endif + +/* $NetBSD: expand.c,v 1.56 2002/11/24 22:35:39 christos Exp $ */ + +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ + +/* + * _rmescape() flags + */ +#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ +#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ +#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */ +#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ +#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ + +/* + * Structure specifying which parts of the string should be searched + * for IFS characters. + */ + +struct ifsregion { + struct ifsregion *next; /* next region in list */ + int begoff; /* offset of start of region */ + int endoff; /* offset of end of region */ + int nulonly; /* search for nul bytes only */ +}; + +/* output of current string */ +static char *expdest; +/* list of back quote expressions */ +static struct nodelist *argbackq; +/* first struct in list of ifs regions */ +static struct ifsregion ifsfirst; +/* last struct in list */ +static struct ifsregion *ifslastp; +/* holds expanded arg list */ +static struct arglist exparg; + +static void argstr(char *, int); +static char *exptilde(char *, char *, int); +static void expbackq(union node *, int, int); +static const char *subevalvar(char *, char *, int, int, int, int, int); +static char *evalvar(char *, int); +static void strtodest(const char *, int, int); +static void memtodest(const char *p, size_t len, int syntax, int quotes); +static ssize_t varvalue(char *, int, int); +static void recordregion(int, int, int); +static void removerecordregions(int); +static void ifsbreakup(char *, struct arglist *); +static void ifsfree(void); +static void expandmeta(struct strlist *, int); +static int patmatch(char *, const char *); + +static int cvtnum(arith_t); +static size_t esclen(const char *, const char *); +static char *scanleft(char *, char *, char *, char *, int, int); +static char *scanright(char *, char *, char *, char *, int, int); +static void varunset(const char *, const char *, const char *, int) + __attribute__((__noreturn__)); + + +#define pmatch(a, b) !fnmatch((a), (b), 0) +/* + * Prepare a pattern for a expmeta (internal glob(3)) call. + * + * Returns an stalloced string. + */ + +static inline char * +preglob(const char *pattern, int quoted, int flag) { + flag |= RMESCAPE_GLOB; + if (quoted) { + flag |= RMESCAPE_QUOTED; + } + return _rmescapes((char *)pattern, flag); +} + + +static size_t +esclen(const char *start, const char *p) { + size_t esc = 0; + + while (p > start && *--p == CTLESC) { + esc++; + } + return esc; +} + + +/* + * Expand shell variables and backquotes inside a here document. + */ + +static inline void +expandhere(union node *arg, int fd) +{ + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + bb_full_write(fd, stackblock(), expdest - (char *)stackblock()); +} + + +/* + * Perform variable substitution and command substitution on an argument, + * placing the resulting list of arguments in arglist. If EXP_FULL is true, + * perform splitting and file name expansion. When arglist is NULL, perform + * here document expansion. + */ + +void +expandarg(union node *arg, struct arglist *arglist, int flag) +{ + struct strlist *sp; + char *p; + + argbackq = arg->narg.backquote; + STARTSTACKSTR(expdest); + ifsfirst.next = NULL; + ifslastp = NULL; + argstr(arg->narg.text, flag); + if (arglist == NULL) { + return; /* here document expanded */ + } + STPUTC('\0', expdest); + p = grabstackstr(expdest); + exparg.lastp = &exparg.list; + /* + * TODO - EXP_REDIR + */ + if (flag & EXP_FULL) { + ifsbreakup(p, &exparg); + *exparg.lastp = NULL; + exparg.lastp = &exparg.list; + expandmeta(exparg.list, flag); + } else { + if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ + rmescapes(p); + sp = (struct strlist *)stalloc(sizeof (struct strlist)); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; + } + if (ifsfirst.next) + ifsfree(); + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } +} + + +/* + * Perform variable and command substitution. If EXP_FULL is set, output CTLESC + * characters to allow for further processing. Otherwise treat + * $@ like $* since no splitting will be performed. + */ + +static void +argstr(char *p, int flag) +{ + static const char spclchars[] = { + '=', + ':', + CTLQUOTEMARK, + CTLENDVAR, + CTLESC, + CTLVAR, + CTLBACKQ, + CTLBACKQ | CTLQUOTE, +#ifdef CONFIG_ASH_MATH_SUPPORT + CTLENDARI, +#endif + 0 + }; + const char *reject = spclchars; + int c; + int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ + int breakall = flag & EXP_WORD; + int inquotes; + size_t length; + int startloc; + + if (!(flag & EXP_VARTILDE)) { + reject += 2; + } else if (flag & EXP_VARTILDE2) { + reject++; + } + inquotes = 0; + length = 0; + if (flag & EXP_TILDE) { + char *q; + + flag &= ~EXP_TILDE; +tilde: + q = p; + if (*q == CTLESC && (flag & EXP_QWORD)) + q++; + if (*q == '~') + p = exptilde(p, q, flag); + } +start: + startloc = expdest - (char *)stackblock(); + for (;;) { + length += strcspn(p + length, reject); + c = p[length]; + if (c && (!(c & 0x80) +#ifdef CONFIG_ASH_MATH_SUPPORT + || c == CTLENDARI +#endif + )) { + /* c == '=' || c == ':' || c == CTLENDARI */ + length++; + } + if (length > 0) { + int newloc; + expdest = stnputs(p, length, expdest); + newloc = expdest - (char *)stackblock(); + if (breakall && !inquotes && newloc > startloc) { + recordregion(startloc, newloc, 0); + } + startloc = newloc; + } + p += length + 1; + length = 0; + + switch (c) { + case '\0': + goto breakloop; + case '=': + if (flag & EXP_VARTILDE2) { + p--; + continue; + } + flag |= EXP_VARTILDE2; + reject++; + /* fall through */ + case ':': + /* + * sort of a hack - expand tildes in variable + * assignments (after the first '=' and after ':'s). + */ + if (*--p == '~') { + goto tilde; + } + continue; + } + + switch (c) { + case CTLENDVAR: /* ??? */ + goto breakloop; + case CTLQUOTEMARK: + /* "$@" syntax adherence hack */ + if ( + !inquotes && + !memcmp(p, dolatstr, DOLATSTRLEN) && + (p[4] == CTLQUOTEMARK || ( + p[4] == CTLENDVAR && + p[5] == CTLQUOTEMARK + )) + ) { + p = evalvar(p + 1, flag) + 1; + goto start; + } + inquotes = !inquotes; +addquote: + if (quotes) { + p--; + length++; + startloc++; + } + break; + case CTLESC: + startloc++; + length++; + goto addquote; + case CTLVAR: + p = evalvar(p, flag); + goto start; + case CTLBACKQ: + c = 0; + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c, quotes); + argbackq = argbackq->next; + goto start; +#ifdef CONFIG_ASH_MATH_SUPPORT + case CTLENDARI: + p--; + expari(quotes); + goto start; +#endif + } + } +breakloop: + ; +} + +static char * +exptilde(char *startp, char *p, int flag) +{ + char c; + char *name; + struct passwd *pw; + const char *home; + int quotes = flag & (EXP_FULL | EXP_CASE); + int startloc; + + name = p + 1; + + while ((c = *++p) != '\0') { + switch(c) { + case CTLESC: + return (startp); + case CTLQUOTEMARK: + return (startp); + case ':': + if (flag & EXP_VARTILDE) + goto done; + break; + case '/': + case CTLENDVAR: + goto done; + } + } +done: + *p = '\0'; + if (*name == '\0') { + if ((home = lookupvar(homestr)) == NULL) + goto lose; + } else { + if ((pw = getpwnam(name)) == NULL) + goto lose; + home = pw->pw_dir; + } + if (*home == '\0') + goto lose; + *p = c; + startloc = expdest - (char *)stackblock(); + strtodest(home, SQSYNTAX, quotes); + recordregion(startloc, expdest - (char *)stackblock(), 0); + return (p); +lose: + *p = c; + return (startp); +} + + +static void +removerecordregions(int endoff) +{ + if (ifslastp == NULL) + return; + + if (ifsfirst.endoff > endoff) { + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + if (ifsfirst.begoff > endoff) + ifslastp = NULL; + else { + ifslastp = &ifsfirst; + ifsfirst.endoff = endoff; + } + return; + } + + ifslastp = &ifsfirst; + while (ifslastp->next && ifslastp->next->begoff < endoff) + ifslastp=ifslastp->next; + while (ifslastp->next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifslastp->next->next; + ckfree(ifslastp->next); + ifslastp->next = ifsp; + INTON; + } + if (ifslastp->endoff > endoff) + ifslastp->endoff = endoff; +} + + +#ifdef CONFIG_ASH_MATH_SUPPORT +/* + * Expand arithmetic expression. Backup to start of expression, + * evaluate, place result in (backed up) result, adjust string position. + */ +void +expari(int quotes) +{ + char *p, *start; + int begoff; + int flag; + int len; + + /* ifsfree(); */ + + /* + * This routine is slightly over-complicated for + * efficiency. Next we scan backwards looking for the + * start of arithmetic. + */ + start = stackblock(); + p = expdest - 1; + *p = '\0'; + p--; + do { + int esc; + + while (*p != CTLARI) { + p--; +#ifdef DEBUG + if (p < start) { + error("missing CTLARI (shouldn't happen)"); + } +#endif + } + + esc = esclen(start, p); + if (!(esc % 2)) { + break; + } + + p -= esc + 1; + } while (1); + + begoff = p - start; + + removerecordregions(begoff); + + flag = p[1]; + + expdest = p; + + if (quotes) + rmescapes(p + 2); + + len = cvtnum(dash_arith(p + 2)); + + if (flag != '"') + recordregion(begoff, begoff + len, 0); +} +#endif + +/* + * Expand stuff in backwards quotes. + */ + +static void +expbackq(union node *cmd, int quoted, int quotes) +{ + struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest; + int startloc; + int syntax = quoted? DQSYNTAX : BASESYNTAX; + struct stackmark smark; + + INTOFF; + setstackmark(&smark); + dest = expdest; + startloc = dest - (char *)stackblock(); + grabstackstr(dest); + evalbackcmd(cmd, (struct backcmd *) &in); + popstackmark(&smark); + + p = in.buf; + i = in.nleft; + if (i == 0) + goto read; + for (;;) { + memtodest(p, i, syntax, quotes); +read: + if (in.fd < 0) + break; + i = safe_read(in.fd, buf, sizeof buf); + TRACE(("expbackq: read returns %d\n", i)); + if (i <= 0) + break; + p = buf; + } + + if (in.buf) + ckfree(in.buf); + if (in.fd >= 0) { + close(in.fd); + back_exitstatus = waitforjob(in.jp); + } + INTON; + + /* Eat all trailing newlines */ + dest = expdest; + for (; dest > (char *)stackblock() && dest[-1] == '\n';) + STUNPUTC(dest); + expdest = dest; + + if (quoted == 0) + recordregion(startloc, dest - (char *)stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - (char *)stackblock()) - startloc, + (dest - (char *)stackblock()) - startloc, + stackblock() + startloc)); +} + + +static char * +scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes, + int zero) +{ + char *loc; + char *loc2; + char c; + + loc = startp; + loc2 = rmesc; + do { + int match; + const char *s = loc2; + c = *loc2; + if (zero) { + *loc2 = '\0'; + s = rmesc; + } + match = pmatch(str, s); + *loc2 = c; + if (match) + return loc; + if (quotes && *loc == CTLESC) + loc++; + loc++; + loc2++; + } while (c); + return 0; +} + + +static char * +scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes, + int zero) +{ + int esc = 0; + char *loc; + char *loc2; + + for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) { + int match; + char c = *loc2; + const char *s = loc2; + if (zero) { + *loc2 = '\0'; + s = rmesc; + } + match = pmatch(str, s); + *loc2 = c; + if (match) + return loc; + loc--; + if (quotes) { + if (--esc < 0) { + esc = esclen(startp, loc); + } + if (esc % 2) { + esc--; + loc--; + } + } + } + return 0; +} + +static const char * +subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) +{ + char *startp; + char *loc; + int saveherefd = herefd; + struct nodelist *saveargbackq = argbackq; + int amount; + char *rmesc, *rmescend; + int zero; + char *(*scan)(char *, char *, char *, char *, int , int); + + herefd = -1; + argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); + STPUTC('\0', expdest); + herefd = saveherefd; + argbackq = saveargbackq; + startp = stackblock() + startloc; + + switch (subtype) { + case VSASSIGN: + setvar(str, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + return startp; + + case VSQUESTION: + varunset(p, str, startp, varflags); + /* NOTREACHED */ + } + + subtype -= VSTRIMRIGHT; +#ifdef DEBUG + if (subtype < 0 || subtype > 3) + abort(); +#endif + + rmesc = startp; + rmescend = stackblock() + strloc; + if (quotes) { + rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); + if (rmesc != startp) { + rmescend = expdest; + startp = stackblock() + startloc; + } + } + rmescend--; + str = stackblock() + strloc; + preglob(str, varflags & VSQUOTE, 0); + + /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ + zero = subtype >> 1; + /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ + scan = (subtype & 1) ^ zero ? scanleft : scanright; + + loc = scan(startp, rmesc, rmescend, str, quotes, zero); + if (loc) { + if (zero) { + memmove(startp, loc, str - loc); + loc = startp + (str - loc) - 1; + } + *loc = '\0'; + amount = loc - expdest; + STADJUST(amount, expdest); + } + return loc; +} + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ +static char * +evalvar(char *p, int flag) +{ + int subtype; + int varflags; + char *var; + int patloc; + int c; + int startloc; + ssize_t varlen; + int easy; + int quotes; + int quoted; + + quotes = flag & (EXP_FULL | EXP_CASE); + varflags = *p++; + subtype = varflags & VSTYPE; + quoted = varflags & VSQUOTE; + var = p; + easy = (!quoted || (*var == '@' && shellparam.nparam)); + startloc = expdest - (char *)stackblock(); + p = strchr(p, '=') + 1; + +again: + varlen = varvalue(var, varflags, flag); + if (varflags & VSNUL) + varlen--; + + if (subtype == VSPLUS) { + varlen = -1 - varlen; + goto vsplus; + } + + if (subtype == VSMINUS) { +vsplus: + if (varlen < 0) { + argstr( + p, flag | EXP_TILDE | + (quoted ? EXP_QWORD : EXP_WORD) + ); + goto end; + } + if (easy) + goto record; + goto end; + } + + if (subtype == VSASSIGN || subtype == VSQUESTION) { + if (varlen < 0) { + if (subevalvar(p, var, 0, subtype, startloc, + varflags, 0)) { + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; + } + goto end; + } + if (easy) + goto record; + goto end; + } + + if (varlen < 0 && uflag) + varunset(p, var, 0, 0); + + if (subtype == VSLENGTH) { + cvtnum(varlen > 0 ? varlen : 0); + goto record; + } + + if (subtype == VSNORMAL) { + if (!easy) + goto end; +record: + recordregion(startloc, expdest - (char *)stackblock(), quoted); + goto end; + } + +#ifdef DEBUG + switch (subtype) { + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + break; + default: + abort(); + } +#endif + + if (varlen >= 0) { + /* + * Terminate the string and start recording the pattern + * right after it + */ + STPUTC('\0', expdest); + patloc = expdest - (char *)stackblock(); + if (subevalvar(p, NULL, patloc, subtype, + startloc, varflags, quotes) == 0) { + int amount = expdest - ( + (char *)stackblock() + patloc - 1 + ); + STADJUST(-amount, expdest); + } + /* Remove any recorded regions beyond start of variable */ + removerecordregions(startloc); + goto record; + } + +end: + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (varlen >= 0) + argbackq = argbackq->next; + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + +/* + * Put a string on the stack. + */ + +static void +memtodest(const char *p, size_t len, int syntax, int quotes) { + char *q = expdest; + + q = makestrspace(len * 2, q); + + while (len--) { + int c = *p++; + if (!c) + continue; + if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK)) + USTPUTC(CTLESC, q); + USTPUTC(c, q); + } + + expdest = q; +} + + +static void +strtodest(const char *p, int syntax, int quotes) +{ + memtodest(p, strlen(p), syntax, quotes); +} + + +/* + * Add the value of a specialized variable to the stack string. + */ + +static ssize_t +varvalue(char *name, int varflags, int flags) +{ + int num; + char *p; + int i; + int sep = 0; + int sepq = 0; + ssize_t len = 0; + char **ap; + int syntax; + int quoted = varflags & VSQUOTE; + int subtype = varflags & VSTYPE; + int quotes = flags & (EXP_FULL | EXP_CASE); + + if (quoted && (flags & EXP_FULL)) + sep = 1 << CHAR_BIT; + + syntax = quoted ? DQSYNTAX : BASESYNTAX; + switch (*name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = exitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; + if (num == 0) + return -1; +numvar: + len = cvtnum(num); + break; + case '-': + p = makestrspace(NOPTS, expdest); + for (i = NOPTS - 1; i >= 0; i--) { + if (optlist[i]) { + USTPUTC(optletters(i), p); + len++; + } + } + expdest = p; + break; + case '@': + if (sep) + goto param; + /* fall through */ + case '*': + sep = ifsset() ? ifsval()[0] : ' '; + if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK)) + sepq = 1; +param: + if (!(ap = shellparam.p)) + return -1; + while ((p = *ap++)) { + size_t partlen; + + partlen = strlen(p); + + len += partlen; + if (len > partlen && sep) { + char *q; + + len++; + if (subtype == VSPLUS || subtype == VSLENGTH) { + continue; + } + q = expdest; + if (sepq) + STPUTC(CTLESC, q); + STPUTC(sep, q); + expdest = q; + } + + if (!(subtype == VSPLUS || subtype == VSLENGTH)) + memtodest(p, partlen, syntax, quotes); + } + return len; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + num = atoi(name); + if (num < 0 || num > shellparam.nparam) + return -1; + p = num ? shellparam.p[num - 1] : arg0; + goto value; + default: + p = lookupvar(name); +value: + if (!p) + return -1; + + len = strlen(p); + if (!(subtype == VSPLUS || subtype == VSLENGTH)) + memtodest(p, len, syntax, quotes); + return len; + } + + if (subtype == VSPLUS || subtype == VSLENGTH) + STADJUST(-len, expdest); + return len; +} + + +/* + * Record the fact that we have to scan this region of the + * string for IFS characters. + */ + +static void +recordregion(int start, int end, int nulonly) +{ + struct ifsregion *ifsp; + + if (ifslastp == NULL) { + ifsp = &ifsfirst; + } else { + INTOFF; + ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); + ifsp->next = NULL; + ifslastp->next = ifsp; + INTON; + } + ifslastp = ifsp; + ifslastp->begoff = start; + ifslastp->endoff = end; + ifslastp->nulonly = nulonly; +} + + +/* + * Break the argument string into pieces based upon IFS and add the + * strings to the argument list. The regions of the string to be + * searched for IFS characters have been stored by recordregion. + */ +static void +ifsbreakup(char *string, struct arglist *arglist) +{ + struct ifsregion *ifsp; + struct strlist *sp; + char *start; + char *p; + char *q; + const char *ifs, *realifs; + int ifsspc; + int nulonly; + + + start = string; + if (ifslastp != NULL) { + ifsspc = 0; + nulonly = 0; + realifs = ifsset() ? ifsval() : defifs; + ifsp = &ifsfirst; + do { + p = string + ifsp->begoff; + nulonly = ifsp->nulonly; + ifs = nulonly ? nullstr : realifs; + ifsspc = 0; + while (p < string + ifsp->endoff) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p)) { + if (!nulonly) + ifsspc = (strchr(defifs, *p) != NULL); + /* Ignore IFS whitespace at start */ + if (q == start && ifsspc) { + p++; + start = p; + continue; + } + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + p++; + if (!nulonly) { + for (;;) { + if (p >= string + ifsp->endoff) { + break; + } + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p) == NULL ) { + p = q; + break; + } else if (strchr(defifs, *p) == NULL) { + if (ifsspc) { + p++; + ifsspc = 0; + } else { + p = q; + break; + } + } else + p++; + } + } + start = p; + } else + p++; + } + } while ((ifsp = ifsp->next) != NULL); + if (nulonly) + goto add; + } + + if (!*start) + return; + +add: + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; +} + +static void +ifsfree(void) +{ + struct ifsregion *p; + + INTOFF; + p = ifsfirst.next; + do { + struct ifsregion *ifsp; + ifsp = p->next; + ckfree(p); + p = ifsp; + } while (p); + ifslastp = NULL; + ifsfirst.next = NULL; + INTON; +} + +static void expmeta(char *, char *); +static struct strlist *expsort(struct strlist *); +static struct strlist *msort(struct strlist *, int); + +static char *expdir; + + +static void +expandmeta(struct strlist *str, int flag) +{ + static const char metachars[] = { + '*', '?', '[', 0 + }; + /* TODO - EXP_REDIR */ + + while (str) { + struct strlist **savelastp; + struct strlist *sp; + char *p; + + if (fflag) + goto nometa; + if (!strpbrk(str->text, metachars)) + goto nometa; + savelastp = exparg.lastp; + + INTOFF; + p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); + { + int i = strlen(str->text); + expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ + } + + expmeta(expdir, p); + ckfree(expdir); + if (p != str->text) + ckfree(p); + INTON; + if (exparg.lastp == savelastp) { + /* + * no matches + */ +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + } else { + *exparg.lastp = NULL; + *savelastp = sp = expsort(*savelastp); + while (sp->next != NULL) + sp = sp->next; + exparg.lastp = &sp->next; + } + str = str->next; + } +} + +/* + * Add a file name to the list. + */ + +static void +addfname(const char *name) +{ + struct strlist *sp; + + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = sstrdup(name); + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +static void +expmeta(char *enddir, char *name) +{ + char *p; + const char *cp; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + + metaflag = 0; + start = name; + for (p = name; *p; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + char *q = p + 1; + if (*q == '!') + q++; + for (;;) { + if (*q == '\\') + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '\\') + p++; + else if (*p == '/') { + if (metaflag) + goto out; + start = p + 1; + } + } +out: + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + p = name; + do { + if (*p == '\\') + p++; + *enddir++ = *p; + } while (*p++); + if (metaflag == 0 || lstat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (name < start) { + p = name; + do { + if (*p == '\\') + p++; + *enddir++ = *p++; + } while (p < start); + } + if (enddir == expdir) { + cp = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + cp = "/"; + } else { + cp = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(cp)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname++ = '\0'; + } + matchdot = 0; + p = start; + if (*p == '\\') + p++; + if (*p == '.') + matchdot++; + while (! intpending && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (pmatch(start, dp->d_name)) { + if (atend) { + scopy(dp->d_name, enddir); + addfname(expdir); + } else { + for (p = enddir, cp = dp->d_name; + (*p++ = *cp++) != '\0';) + continue; + p[-1] = '/'; + expmeta(p, endname); + } + } + } + closedir(dirp); + if (! atend) + endname[-1] = '/'; +} + +/* + * Sort the results of file name expansion. It calculates the number of + * strings to sort and then calls msort (short for merge sort) to do the + * work. + */ + +static struct strlist * +expsort(struct strlist *str) +{ + int len; + struct strlist *sp; + + len = 0; + for (sp = str ; sp ; sp = sp->next) + len++; + return msort(str, len); +} + + +static struct strlist * +msort(struct strlist *list, int len) +{ + struct strlist *p, *q = NULL; + struct strlist **lpp; + int half; + int n; + + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { +#ifdef CONFIG_LOCALE_SUPPORT + if (strcoll(p->text, q->text) < 0) +#else + if (strcmp(p->text, q->text) < 0) +#endif + { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} + + +/* + * Returns true if the pattern matches the string. + */ + +static inline int +patmatch(char *pattern, const char *string) +{ + return pmatch(preglob(pattern, 0, 0), string); +} + + +/* + * Remove any CTLESC characters from a string. + */ + +static char * +_rmescapes(char *str, int flag) +{ + char *p, *q, *r; + static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; + unsigned inquotes; + int notescaped; + int globbing; + + p = strpbrk(str, qchars); + if (!p) { + return str; + } + q = p; + r = str; + if (flag & RMESCAPE_ALLOC) { + size_t len = p - str; + size_t fulllen = len + strlen(p) + 1; + + if (flag & RMESCAPE_GROW) { + r = makestrspace(fulllen, expdest); + } else if (flag & RMESCAPE_HEAP) { + r = ckmalloc(fulllen); + } else { + r = stalloc(fulllen); + } + q = r; + if (len > 0) { + q = mempcpy(q, str, len); + } + } + inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; + globbing = flag & RMESCAPE_GLOB; + notescaped = globbing; + while (*p) { + if (*p == CTLQUOTEMARK) { + inquotes = ~inquotes; + p++; + notescaped = globbing; + continue; + } + if (*p == '\\') { + /* naked back slash */ + notescaped = 0; + goto copy; + } + if (*p == CTLESC) { + p++; + if (notescaped && inquotes && *p != '/') { + *q++ = '\\'; + } + } + notescaped = globbing; +copy: + *q++ = *p++; + } + *q = '\0'; + if (flag & RMESCAPE_GROW) { + expdest = r; + STADJUST(q - r + 1, expdest); + } + return r; +} + + +/* + * See if a pattern matches in a case statement. + */ + +int +casematch(union node *pattern, char *val) +{ + struct stackmark smark; + int result; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + ifslastp = NULL; + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + STACKSTRNUL(expdest); + result = patmatch(stackblock(), val); + popstackmark(&smark); + return result; +} + +/* + * Our own itoa(). + */ + +static int +cvtnum(arith_t num) +{ + int len; + + expdest = makestrspace(32, expdest); +#ifdef CONFIG_ASH_MATH_SUPPORT_64 + len = fmtstr(expdest, 32, "%lld", (long long) num); +#else + len = fmtstr(expdest, 32, "%ld", num); +#endif + STADJUST(len, expdest); + return len; +} + +static void +varunset(const char *end, const char *var, const char *umsg, int varflags) +{ + const char *msg; + const char *tail; + + tail = nullstr; + msg = "parameter not set"; + if (umsg) { + if (*end == CTLENDVAR) { + if (varflags & VSNUL) + tail = " or null"; + } else + msg = umsg; + } + error("%.*s: %s%s", end - var - 1, var, msg, tail); +} + + +/* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */ + +/* + * This implements the input routines used by the parser. + */ + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ +#define IBUFSIZ (BUFSIZ + 1) + +static void pushfile(void); + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +#define pgetc_as_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) + +#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE +#define pgetc_macro() pgetc() +static int +pgetc(void) +{ + return pgetc_as_macro(); +} +#else +#define pgetc_macro() pgetc_as_macro() +static int +pgetc(void) +{ + return pgetc_macro(); +} +#endif + + +/* + * Same as pgetc(), but ignores PEOA. + */ +#ifdef CONFIG_ASH_ALIAS +static int pgetc2(void) +{ + int c; + + do { + c = pgetc_macro(); + } while (c == PEOA); + return c; +} +#else +static inline int pgetc2(void) +{ + return pgetc_macro(); +} +#endif + +/* + * Read a line from the script. + */ + +static inline char * +pfgets(char *line, int len) +{ + char *p = line; + int nleft = len; + int c; + + while (--nleft > 0) { + c = pgetc2(); + if (c == PEOF) { + if (p == line) + return NULL; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = '\0'; + return line; +} + + + +#ifdef CONFIG_FEATURE_COMMAND_EDITING +static const char *cmdedit_prompt; +static inline void putprompt(const char *s) +{ + cmdedit_prompt = s; +} +#else +static inline void putprompt(const char *s) +{ + out2str(s); +} +#endif + +static inline int +preadfd(void) +{ + int nr; + char *buf = parsefile->buf; + parsenextc = buf; + +retry: +#ifdef CONFIG_FEATURE_COMMAND_EDITING + if (!iflag || parsefile->fd) + nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); + else { +#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION + cmdedit_path_lookup = pathval(); +#endif + nr = cmdedit_read_input((char *) cmdedit_prompt, buf); + if(nr == 0) { + /* Ctrl+C presend */ + if(trap[SIGINT]) { + buf[0] = '\n'; + buf[1] = 0; + raise(SIGINT); + return 1; + } + goto retry; + } + if(nr < 0 && errno == 0) { + /* Ctrl+D presend */ + nr = 0; + } + } +#else + nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); +#endif + + if (nr < 0) { + if (parsefile->fd == 0 && errno == EWOULDBLOCK) { + int flags = fcntl(0, F_GETFL, 0); + if (flags >= 0 && flags & O_NONBLOCK) { + flags &=~ O_NONBLOCK; + if (fcntl(0, F_SETFL, flags) >= 0) { + out2str("sh: turning off NDELAY mode\n"); + goto retry; + } + } + } + } + return nr; +} + +/* + * Refill the input buffer and return the next input character: + * + * 1) If a string was pushed back on the input, pop it; + * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading + * from a string so we can't refill the buffer, return EOF. + * 3) If the is more stuff in this buffer, use it else call read to fill it. + * 4) Process input up to the next newline, deleting nul characters. + */ + +int +preadbuffer(void) +{ + char *p, *q; + int more; + char savec; + + while (parsefile->strpush) { +#ifdef CONFIG_ASH_ALIAS + if (parsenleft == -1 && parsefile->strpush->ap && + parsenextc[-1] != ' ' && parsenextc[-1] != '\t') { + return PEOA; + } +#endif + popstring(); + if (--parsenleft >= 0) + return (*parsenextc++); + } + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return PEOF; + flushall(); + +again: + if (parselleft <= 0) { + if ((parselleft = preadfd()) <= 0) { + parselleft = parsenleft = EOF_NLEFT; + return PEOF; + } + } + + q = p = parsenextc; + + /* delete nul characters */ + for (more = 1; more;) { + switch (*p) { + case '\0': + p++; /* Skip nul */ + goto check; + + case '\n': + parsenleft = q - parsenextc; + more = 0; /* Stop processing here */ + break; + + } + + *q++ = *p++; +check: + if (--parselleft <= 0 && more) { + parsenleft = q - parsenextc - 1; + if (parsenleft < 0) + goto again; + more = 0; + } + } + + savec = *q; + *q = '\0'; + + if (vflag) { + out2str(parsenextc); + } + + *q = savec; + + return *parsenextc++; +} + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +void +pungetc(void) +{ + parsenleft++; + parsenextc--; +} + +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +void +pushstring(char *s, void *ap) +{ + struct strpush *sp; + size_t len; + + len = strlen(s); + INTOFF; +/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ + if (parsefile->strpush) { + sp = ckmalloc(sizeof (struct strpush)); + sp->prev = parsefile->strpush; + parsefile->strpush = sp; + } else + sp = parsefile->strpush = &(parsefile->basestrpush); + sp->prevstring = parsenextc; + sp->prevnleft = parsenleft; +#ifdef CONFIG_ASH_ALIAS + sp->ap = (struct alias *)ap; + if (ap) { + ((struct alias *)ap)->flag |= ALIASINUSE; + sp->string = s; + } +#endif + parsenextc = s; + parsenleft = len; + INTON; +} + +void +popstring(void) +{ + struct strpush *sp = parsefile->strpush; + + INTOFF; +#ifdef CONFIG_ASH_ALIAS + if (sp->ap) { + if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { + checkkwd |= CHKALIAS; + } + if (sp->string != sp->ap->val) { + ckfree(sp->string); + } + sp->ap->flag &= ~ALIASINUSE; + if (sp->ap->flag & ALIASDEAD) { + unalias(sp->ap->name); + } + } +#endif + parsenextc = sp->prevstring; + parsenleft = sp->prevnleft; +/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ + parsefile->strpush = sp->prev; + if (sp != &(parsefile->basestrpush)) + ckfree(sp); + INTON; +} + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +void +setinputfile(const char *fname, int push) +{ + int fd; + int fd2; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s", fname); + if (fd < 10) { + fd2 = copyfd(fd, 10); + close(fd); + if (fd2 < 0) + error("Out of file descriptors"); + fd = fd2; + } + setinputfd(fd, push); + INTON; +} + + +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ + +static void +setinputfd(int fd, int push) +{ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); + if (push) { + pushfile(); + parsefile->buf = 0; + } + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(IBUFSIZ); + parselleft = parsenleft = 0; + plinno = 1; +} + + +/* + * Like setinputfile, but takes input from a string. + */ + +static void +setinputstring(char *string) +{ + INTOFF; + pushfile(); + parsenextc = string; + parsenleft = strlen(string); + parsefile->buf = NULL; + plinno = 1; + INTON; +} + + +/* + * To handle the "." command, a stack of input files is used. Pushfile + * adds a new entry to the stack and popfile restores the previous level. + */ + +static void +pushfile(void) +{ + struct parsefile *pf; + + parsefile->nleft = parsenleft; + parsefile->lleft = parselleft; + parsefile->nextc = parsenextc; + parsefile->linno = plinno; + pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); + pf->prev = parsefile; + pf->fd = -1; + pf->strpush = NULL; + pf->basestrpush.prev = NULL; + parsefile = pf; +} + + +static void +popfile(void) +{ + struct parsefile *pf = parsefile; + + INTOFF; + if (pf->fd >= 0) + close(pf->fd); + if (pf->buf) + ckfree(pf->buf); + while (pf->strpush) + popstring(); + parsefile = pf->prev; + ckfree(pf); + parsenleft = parsefile->nleft; + parselleft = parsefile->lleft; + parsenextc = parsefile->nextc; + plinno = parsefile->linno; + INTON; +} + + +/* + * Return to top level. + */ + +static void +popallfiles(void) +{ + while (parsefile != &basepf) + popfile(); +} + + +/* + * Close the file(s) that the shell is reading commands from. Called + * after a fork is done. + */ + +static void +closescript(void) +{ + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} + +/* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */ + +/* mode flags for set_curjob */ +#define CUR_DELETE 2 +#define CUR_RUNNING 1 +#define CUR_STOPPED 0 + +/* mode flags for dowait */ +#define DOWAIT_NORMAL 0 +#define DOWAIT_BLOCK 1 + +/* array of jobs */ +static struct job *jobtab; +/* size of array */ +static unsigned njobs; +#if JOBS +/* pgrp of shell on invocation */ +static int initialpgrp; +static int ttyfd = -1; +#endif +/* current job */ +static struct job *curjob; +/* number of presumed living untracked jobs */ +static int jobless; + +static void set_curjob(struct job *, unsigned); +#if JOBS +static int restartjob(struct job *, int); +static void xtcsetpgrp(int, pid_t); +static char *commandtext(union node *); +static void cmdlist(union node *, int); +static void cmdtxt(union node *); +static void cmdputs(const char *); +static void showpipe(struct job *, FILE *); +#endif +static int sprint_status(char *, int, int); +static void freejob(struct job *); +static struct job *getjob(const char *, int); +static struct job *growjobtab(void); +static void forkchild(struct job *, union node *, int); +static void forkparent(struct job *, union node *, int, pid_t); +static int dowait(int, struct job *); +static int getstatus(struct job *); + +static void +set_curjob(struct job *jp, unsigned mode) +{ + struct job *jp1; + struct job **jpp, **curp; + + /* first remove from list */ + jpp = curp = &curjob; + do { + jp1 = *jpp; + if (jp1 == jp) + break; + jpp = &jp1->prev_job; + } while (1); + *jpp = jp1->prev_job; + + /* Then re-insert in correct position */ + jpp = curp; + switch (mode) { + default: +#ifdef DEBUG + abort(); +#endif + case CUR_DELETE: + /* job being deleted */ + break; + case CUR_RUNNING: + /* newly created job or backgrounded job, + put after all stopped jobs. */ + do { + jp1 = *jpp; +#ifdef JOBS + if (!jp1 || jp1->state != JOBSTOPPED) +#endif + break; + jpp = &jp1->prev_job; + } while (1); + /* FALLTHROUGH */ +#ifdef JOBS + case CUR_STOPPED: +#endif + /* newly stopped job - becomes curjob */ + jp->prev_job = *jpp; + *jpp = jp; + break; + } +} + +#if JOBS +/* + * Turn job control on and off. + * + * Note: This code assumes that the third arg to ioctl is a character + * pointer, which is true on Berkeley systems but not System V. Since + * System V doesn't have job control yet, this isn't a problem now. + * + * Called with interrupts off. + */ + +void +setjobctl(int on) +{ + int fd; + int pgrp; + + if (on == jobctl || rootshell == 0) + return; + if (on) { + int ofd; + ofd = fd = open(_PATH_TTY, O_RDWR); + if (fd < 0) { + fd += 3; + while (!isatty(fd) && --fd >= 0) + ; + } + fd = fcntl(fd, F_DUPFD, 10); + close(ofd); + if (fd < 0) + goto out; + fcntl(fd, F_SETFD, FD_CLOEXEC); + do { /* while we are in the background */ + if ((pgrp = tcgetpgrp(fd)) < 0) { +out: + sh_warnx("can't access tty; job control turned off"); + mflag = on = 0; + goto close; + } + if (pgrp == getpgrp()) + break; + killpg(0, SIGTTIN); + } while (1); + initialpgrp = pgrp; + + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + pgrp = rootpid; + setpgid(0, pgrp); + xtcsetpgrp(fd, pgrp); + } else { + /* turning job control off */ + fd = ttyfd; + pgrp = initialpgrp; + xtcsetpgrp(fd, pgrp); + setpgid(0, pgrp); + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); +close: + close(fd); + fd = -1; + } + ttyfd = fd; + jobctl = on; +} + +static int +killcmd(int argc, char **argv) +{ + int signo = -1; + int list = 0; + int i; + pid_t pid; + struct job *jp; + + if (argc <= 1) { +usage: + error( +"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n" +"kill -l [exitstatus]" + ); + } + + if (**++argv == '-') { + signo = decode_signal(*argv + 1, 1); + if (signo < 0) { + int c; + + while ((c = nextopt("ls:")) != '\0') + switch (c) { + default: +#ifdef DEBUG + abort(); +#endif + case 'l': + list = 1; + break; + case 's': + signo = decode_signal(optionarg, 1); + if (signo < 0) { + error( + "invalid signal number or name: %s", + optionarg + ); + } + break; + } + argv = argptr; + } else + argv++; + } + + if (!list && signo < 0) + signo = SIGTERM; + + if ((signo < 0 || !*argv) ^ list) { + goto usage; + } + + if (list) { + const char *name; + + if (!*argv) { + for (i = 1; i < NSIG; i++) { + name = u_signal_names(0, &i, 1); + if (name) + out1fmt(snlfmt, name); + } + return 0; + } + name = u_signal_names(*argptr, &signo, -1); + if (name) + out1fmt(snlfmt, name); + else + error("invalid signal number or exit status: %s", *argptr); + return 0; + } + + i = 0; + do { + if (**argv == '%') { + jp = getjob(*argv, 0); + pid = -jp->ps[0].pid; + } else + pid = number(*argv); + if (kill(pid, signo) != 0) { + sh_warnx("%m\n"); + i = 1; + } + } while (*++argv); + + return i; +} +#endif /* JOBS */ + +#if defined(JOBS) || defined(DEBUG) +static int +jobno(const struct job *jp) +{ + return jp - jobtab + 1; +} +#endif + +#ifdef JOBS +static int +fgcmd(int argc, char **argv) +{ + struct job *jp; + FILE *out; + int mode; + int retval; + + mode = (**argv == 'f') ? FORK_FG : FORK_BG; + nextopt(nullstr); + argv = argptr; + out = stdout; + do { + jp = getjob(*argv, 1); + if (mode == FORK_BG) { + set_curjob(jp, CUR_RUNNING); + fprintf(out, "[%d] ", jobno(jp)); + } + outstr(jp->ps->cmd, out); + showpipe(jp, out); + retval = restartjob(jp, mode); + } while (*argv && *++argv); + return retval; +} + +static int bgcmd(int, char **) __attribute__((__alias__("fgcmd"))); + + +static int +restartjob(struct job *jp, int mode) +{ + struct procstat *ps; + int i; + int status; + pid_t pgid; + + INTOFF; + if (jp->state == JOBDONE) + goto out; + jp->state = JOBRUNNING; + pgid = jp->ps->pid; + if (mode == FORK_FG) + xtcsetpgrp(ttyfd, pgid); + killpg(pgid, SIGCONT); + ps = jp->ps; + i = jp->nprocs; + do { + if (WIFSTOPPED(ps->status)) { + ps->status = -1; + } + } while (ps++, --i); +out: + status = (mode == FORK_FG) ? waitforjob(jp) : 0; + INTON; + return status; +} +#endif + +static int +sprint_status(char *s, int status, int sigonly) +{ + int col; + int st; + + col = 0; + if (!WIFEXITED(status)) { +#if JOBS + if (WIFSTOPPED(status)) + st = WSTOPSIG(status); + else +#endif + st = WTERMSIG(status); + if (sigonly) { + if (st == SIGINT || st == SIGPIPE) + goto out; +#if JOBS + if (WIFSTOPPED(status)) + goto out; +#endif + } + st &= 0x7f; + col = fmtstr(s, 32, strsignal(st)); + if (WCOREDUMP(status)) { + col += fmtstr(s + col, 16, " (core dumped)"); + } + } else if (!sigonly) { + st = WEXITSTATUS(status); + if (st) + col = fmtstr(s, 16, "Done(%d)", st); + else + col = fmtstr(s, 16, "Done"); + } + +out: + return col; +} + +#if JOBS +static void +showjob(FILE *out, struct job *jp, int mode) +{ + struct procstat *ps; + struct procstat *psend; + int col; + int indent; + char s[80]; + + ps = jp->ps; + + if (mode & SHOW_PGID) { + /* just output process (group) id of pipeline */ + fprintf(out, "%d\n", ps->pid); + return; + } + + col = fmtstr(s, 16, "[%d] ", jobno(jp)); + indent = col; + + if (jp == curjob) + s[col - 2] = '+'; + else if (curjob && jp == curjob->prev_job) + s[col - 2] = '-'; + + if (mode & SHOW_PID) + col += fmtstr(s + col, 16, "%d ", ps->pid); + + psend = ps + jp->nprocs; + + if (jp->state == JOBRUNNING) { + scopy("Running", s + col); + col += strlen("Running"); + } else { + int status = psend[-1].status; +#if JOBS + if (jp->state == JOBSTOPPED) + status = jp->stopstatus; +#endif + col += sprint_status(s + col, status, 0); + } + + goto start; + + do { + /* for each process */ + col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3; + +start: + fprintf(out, "%s%*c%s", + s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd + ); + if (!(mode & SHOW_PID)) { + showpipe(jp, out); + break; + } + if (++ps == psend) { + outcslow('\n', out); + break; + } + } while (1); + + jp->changed = 0; + + if (jp->state == JOBDONE) { + TRACE(("showjob: freeing job %d\n", jobno(jp))); + freejob(jp); + } +} + + +static int +jobscmd(int argc, char **argv) +{ + int mode, m; + FILE *out; + + mode = 0; + while ((m = nextopt("lp"))) + if (m == 'l') + mode = SHOW_PID; + else + mode = SHOW_PGID; + + out = stdout; + argv = argptr; + if (*argv) + do + showjob(out, getjob(*argv,0), mode); + while (*++argv); + else + showjobs(out, mode); + + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + */ + +static void +showjobs(FILE *out, int mode) +{ + struct job *jp; + + TRACE(("showjobs(%x) called\n", mode)); + + /* If not even one one job changed, there is nothing to do */ + while (dowait(DOWAIT_NORMAL, NULL) > 0) + continue; + + for (jp = curjob; jp; jp = jp->prev_job) { + if (!(mode & SHOW_CHANGED) || jp->changed) + showjob(out, jp, mode); + } +} +#endif /* JOBS */ + +/* + * Mark a job structure as unused. + */ + +static void +freejob(struct job *jp) +{ + struct procstat *ps; + int i; + + INTOFF; + for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { + if (ps->cmd != nullstr) + ckfree(ps->cmd); + } + if (jp->ps != &jp->ps0) + ckfree(jp->ps); + jp->used = 0; + set_curjob(jp, CUR_DELETE); + INTON; +} + + +static int +waitcmd(int argc, char **argv) +{ + struct job *job; + int retval; + struct job *jp; + + EXSIGON(); + + nextopt(nullstr); + retval = 0; + + argv = argptr; + if (!*argv) { + /* wait for all jobs */ + for (;;) { + jp = curjob; + while (1) { + if (!jp) { + /* no running procs */ + goto out; + } + if (jp->state == JOBRUNNING) + break; + jp->waited = 1; + jp = jp->prev_job; + } + dowait(DOWAIT_BLOCK, 0); + } + } + + retval = 127; + do { + if (**argv != '%') { + pid_t pid = number(*argv); + job = curjob; + goto start; + do { + if (job->ps[job->nprocs - 1].pid == pid) + break; + job = job->prev_job; +start: + if (!job) + goto repeat; + } while (1); + } else + job = getjob(*argv, 0); + /* loop until process terminated or stopped */ + while (job->state == JOBRUNNING) + dowait(DOWAIT_BLOCK, 0); + job->waited = 1; + retval = getstatus(job); +repeat: + ; + } while (*++argv); + +out: + return retval; +} + + +/* + * Convert a job name to a job structure. + */ + +static struct job * +getjob(const char *name, int getctl) +{ + struct job *jp; + struct job *found; + const char *err_msg = "No such job: %s"; + unsigned num; + int c; + const char *p; + char *(*match)(const char *, const char *); + + jp = curjob; + p = name; + if (!p) + goto currentjob; + + if (*p != '%') + goto err; + + c = *++p; + if (!c) + goto currentjob; + + if (!p[1]) { + if (c == '+' || c == '%') { +currentjob: + err_msg = "No current job"; + goto check; + } else if (c == '-') { + if (jp) + jp = jp->prev_job; + err_msg = "No previous job"; +check: + if (!jp) + goto err; + goto gotit; + } + } + + if (is_number(p)) { + num = atoi(p); + if (num < njobs) { + jp = jobtab + num - 1; + if (jp->used) + goto gotit; + goto err; + } + } + + match = prefix; + if (*p == '?') { + match = strstr; + p++; + } + + found = 0; + while (1) { + if (!jp) + goto err; + if (match(jp->ps[0].cmd, p)) { + if (found) + goto err; + found = jp; + err_msg = "%s: ambiguous"; + } + jp = jp->prev_job; + } + +gotit: +#if JOBS + err_msg = "job %s not created under job control"; + if (getctl && jp->jobctl == 0) + goto err; +#endif + return jp; +err: + error(err_msg, name); +} + + +/* + * Return a new job structure. + * Called with interrupts off. + */ + +static struct job * +makejob(union node *node, int nprocs) +{ + int i; + struct job *jp; + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + jp = growjobtab(); + break; + } + if (jp->used == 0) + break; + if (jp->state != JOBDONE || !jp->waited) + continue; +#if JOBS + if (jobctl) + continue; +#endif + freejob(jp); + break; + } + memset(jp, 0, sizeof(*jp)); +#if JOBS + if (jobctl) + jp->jobctl = 1; +#endif + jp->prev_job = curjob; + curjob = jp; + jp->used = 1; + jp->ps = &jp->ps0; + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } + TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, + jobno(jp))); + return jp; +} + +static struct job * +growjobtab(void) +{ + size_t len; + ptrdiff_t offset; + struct job *jp, *jq; + + len = njobs * sizeof(*jp); + jq = jobtab; + jp = ckrealloc(jq, len + 4 * sizeof(*jp)); + + offset = (char *)jp - (char *)jq; + if (offset) { + /* Relocate pointers */ + size_t l = len; + + jq = (struct job *)((char *)jq + l); + while (l) { + l -= sizeof(*jp); + jq--; +#define joff(p) ((struct job *)((char *)(p) + l)) +#define jmove(p) (p) = (void *)((char *)(p) + offset) + if (xlikely(joff(jp)->ps == &jq->ps0)) + jmove(joff(jp)->ps); + if (joff(jp)->prev_job) + jmove(joff(jp)->prev_job); + } + if (curjob) + jmove(curjob); +#undef joff +#undef jmove + } + + njobs += 4; + jobtab = jp; + jp = (struct job *)((char *)jp + len); + jq = jp + 3; + do { + jq->used = 0; + } while (--jq >= jp); + return jp; +} + + +/* + * Fork off a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + * + * Called with interrupts off. + */ + +static inline void +forkchild(struct job *jp, union node *n, int mode) +{ + int wasroot; + + TRACE(("Child shell %d\n", getpid())); + wasroot = rootshell; + rootshell = 0; + + closescript(); + clear_traps(); +#if JOBS + /* do job control only in root shell */ + jobctl = 0; + if (mode != FORK_NOJOB && jp->jobctl && wasroot) { + pid_t pgrp; + + if (jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + /* This can fail because we are doing it in the parent also */ + (void)setpgid(0, pgrp); + if (mode == FORK_FG) + xtcsetpgrp(ttyfd, pgrp); + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } else +#endif + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if (jp->nprocs == 0) { + close(0); + if (open(_PATH_DEVNULL, O_RDONLY) != 0) + error("Can't open %s", _PATH_DEVNULL); + } + } + if (wasroot && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + for (jp = curjob; jp; jp = jp->prev_job) + freejob(jp); + jobless = 0; +} + +static inline void +forkparent(struct job *jp, union node *n, int mode, pid_t pid) +{ + TRACE(("In parent shell: child = %d\n", pid)); + if (!jp) { + while (jobless && dowait(DOWAIT_NORMAL, 0) > 0); + jobless++; + return; + } +#if JOBS + if (mode != FORK_NOJOB && jp->jobctl) { + int pgrp; + + if (jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; + /* This can fail because we are doing it in the child also */ + (void)setpgid(pid, pgrp); + } +#endif + if (mode == FORK_BG) { + backgndpid = pid; /* set $! */ + set_curjob(jp, CUR_RUNNING); + } + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; +#if JOBS + if (jobctl && n) + ps->cmd = commandtext(n); +#endif + } +} + +static int +forkshell(struct job *jp, union node *n, int mode) +{ + int pid; + + TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); + pid = fork(); + if (pid < 0) { + TRACE(("Fork failed, errno=%d", errno)); + if (jp) + freejob(jp); + error("Cannot fork"); + } + if (pid == 0) + forkchild(jp, n, mode); + else + forkparent(jp, n, mode, pid); + return pid; +} + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * foreground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + * + * Called with interrupts off. + */ + +int +waitforjob(struct job *jp) +{ + int st; + + TRACE(("waitforjob(%%%d) called\n", jobno(jp))); + while (jp->state == JOBRUNNING) { + dowait(DOWAIT_BLOCK, jp); + } + st = getstatus(jp); +#if JOBS + if (jp->jobctl) { + xtcsetpgrp(ttyfd, rootpid); + /* + * This is truly gross. + * If we're doing job control, then we did a TIOCSPGRP which + * caused us (the shell) to no longer be in the controlling + * session -- so we wouldn't have seen any ^C/SIGINT. So, we + * intuit from the subprocess exit status whether a SIGINT + * occurred, and if so interrupt ourselves. Yuck. - mycroft + */ + if (jp->sigint) + raise(SIGINT); + } + if (jp->state == JOBDONE) +#endif + freejob(jp); + return st; +} + + +/* + * Do a wait system call. If job control is compiled in, we accept + * stopped processes. If block is zero, we return a value of zero + * rather than blocking. + * + * System V doesn't have a non-blocking wait system call. It does + * have a SIGCLD signal that is sent to a process when one of it's + * children dies. The obvious way to use SIGCLD would be to install + * a handler for SIGCLD which simply bumped a counter when a SIGCLD + * was received, and have waitproc bump another counter when it got + * the status of a process. Waitproc would then know that a wait + * system call would not block if the two counters were different. + * This approach doesn't work because if a process has children that + * have not been waited for, System V will send it a SIGCLD when it + * installs a signal handler for SIGCLD. What this means is that when + * a child exits, the shell will be sent SIGCLD signals continuously + * until is runs out of stack space, unless it does a wait call before + * restoring the signal handler. The code below takes advantage of + * this (mis)feature by installing a signal handler for SIGCLD and + * then checking to see whether it was called. If there are any + * children to be waited for, it will be. + * + * If neither SYSV nor BSD is defined, we don't implement nonblocking + * waits at all. In this case, the user will not be informed when + * a background process until the next time she runs a real program + * (as opposed to running a builtin command or just typing return), + * and the jobs command may give out of date information. + */ + +static inline int +waitproc(int block, int *status) +{ + int flags = 0; + +#if JOBS + if (jobctl) + flags |= WUNTRACED; +#endif + if (block == 0) + flags |= WNOHANG; + return wait3(status, flags, (struct rusage *)NULL); +} + +/* + * Wait for a process to terminate. + */ + +static int +dowait(int block, struct job *job) +{ + int pid; + int status; + struct job *jp; + struct job *thisjob; + int state; + + TRACE(("dowait(%d) called\n", block)); + pid = waitproc(block, &status); + TRACE(("wait returns pid %d, status=%d\n", pid, status)); + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = curjob; jp; jp = jp->prev_job) { + struct procstat *sp; + struct procstat *spend; + if (jp->state == JOBDONE) + continue; + state = JOBDONE; + spend = jp->ps + jp->nprocs; + sp = jp->ps; + do { + if (sp->pid == pid) { + TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + state = JOBRUNNING; +#ifdef JOBS + if (state == JOBRUNNING) + continue; + if (WIFSTOPPED(sp->status)) { + jp->stopstatus = sp->status; + state = JOBSTOPPED; + } +#endif + } while (++sp < spend); + if (thisjob) + goto gotjob; + } +#ifdef JOBS + if (!WIFSTOPPED(status)) +#endif + + jobless--; + goto out; + +gotjob: + if (state != JOBRUNNING) { + thisjob->changed = 1; + + if (thisjob->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state)); + thisjob->state = state; +#ifdef JOBS + if (state == JOBSTOPPED) { + set_curjob(thisjob, CUR_STOPPED); + } +#endif + } + } + +out: + INTON; + + if (thisjob && thisjob == job) { + char s[48 + 1]; + int len; + + len = sprint_status(s, status, 1); + if (len) { + s[len] = '\n'; + s[len + 1] = 0; + out2str(s); + } + } + return pid; +} + + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ + +int +stoppedjobs(void) +{ + struct job *jp; + int retval; + + retval = 0; + if (job_warning) + goto out; + jp = curjob; + if (jp && jp->state == JOBSTOPPED) { + out2str("You have stopped jobs.\n"); + job_warning = 2; + retval++; + } + +out: + return retval; +} + +/* + * Return a string identifying a command (to be printed by the + * jobs command). + */ + +#if JOBS +static char *cmdnextc; + +static char * +commandtext(union node *n) +{ + char *name; + + STARTSTACKSTR(cmdnextc); + cmdtxt(n); + name = stackblock(); + TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n", + name, cmdnextc, cmdnextc)); + return savestr(name); +} + +static void +cmdtxt(union node *n) +{ + union node *np; + struct nodelist *lp; + const char *p; + char s[2]; + + if (!n) + return; + switch (n->type) { + default: +#if DEBUG + abort(); +#endif + case NPIPE: + lp = n->npipe.cmdlist; + for (;;) { + cmdtxt(lp->n); + lp = lp->next; + if (!lp) + break; + cmdputs(" | "); + } + break; + case NSEMI: + p = "; "; + goto binop; + case NAND: + p = " && "; + goto binop; + case NOR: + p = " || "; +binop: + cmdtxt(n->nbinary.ch1); + cmdputs(p); + n = n->nbinary.ch2; + goto donode; + case NREDIR: + case NBACKGND: + n = n->nredir.n; + goto donode; + case NNOT: + cmdputs("!"); + n = n->nnot.com; +donode: + cmdtxt(n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + n = n->nif.ifpart; + if (n->nif.elsepart) { + cmdtxt(n); + cmdputs("; else "); + n = n->nif.elsepart; + } + p = "; fi"; + goto dotail; + case NSUBSHELL: + cmdputs("("); + n = n->nredir.n; + p = ")"; + goto dotail; + case NWHILE: + p = "while "; + goto until; + case NUNTIL: + p = "until "; +until: + cmdputs(p); + cmdtxt(n->nbinary.ch1); + n = n->nbinary.ch2; + p = "; done"; +dodo: + cmdputs("; do "); +dotail: + cmdtxt(n); + goto dotail2; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in "); + cmdlist(n->nfor.args, 1); + n = n->nfor.body; + p = "; done"; + goto dodo; + case NDEFUN: + cmdputs(n->narg.text); + p = "() { ... }"; + goto dotail2; + case NCMD: + cmdlist(n->ncmd.args, 1); + cmdlist(n->ncmd.redirect, 0); + break; + case NARG: + p = n->narg.text; +dotail2: + cmdputs(p); + break; + case NHERE: + case NXHERE: + p = "<<..."; + goto dotail2; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in "); + for (np = n->ncase.cases; np; np = np->nclist.next) { + cmdtxt(np->nclist.pattern); + cmdputs(") "); + cmdtxt(np->nclist.body); + cmdputs(";; "); + } + p = "esac"; + goto dotail2; + case NTO: + p = ">"; + goto redir; + case NCLOBBER: + p = ">|"; + goto redir; + case NAPPEND: + p = ">>"; + goto redir; + case NTOFD: + p = ">&"; + goto redir; + case NFROM: + p = "<"; + goto redir; + case NFROMFD: + p = "<&"; + goto redir; + case NFROMTO: + p = "<>"; +redir: + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + p = s; + goto dotail2; + } else { + n = n->nfile.fname; + goto donode; + } + } +} + +static void +cmdlist(union node *np, int sep) +{ + for (; np; np = np->narg.next) { + if (!sep) + cmdputs(spcstr); + cmdtxt(np); + if (sep && np->narg.next) + cmdputs(spcstr); + } +} + +static void +cmdputs(const char *s) +{ + const char *p, *str; + char c, cc[2] = " "; + char *nextc; + int subtype = 0; + int quoted = 0; + static const char *const vstype[16] = { + nullstr, "}", "-", "+", "?", "=", + "%", "%%", "#", "##", nullstr + }; + + nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); + p = s; + while ((c = *p++) != 0) { + str = 0; + switch (c) { + case CTLESC: + c = *p++; + break; + case CTLVAR: + subtype = *p++; + if ((subtype & VSTYPE) == VSLENGTH) + str = "${#"; + else + str = "${"; + if (!(subtype & VSQUOTE) != !(quoted & 1)) { + quoted ^= 1; + c = '"'; + } else + goto dostr; + break; + case CTLENDVAR: + quoted >>= 1; + subtype = 0; + if (quoted & 1) { + str = "\"}"; + goto dostr; + } + c = '}'; + break; + case CTLBACKQ: + str = "$(...)"; + goto dostr; + case CTLBACKQ+CTLQUOTE: + str = "\"$(...)\""; + goto dostr; +#ifdef CONFIG_ASH_MATH_SUPPORT + case CTLARI: + str = "$(("; + goto dostr; + case CTLENDARI: + str = "))"; + goto dostr; +#endif + case CTLQUOTEMARK: + quoted ^= 1; + c = '"'; + break; + case '=': + if (subtype == 0) + break; + str = vstype[subtype & VSTYPE]; + if (subtype & VSNUL) + c = ':'; + else + c = *str++; + if (c != '}') + quoted <<= 1; + break; + case '\'': + case '\\': + case '"': + case '$': + /* These can only happen inside quotes */ + cc[0] = c; + str = cc; + c = '\\'; + break; + default: + break; + } + USTPUTC(c, nextc); + if (!str) + continue; +dostr: + while ((c = *str++)) { + USTPUTC(c, nextc); + } + } + if (quoted & 1) { + USTPUTC('"', nextc); + } + *nextc = 0; + cmdnextc = nextc; +} + + +static void +showpipe(struct job *jp, FILE *out) +{ + struct procstat *sp; + struct procstat *spend; + + spend = jp->ps + jp->nprocs; + for (sp = jp->ps + 1; sp < spend; sp++) + fprintf(out, " | %s", sp->cmd); + outcslow('\n', out); + flushall(); +} + +static void +xtcsetpgrp(int fd, pid_t pgrp) +{ + if (tcsetpgrp(fd, pgrp)) + error("Cannot set tty process group (%m)"); +} +#endif /* JOBS */ + +static int +getstatus(struct job *job) { + int status; + int retval; + + status = job->ps[job->nprocs - 1].status; + retval = WEXITSTATUS(status); + if (!WIFEXITED(status)) { +#if JOBS + retval = WSTOPSIG(status); + if (!WIFSTOPPED(status)) +#endif + { + /* XXX: limits number of signals */ + retval = WTERMSIG(status); +#if JOBS + if (retval == SIGINT) + job->sigint = 1; +#endif + } + retval += 128; + } + TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", + jobno(job), job->nprocs, status, retval)); + return retval; +} + +#ifdef CONFIG_ASH_MAIL +/* $NetBSD: mail.c,v 1.15 2002/11/24 22:35:40 christos Exp $ */ + +/* + * Routines to check for mail. (Perhaps make part of main.c?) + */ + +#define MAXMBOXES 10 + +/* times of mailboxes */ +static time_t mailtime[MAXMBOXES]; +/* Set if MAIL or MAILPATH is changed. */ +static int mail_var_path_changed; + + + +/* + * Print appropriate message(s) if mail has arrived. + * If mail_var_path_changed is set, + * then the value of MAIL has mail_var_path_changed, + * so we just update the values. + */ + +static void +chkmail(void) +{ + const char *mpath; + char *p; + char *q; + time_t *mtp; + struct stackmark smark; + struct stat statb; + + setstackmark(&smark); + mpath = mpathset() ? mpathval() : mailval(); + for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { + p = padvance(&mpath, nullstr); + if (p == NULL) + break; + if (*p == '\0') + continue; + for (q = p ; *q ; q++); +#ifdef DEBUG + if (q[-1] != '/') + abort(); +#endif + q[-1] = '\0'; /* delete trailing '/' */ + if (stat(p, &statb) < 0) { + *mtp = 0; + continue; + } + if (!mail_var_path_changed && statb.st_mtime != *mtp) { + fprintf( + stderr, snlfmt, + pathopt ? pathopt : "you have mail" + ); + } + *mtp = statb.st_mtime; + } + mail_var_path_changed = 0; + popstackmark(&smark); +} + + +static void +changemail(const char *val) +{ + mail_var_path_changed++; +} + +#endif /* CONFIG_ASH_MAIL */ + +/* $NetBSD: main.c,v 1.46 2002/12/11 19:12:18 christos Exp $ */ + + +#if PROFILE +static short profile_buf[16384]; +extern int etext(); +#endif + +static int isloginsh; + +static void read_profile(const char *); + +/* + * Main routine. We initialize things, parse the arguments, execute + * profiles if we're a login shell, and then call cmdloop to execute + * commands. The setjmp call sets up the location to jump to when an + * exception occurs. When an exception occurs the variable "state" + * is used to figure out how far we had gotten. + */ + +int +ash_main(int argc, char **argv) +{ + char *shinit; + volatile int state; + struct jmploc jmploc; + struct stackmark smark; + +#ifdef __GLIBC__ + dash_errno = __errno_location(); +#endif + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif + state = 0; + if (setjmp(jmploc.loc)) { + int status; + int e; + + reset(); + + e = exception; + switch (exception) { + case EXEXEC: + status = exerrno; + break; + + case EXERROR: + status = 2; + break; + + default: + status = exitstatus; + break; + } + exitstatus = status; + + if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell) + exitshell(); + + if (e == EXINT) { + outcslow('\n', stderr); + } + popstackmark(&smark); + FORCEINTON; /* enable interrupts */ + if (state == 1) + goto state1; + else if (state == 2) + goto state2; + else if (state == 3) + goto state3; + else + goto state4; + } + handler = &jmploc; +#ifdef DEBUG + opentrace(); + trputs("Shell args: "); trargs(argv); +#endif + rootpid = getpid(); + +#ifdef CONFIG_ASH_RANDOM_SUPPORT + rseed = rootpid + ((time_t)time((time_t *)0)); +#endif + rootshell = 1; + init(); + setstackmark(&smark); + procargs(argc, argv); +#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY + if ( iflag ) { + const char *hp = lookupvar("HISTFILE"); + + if(hp == NULL ) { + hp = lookupvar("HOME"); + if(hp != NULL) { + char *defhp = concat_path_file(hp, ".ash_history"); + setvar("HISTFILE", defhp, 0); + free(defhp); + } + } + } +#endif + if (argv[0] && argv[0][0] == '-') + isloginsh = 1; + if (isloginsh) { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + read_profile(".profile"); + } +state2: + state = 3; + if ( +#ifndef linux + getuid() == geteuid() && getgid() == getegid() && +#endif + iflag + ) { + if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { + read_profile(shinit); + } + } +state3: + state = 4; + if (minusc) + evalstring(minusc); + + if (sflag || minusc == NULL) { +#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY + if ( iflag ) { + const char *hp = lookupvar("HISTFILE"); + + if(hp != NULL ) + load_history ( hp ); + } +#endif +state4: /* XXX ??? - why isn't this before the "if" statement */ + cmdloop(1); + } +#if PROFILE + monitor(0); +#endif +#if GPROF + { + extern void _mcleanup(void); + _mcleanup(); + } +#endif + exitshell(); + /* NOTREACHED */ +} + + +/* + * Read and execute commands. "Top" is nonzero for the top level command + * loop; it turns on prompting if the shell is interactive. + */ + +static void +cmdloop(int top) +{ + union node *n; + struct stackmark smark; + int inter; + int numeof = 0; + + TRACE(("cmdloop(%d) called\n", top)); + for (;;) { + setstackmark(&smark); + if (pendingsigs) + dotrap(); +#if JOBS + if (jobctl) + showjobs(stderr, SHOW_CHANGED); +#endif + inter = 0; + if (iflag && top) { + inter++; +#ifdef CONFIG_ASH_MAIL + chkmail(); +#endif + } + n = parsecmd(inter); + /* showtree(n); DEBUG */ + if (n == NEOF) { + if (!top || numeof >= 50) + break; + if (!stoppedjobs()) { + if (!Iflag) + break; + out2str("\nUse \"exit\" to leave shell.\n"); + } + numeof++; + } else if (n != NULL && nflag == 0) { + job_warning = (job_warning == 2) ? 1 : 0; + numeof = 0; + evaltree(n, 0); + } + popstackmark(&smark); + if (evalskip) { + evalskip = 0; + break; + } + } +} + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +static void +read_profile(const char *name) +{ + int fd; + int xflag_set = 0; + int vflag_set = 0; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + INTON; + if (fd < 0) + return; + /* -q turns off -x and -v just when executing init files */ + if (qflag) { + if (xflag) + xflag = 0, xflag_set = 1; + if (vflag) + vflag = 0, vflag_set = 1; + } + cmdloop(0); + if (qflag) { + if (xflag_set) + xflag = 1; + if (vflag_set) + vflag = 1; + } + popfile(); +} + + +/* + * Read a file containing shell functions. + */ + +static void +readcmdfile(char *name) +{ + int fd; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + else + error("Can't open %s", name); + INTON; + cmdloop(0); + popfile(); +} + + +/* + * Take commands from a file. To be compatible we should do a path + * search for the file, which is necessary to find sub-commands. + */ + +static inline char * +find_dot_file(char *name) +{ + char *fullname; + const char *path = pathval(); + struct stat statb; + + /* don't try this for absolute or relative paths */ + if (strchr(name, '/')) + return name; + + while ((fullname = padvance(&path, name)) != NULL) { + if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { + /* + * Don't bother freeing here, since it will + * be freed by the caller. + */ + return fullname; + } + stunalloc(fullname); + } + + /* not found in the PATH */ + error(not_found_msg, name); + /* NOTREACHED */ +} + +static int dotcmd(int argc, char **argv) +{ + struct strlist *sp; + volatile struct shparam saveparam; + + exitstatus = 0; + + for (sp = cmdenviron; sp; sp = sp->next) + setvareq(bb_xstrdup(sp->text), VSTRFIXED | VTEXTFIXED); + + if (argc >= 2) { /* That's what SVR2 does */ + char *fullname; + struct stackmark smark; + + setstackmark(&smark); + fullname = find_dot_file(argv[1]); + + if (argc > 2) { + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.nparam = argc - 2; + shellparam.p = argv + 2; + }; + + setinputfile(fullname, 1); + commandname = fullname; + cmdloop(0); + popfile(); + + if (argc > 2) { + freeparam(&shellparam); + shellparam = saveparam; + }; + + popstackmark(&smark); + } + return exitstatus; +} + + +static int +exitcmd(int argc, char **argv) +{ + if (stoppedjobs()) + return 0; + if (argc > 1) + exitstatus = number(argv[1]); + exraise(EXEXIT); + /* NOTREACHED */ +} + +/* $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $ */ + +/* + * Same for malloc, realloc, but returns an error when out of space. + */ + +static pointer +ckrealloc(pointer p, size_t nbytes) +{ + p = realloc(p, nbytes); + if (p == NULL) + error(bb_msg_memory_exhausted); + return p; +} + +static pointer +ckmalloc(size_t nbytes) +{ + return ckrealloc(NULL, nbytes); +} + +/* + * Make a copy of a string in safe storage. + */ + +static char * +savestr(const char *s) +{ + char *p = strdup(s); + if (!p) + error(bb_msg_memory_exhausted); + return p; +} + + +/* + * Parse trees for commands are allocated in lifo order, so we use a stack + * to make this more efficient, and also to avoid all sorts of exception + * handling code to handle interrupts in the middle of a parse. + * + * The size 504 was chosen because the Ultrix malloc handles that size + * well. + */ + + +static pointer +stalloc(size_t nbytes) +{ + char *p; + size_t aligned; + + aligned = SHELL_ALIGN(nbytes); + if (aligned > stacknleft) { + size_t len; + size_t blocksize; + struct stack_block *sp; + + blocksize = aligned; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + len = sizeof(struct stack_block) - MINSIZE + blocksize; + if (len < blocksize) + error(bb_msg_memory_exhausted); + INTOFF; + sp = ckmalloc(len); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + sstrend = stacknxt + blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += aligned; + stacknleft -= aligned; + return p; +} + + +void +stunalloc(pointer p) +{ +#ifdef DEBUG + if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) { + write(2, "stunalloc\n", 10); + abort(); + } +#endif + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + +void +setstackmark(struct stackmark *mark) +{ + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; + mark->marknext = markp; + markp = mark; +} + + +void +popstackmark(struct stackmark *mark) +{ + struct stack_block *sp; + + INTOFF; + markp = mark->marknext; + while (stackp != mark->stackp) { + sp = stackp; + stackp = sp->prev; + ckfree(sp); + } + stacknxt = mark->stacknxt; + stacknleft = mark->stacknleft; + sstrend = mark->stacknxt + mark->stacknleft; + INTON; +} + + +/* + * When the parser reads in a string, it wants to stick the string on the + * stack and only adjust the stack pointer when it knows how big the + * string is. Stackblock (defined in stack.h) returns a pointer to a block + * of space on top of the stack and stackblocklen returns the length of + * this block. Growstackblock will grow this space by at least one byte, + * possibly moving it (like realloc). Grabstackblock actually allocates the + * part of the block that has been used. + */ + +void +growstackblock(void) +{ + size_t newlen; + + newlen = stacknleft * 2; + if (newlen < stacknleft) + error(bb_msg_memory_exhausted); + if (newlen < 128) + newlen += 128; + + if (stacknxt == stackp->space && stackp != &stackbase) { + struct stack_block *oldstackp; + struct stackmark *xmark; + struct stack_block *sp; + struct stack_block *prevstackp; + size_t grosslen; + + INTOFF; + oldstackp = stackp; + sp = stackp; + prevstackp = sp->prev; + grosslen = newlen + sizeof(struct stack_block) - MINSIZE; + sp = ckrealloc((pointer)sp, grosslen); + sp->prev = prevstackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + sstrend = sp->space + newlen; + + /* + * Stack marks pointing to the start of the old block + * must be relocated to point to the new block + */ + xmark = markp; + while (xmark != NULL && xmark->stackp == oldstackp) { + xmark->stackp = stackp; + xmark->stacknxt = stacknxt; + xmark->stacknleft = stacknleft; + xmark = xmark->marknext; + } + INTON; + } else { + char *oldspace = stacknxt; + int oldlen = stacknleft; + char *p = stalloc(newlen); + + /* free the space we just allocated */ + stacknxt = memcpy(p, oldspace, oldlen); + stacknleft += newlen; + } +} + +static inline void +grabstackblock(size_t len) +{ + len = SHELL_ALIGN(len); + stacknxt += len; + stacknleft -= len; +} + +/* + * The following routines are somewhat easier to use than the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + +void * +growstackstr(void) +{ + size_t len = stackblocksize(); + if (herefd >= 0 && len >= 1024) { + bb_full_write(herefd, stackblock(), len); + return stackblock(); + } + growstackblock(); + return stackblock() + len; +} + +/* + * Called from CHECKSTRSPACE. + */ + +char * +makestrspace(size_t newlen, char *p) +{ + size_t len = p - stacknxt; + size_t size = stackblocksize(); + + for (;;) { + size_t nleft; + + size = stackblocksize(); + nleft = size - len; + if (nleft >= newlen) + break; + growstackblock(); + } + return stackblock() + len; +} + +char * +stnputs(const char *s, size_t n, char *p) +{ + p = makestrspace(n, p); + p = mempcpy(p, s, n); + return p; +} + +char * +stputs(const char *s, char *p) +{ + return stnputs(s, strlen(s), p); +} + +/* $NetBSD: mystring.c,v 1.15 2002/11/24 22:35:42 christos Exp $ */ + +/* + * String functions. + * + * number(s) Convert a string of digits to an integer. + * is_number(s) Return true if s is a string of digits. + */ + +/* + * prefix -- see if pfx is a prefix of string. + */ + +char * +prefix(const char *string, const char *pfx) +{ + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return (char *) string; +} + + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +int +number(const char *s) +{ + + if (! is_number(s)) + error(illnum, s); + return atoi(s); +} + + +/* + * Check for a valid number. This should be elsewhere. + */ + +int +is_number(const char *p) +{ + do { + if (! is_digit(*p)) + return 0; + } while (*++p != '\0'); + return 1; +} + + +/* + * Produce a possibly single quoted string suitable as input to the shell. + * The return string is allocated on the stack. + */ + +char * +single_quote(const char *s) { + char *p; + + STARTSTACKSTR(p); + + do { + char *q; + size_t len; + + len = strchrnul(s, '\'') - s; + + q = p = makestrspace(len + 3, p); + + *q++ = '\''; + q = mempcpy(q, s, len); + *q++ = '\''; + s += len; + + STADJUST(q - p, p); + + len = strspn(s, "'"); + if (!len) + break; + + q = p = makestrspace(len + 3, p); + + *q++ = '"'; + q = mempcpy(q, s, len); + *q++ = '"'; + s += len; + + STADJUST(q - p, p); + } while (*s); + + USTPUTC(0, p); + + return stackblock(); +} + +/* + * Like strdup but works with the ash stack. + */ + +char * +sstrdup(const char *p) +{ + size_t len = strlen(p) + 1; + return memcpy(stalloc(len), p, len); +} + + +static void +calcsize(union node *n) +{ + if (n == NULL) + return; + funcblocksize += nodesize[n->type]; + switch (n->type) { + case NCMD: + calcsize(n->ncmd.redirect); + calcsize(n->ncmd.args); + calcsize(n->ncmd.assign); + break; + case NPIPE: + sizenodelist(n->npipe.cmdlist); + break; + case NREDIR: + case NBACKGND: + case NSUBSHELL: + calcsize(n->nredir.redirect); + calcsize(n->nredir.n); + break; + case NAND: + case NOR: + case NSEMI: + case NWHILE: + case NUNTIL: + calcsize(n->nbinary.ch2); + calcsize(n->nbinary.ch1); + break; + case NIF: + calcsize(n->nif.elsepart); + calcsize(n->nif.ifpart); + calcsize(n->nif.test); + break; + case NFOR: + funcstringsize += strlen(n->nfor.var) + 1; + calcsize(n->nfor.body); + calcsize(n->nfor.args); + break; + case NCASE: + calcsize(n->ncase.cases); + calcsize(n->ncase.expr); + break; + case NCLIST: + calcsize(n->nclist.body); + calcsize(n->nclist.pattern); + calcsize(n->nclist.next); + break; + case NDEFUN: + case NARG: + sizenodelist(n->narg.backquote); + funcstringsize += strlen(n->narg.text) + 1; + calcsize(n->narg.next); + break; + case NTO: + case NCLOBBER: + case NFROM: + case NFROMTO: + case NAPPEND: + calcsize(n->nfile.fname); + calcsize(n->nfile.next); + break; + case NTOFD: + case NFROMFD: + calcsize(n->ndup.vname); + calcsize(n->ndup.next); + break; + case NHERE: + case NXHERE: + calcsize(n->nhere.doc); + calcsize(n->nhere.next); + break; + case NNOT: + calcsize(n->nnot.com); + break; + }; +} + + +static void +sizenodelist(struct nodelist *lp) +{ + while (lp) { + funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + +static union node * +copynode(union node *n) +{ + union node *new; + + if (n == NULL) + return NULL; + new = funcblock; + funcblock = (char *) funcblock + nodesize[n->type]; + switch (n->type) { + case NCMD: + new->ncmd.redirect = copynode(n->ncmd.redirect); + new->ncmd.args = copynode(n->ncmd.args); + new->ncmd.assign = copynode(n->ncmd.assign); + break; + case NPIPE: + new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); + new->npipe.backgnd = n->npipe.backgnd; + break; + case NREDIR: + case NBACKGND: + case NSUBSHELL: + new->nredir.redirect = copynode(n->nredir.redirect); + new->nredir.n = copynode(n->nredir.n); + break; + case NAND: + case NOR: + case NSEMI: + case NWHILE: + case NUNTIL: + new->nbinary.ch2 = copynode(n->nbinary.ch2); + new->nbinary.ch1 = copynode(n->nbinary.ch1); + break; + case NIF: + new->nif.elsepart = copynode(n->nif.elsepart); + new->nif.ifpart = copynode(n->nif.ifpart); + new->nif.test = copynode(n->nif.test); + break; + case NFOR: + new->nfor.var = nodesavestr(n->nfor.var); + new->nfor.body = copynode(n->nfor.body); + new->nfor.args = copynode(n->nfor.args); + break; + case NCASE: + new->ncase.cases = copynode(n->ncase.cases); + new->ncase.expr = copynode(n->ncase.expr); + break; + case NCLIST: + new->nclist.body = copynode(n->nclist.body); + new->nclist.pattern = copynode(n->nclist.pattern); + new->nclist.next = copynode(n->nclist.next); + break; + case NDEFUN: + case NARG: + new->narg.backquote = copynodelist(n->narg.backquote); + new->narg.text = nodesavestr(n->narg.text); + new->narg.next = copynode(n->narg.next); + break; + case NTO: + case NCLOBBER: + case NFROM: + case NFROMTO: + case NAPPEND: + new->nfile.fname = copynode(n->nfile.fname); + new->nfile.fd = n->nfile.fd; + new->nfile.next = copynode(n->nfile.next); + break; + case NTOFD: + case NFROMFD: + new->ndup.vname = copynode(n->ndup.vname); + new->ndup.dupfd = n->ndup.dupfd; + new->ndup.fd = n->ndup.fd; + new->ndup.next = copynode(n->ndup.next); + break; + case NHERE: + case NXHERE: + new->nhere.doc = copynode(n->nhere.doc); + new->nhere.fd = n->nhere.fd; + new->nhere.next = copynode(n->nhere.next); + break; + case NNOT: + new->nnot.com = copynode(n->nnot.com); + break; + }; + new->type = n->type; + return new; +} + + +static struct nodelist * +copynodelist(struct nodelist *lp) +{ + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock = (char *) funcblock + + SHELL_ALIGN(sizeof(struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + +static char * +nodesavestr(char *s) +{ + char *rtn = funcstring; + + funcstring = stpcpy(funcstring, s) + 1; + return rtn; +} + + +/* + * Free a parse tree. + */ + +static void +freefunc(struct funcnode *f) +{ + if (f && --f->count < 0) + ckfree(f); +} + + +static void options(int); +static void setoption(int, int); + + +/* + * Process the shell command line arguments. + */ + +void +procargs(int argc, char **argv) +{ + int i; + const char *xminusc; + char **xargv; + + xargv = argv; + arg0 = xargv[0]; + if (argc > 0) + xargv++; + for (i = 0; i < NOPTS; i++) + optlist[i] = 2; + argptr = xargv; + options(1); + xargv = argptr; + xminusc = minusc; + if (*xargv == NULL) { + if (xminusc) + error("-c requires an argument"); + sflag = 1; + } + if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) + iflag = 1; + if (mflag == 2) + mflag = iflag; + for (i = 0; i < NOPTS; i++) + if (optlist[i] == 2) + optlist[i] = 0; +#if DEBUG == 2 + debug = 1; +#endif + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (xminusc) { + minusc = *xargv++; + if (*xargv) + goto setarg0; + } else if (!sflag) { + setinputfile(*xargv, 0); +setarg0: + arg0 = *xargv++; + commandname = arg0; + } + + shellparam.p = xargv; +#ifdef CONFIG_ASH_GETOPTS + shellparam.optind = 1; + shellparam.optoff = -1; +#endif + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*xargv) { + shellparam.nparam++; + xargv++; + } + optschanged(); +} + + +void +optschanged(void) +{ +#ifdef DEBUG + opentrace(); +#endif + setinteractive(iflag); + setjobctl(mflag); +} + +static inline void +minus_o(char *name, int val) +{ + int i; + + if (name == NULL) { + out1str("Current option settings\n"); + for (i = 0; i < NOPTS; i++) + out1fmt("%-16s%s\n", optnames(i), + optlist[i] ? "on" : "off"); + } else { + for (i = 0; i < NOPTS; i++) + if (equal(name, optnames(i))) { + optlist[i] = val; + return; + } + error("Illegal option -o %s", name); + } +} + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +static void +options(int cmdline) +{ + char *p; + int val; + int c; + + if (cmdline) + minusc = NULL; + while ((p = *argptr) != NULL) { + argptr++; + if ((c = *p++) == '-') { + val = 1; + if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { + if (!cmdline) { + /* "-" means turn off -x and -v */ + if (p[0] == '\0') + xflag = vflag = 0; + /* "--" means reset params */ + else if (*argptr == NULL) + setparam(argptr); + } + break; /* "-" or "--" terminates options */ + } + } else if (c == '+') { + val = 0; + } else { + argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + minusc = p; /* command is after shell args*/ + } else if (c == 'o') { + minus_o(*argptr, val); + if (*argptr) + argptr++; + } else if (cmdline && (c == '-')) { // long options + if (strcmp(p, "login") == 0) + isloginsh = 1; + break; + } else { + setoption(c, val); + } + } + } +} + + +static void +setoption(int flag, int val) +{ + int i; + + for (i = 0; i < NOPTS; i++) + if (optletters(i) == flag) { + optlist[i] = val; + return; + } + error("Illegal option -%c", flag); + /* NOTREACHED */ +} + + + +/* + * Set the shell parameters. + */ + +void +setparam(char **argv) +{ + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; +#ifdef CONFIG_ASH_GETOPTS + shellparam.optind = 1; + shellparam.optoff = -1; +#endif +} + + +/* + * Free the list of positional parameters. + */ + +void +freeparam(volatile struct shparam *param) +{ + char **ap; + + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(*ap); + ckfree(param->p); + } +} + + + +/* + * The shift builtin command. + */ + +int +shiftcmd(int argc, char **argv) +{ + int n; + char **ap1, **ap2; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > shellparam.nparam) + error("can't shift that many"); + INTOFF; + shellparam.nparam -= n; + for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { + if (shellparam.malloc) + ckfree(*ap1); + } + ap2 = shellparam.p; + while ((*ap2++ = *ap1++) != NULL); +#ifdef CONFIG_ASH_GETOPTS + shellparam.optind = 1; + shellparam.optoff = -1; +#endif + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +int +setcmd(int argc, char **argv) +{ + if (argc == 1) + return showvars(nullstr, 0, VUNSET); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +#ifdef CONFIG_ASH_GETOPTS +static void +getoptsreset(value) + const char *value; +{ + shellparam.optind = number(value); + shellparam.optoff = -1; +} +#endif + +#ifdef CONFIG_LOCALE_SUPPORT +static void change_lc_all(const char *value) +{ + if (value != 0 && *value != 0) + setlocale(LC_ALL, value); +} + +static void change_lc_ctype(const char *value) +{ + if (value != 0 && *value != 0) + setlocale(LC_CTYPE, value); +} + +#endif + +#ifdef CONFIG_ASH_RANDOM_SUPPORT +/* Roughly copied from bash.. */ +static void change_random(const char *value) +{ + if(value == NULL) { + /* "get", generate */ + char buf[16]; + + rseed = rseed * 1103515245 + 12345; + sprintf(buf, "%d", (unsigned int)((rseed & 32767))); + /* set without recursion */ + setvar(vrandom.text, buf, VNOFUNC); + vrandom.flags &= ~VNOFUNC; + } else { + /* set/reset */ + rseed = strtoul(value, (char **)NULL, 10); + } +} +#endif + + +#ifdef CONFIG_ASH_GETOPTS +static int +getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff) +{ + char *p, *q; + char c = '?'; + int done = 0; + int err = 0; + char s[12]; + char **optnext; + + if(*param_optind < 1) + return 1; + optnext = optfirst + *param_optind - 1; + + if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff) + p = NULL; + else + p = optnext[-1] + *optoff; + if (p == NULL || *p == '\0') { + /* Current word is done, advance */ + p = *optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + p = NULL; + done = 1; + goto out; + } + optnext++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + + c = *p++; + for (q = optstr; *q != c; ) { + if (*q == '\0') { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe("OPTARG", s, 0); + } else { + fprintf(stderr, "Illegal option -%c\n", c); + (void) unsetvar("OPTARG"); + } + c = '?'; + goto out; + } + if (*++q == ':') + q++; + } + + if (*++q == ':') { + if (*p == '\0' && (p = *optnext) == NULL) { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe("OPTARG", s, 0); + c = ':'; + } else { + fprintf(stderr, "No arg for -%c option\n", c); + (void) unsetvar("OPTARG"); + c = '?'; + } + goto out; + } + + if (p == *optnext) + optnext++; + err |= setvarsafe("OPTARG", p, 0); + p = NULL; + } else + err |= setvarsafe("OPTARG", nullstr, 0); + +out: + *optoff = p ? p - *(optnext - 1) : -1; + *param_optind = optnext - optfirst + 1; + fmtstr(s, sizeof(s), "%d", *param_optind); + err |= setvarsafe("OPTIND", s, VNOFUNC); + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(optvar, s, 0); + if (err) { + *param_optind = 1; + *optoff = -1; + flushall(); + exraise(EXERROR); + } + return done; +} + +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +int +getoptscmd(int argc, char **argv) +{ + char **optbase; + + if (argc < 3) + error("Usage: getopts optstring var [arg]"); + else if (argc == 3) { + optbase = shellparam.p; + if (shellparam.optind > shellparam.nparam + 1) { + shellparam.optind = 1; + shellparam.optoff = -1; + } + } + else { + optbase = &argv[3]; + if (shellparam.optind > argc - 2) { + shellparam.optind = 1; + shellparam.optoff = -1; + } + } + + return getopts(argv[1], argv[2], optbase, &shellparam.optind, + &shellparam.optoff); +} +#endif /* CONFIG_ASH_GETOPTS */ + +/* + * XXX - should get rid of. have all builtins use getopt(3). the + * library getopt must have the BSD extension static variable "optreset" + * otherwise it can't be used within the shell safely. + * + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It return the character, or '\0' on + * end of input. + */ + +static int +nextopt(const char *optstring) +{ + char *p; + const char *q; + char c; + + if ((p = optptr) == NULL || *p == '\0') { + p = *argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + error("Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *argptr++) == NULL) + error("No arg for -%c option", c); + optionarg = p; + p = NULL; + } + optptr = p; + return c; +} + + +/* $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */ + +void +outstr(const char *p, FILE *file) +{ + INTOFF; + fputs(p, file); + INTON; +} + +void +flushall(void) +{ + INTOFF; + fflush(stdout); + fflush(stderr); + INTON; +} + +void +flusherr(void) +{ + INTOFF; + fflush(stderr); + INTON; +} + +static void +outcslow(int c, FILE *dest) +{ + INTOFF; + putc(c, dest); + fflush(dest); + INTON; +} + + +static int +out1fmt(const char *fmt, ...) +{ + va_list ap; + int r; + + INTOFF; + va_start(ap, fmt); + r = vprintf(fmt, ap); + va_end(ap); + INTON; + return r; +} + + +int +fmtstr(char *outbuf, size_t length, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + INTOFF; + ret = vsnprintf(outbuf, length, fmt, ap); + va_end(ap); + INTON; + return ret; +} + + + +/* $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $ */ + + +/* + * Shell command parser. + */ + +#define EOFMARKLEN 79 + + +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + int striptabs; /* if set, strip leading tabs */ +}; + + + +static struct heredoc *heredoclist; /* list of here documents to read */ + + +static union node *list(int); +static union node *andor(void); +static union node *pipeline(void); +static union node *command(void); +static union node *simplecmd(void); +static union node *makename(void); +static void parsefname(void); +static void parseheredoc(void); +static char peektoken(void); +static int readtoken(void); +static int xxreadtoken(void); +static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs); +static int noexpand(char *); +static void synexpect(int) __attribute__((__noreturn__)); +static void synerror(const char *) __attribute__((__noreturn__)); +static void setprompt(int); + + + +static inline int +isassignment(const char *p) +{ + const char *q = endofname(p); + if (p == q) + return 0; + return *q == '='; +} + + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +union node * +parsecmd(int interact) +{ + int t; + + tokpushback = 0; + doprompt = interact; + if (doprompt) + setprompt(doprompt); + needprompt = 0; + t = readtoken(); + if (t == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +static union node * +list(int nlflag) +{ + union node *n1, *n2, *n3; + int tok; + + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (nlflag == 2 && peektoken()) + return NULL; + n1 = NULL; + for (;;) { + n2 = andor(); + tok = readtoken(); + if (tok == TBACKGND) { + if (n2->type == NPIPE) { + n2->npipe.backgnd = 1; + } else { + if (n2->type != NREDIR) { + n3 = stalloc(sizeof(struct nredir)); + n3->nredir.n = n2; + n3->nredir.redirect = NULL; + n2 = n3; + } + n2->type = NBACKGND; + } + } + if (n1 == NULL) { + n1 = n2; + } + else { + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = NSEMI; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } + switch (tok) { + case TBACKGND: + case TSEMI: + tok = readtoken(); + /* fall through */ + case TNL: + if (tok == TNL) { + parseheredoc(); + if (nlflag == 1) + return n1; + } else { + tokpushback++; + } + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (peektoken()) + return n1; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return n1; + default: + if (nlflag == 1) + synexpect(-1); + tokpushback++; + return n1; + } + } +} + + + +static union node * +andor(void) +{ + union node *n1, *n2, *n3; + int t; + + n1 = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n1; + } + checkkwd = CHKNL | CHKKWD | CHKALIAS; + n2 = pipeline(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = t; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } +} + + + +static union node * +pipeline(void) +{ + union node *n1, *n2, *pipenode; + struct nodelist *lp, *prev; + int negate; + + negate = 0; + TRACE(("pipeline: entered\n")); + if (readtoken() == TNOT) { + negate = !negate; + checkkwd = CHKKWD | CHKALIAS; + } else + tokpushback++; + n1 = command(); + if (readtoken() == TPIPE) { + pipenode = (union node *)stalloc(sizeof (struct npipe)); + pipenode->type = NPIPE; + pipenode->npipe.backgnd = 0; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + pipenode->npipe.cmdlist = lp; + lp->n = n1; + do { + prev = lp; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + checkkwd = CHKNL | CHKKWD | CHKALIAS; + lp->n = command(); + prev->next = lp; + } while (readtoken() == TPIPE); + lp->next = NULL; + n1 = pipenode; + } + tokpushback++; + if (negate) { + n2 = (union node *)stalloc(sizeof (struct nnot)); + n2->type = NNOT; + n2->nnot.com = n1; + return n2; + } else + return n1; +} + + + +static union node * +command(void) +{ + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + union node **rpp2; + int t; + + redir = NULL; + rpp2 = &redir; + + switch (readtoken()) { + default: + synexpect(-1); + /* NOTREACHED */ + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + n1->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + n2->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + t = TFI; + break; + case TWHILE: + case TUNTIL: { + int got; + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.ch1 = list(0); + if ((got=readtoken()) != TDO) { +TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : "")); + synexpect(TDO); + } + n1->nbinary.ch2 = list(0); + t = TDONE; + break; + } + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.var = wordtext; + checkkwd = CHKKWD | CHKALIAS; + if (readtoken() == TIN) { + app = ≈ + while (readtoken() == TWORD) { + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + *app = n2; + app = &n2->narg.next; + } + *app = NULL; + n1->nfor.args = ap; + if (lasttoken != TNL && lasttoken != TSEMI) + synexpect(-1); + } else { + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = (char *)dolatstr; + n2->narg.backquote = NULL; + n2->narg.next = NULL; + n1->nfor.args = n2; + /* + * Newline or semicolon here is optional (but note + * that the original Bourne shell only allowed NL). + */ + if (lasttoken != TNL && lasttoken != TSEMI) + tokpushback++; + } + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (readtoken() != TDO) + synexpect(TDO); + n1->nfor.body = list(0); + t = TDONE; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + if (readtoken() != TWORD) + synexpect(TWORD); + n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + n2->narg.next = NULL; + do { + checkkwd = CHKKWD | CHKALIAS; + } while (readtoken() == TNL); + if (lasttoken != TIN) + synexpect(TIN); + cpp = &n1->ncase.cases; +next_case: + checkkwd = CHKNL | CHKKWD; + t = readtoken(); + while(t != TESAC) { + if (lasttoken == TLP) + readtoken(); + *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); + cp->type = NCLIST; + app = &cp->nclist.pattern; + for (;;) { + *app = ap = (union node *)stalloc(sizeof (struct narg)); + ap->type = NARG; + ap->narg.text = wordtext; + ap->narg.backquote = backquotelist; + if (readtoken() != TPIPE) + break; + app = &ap->narg.next; + readtoken(); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + synexpect(TRP); + cp->nclist.body = list(2); + + cpp = &cp->nclist.next; + + checkkwd = CHKNL | CHKKWD; + if ((t = readtoken()) != TESAC) { + if (t != TENDCASE) + synexpect(TENDCASE); + else + goto next_case; + } + } + *cpp = NULL; + goto redir; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + t = TRP; + break; + case TBEGIN: + n1 = list(0); + t = TEND; + break; + case TWORD: + case TREDIR: + tokpushback++; + return simplecmd(); + } + + if (readtoken() != t) + synexpect(t); + +redir: + /* Now check for redirection which may follow command */ + checkkwd = CHKKWD | CHKALIAS; + rpp = rpp2; + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (n1->type != NSUBSHELL) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + + return n1; +} + + +static union node * +simplecmd(void) { + union node *args, **app; + union node *n = NULL; + union node *vars, **vpp; + union node **rpp, *redir; + int savecheckkwd; + + args = NULL; + app = &args; + vars = NULL; + vpp = &vars; + redir = NULL; + rpp = &redir; + + savecheckkwd = CHKALIAS; + for (;;) { + checkkwd = savecheckkwd; + switch (readtoken()) { + case TWORD: + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + if (savecheckkwd && isassignment(wordtext)) { + *vpp = n; + vpp = &n->narg.next; + } else { + *app = n; + app = &n->narg.next; + savecheckkwd = 0; + } + break; + case TREDIR: + *rpp = n = redirnode; + rpp = &n->nfile.next; + parsefname(); /* read name of redirection file */ + break; + case TLP: + if ( + args && app == &args->narg.next && + !vars && !redir + ) { + struct builtincmd *bcmd; + const char *name; + + /* We have a function */ + if (readtoken() != TRP) + synexpect(TRP); + name = n->narg.text; + if ( + !goodname(name) || ( + (bcmd = find_builtin(name)) && + IS_BUILTIN_SPECIAL(bcmd) + ) + ) + synerror("Bad function name"); + n->type = NDEFUN; + checkkwd = CHKNL | CHKKWD | CHKALIAS; + n->narg.next = command(); + return n; + } + /* fall through */ + default: + tokpushback++; + goto out; + } + } +out: + *app = NULL; + *vpp = NULL; + *rpp = NULL; + n = (union node *)stalloc(sizeof (struct ncmd)); + n->type = NCMD; + n->ncmd.args = args; + n->ncmd.assign = vars; + n->ncmd.redirect = redir; + return n; +} + +static union node * +makename(void) +{ + union node *n; + + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + return n; +} + +void fixredir(union node *n, const char *text, int err) +{ + TRACE(("Fix redir %s %d\n", text, err)); + if (!err) + n->ndup.vname = NULL; + + if (is_digit(text[0]) && text[1] == '\0') + n->ndup.dupfd = digit_val(text[0]); + else if (text[0] == '-' && text[1] == '\0') + n->ndup.dupfd = -1; + else { + + if (err) + synerror("Bad fd number"); + else + n->ndup.vname = makename(); + } +} + + +static void +parsefname(void) +{ + union node *n = redirnode; + + if (readtoken() != TWORD) + synexpect(-1); + if (n->type == NHERE) { + struct heredoc *here = heredoc; + struct heredoc *p; + int i; + + if (quoteflag == 0) + n->type = NXHERE; + TRACE(("Here document %d\n", n->type)); + if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) + synerror("Illegal eof marker for << redirection"); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + fixredir(n, wordtext, 0); + } else { + n->nfile.fname = makename(); + } +} + + +/* + * Input any here documents. + */ + +static void +parseheredoc(void) +{ + struct heredoc *here; + union node *n; + + here = heredoclist; + heredoclist = 0; + + while (here) { + if (needprompt) { + setprompt(2); + needprompt = 0; + } + readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, + here->eofmark, here->striptabs); + n = (union node *)stalloc(sizeof (struct narg)); + n->narg.type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + here->here->nhere.doc = n; + here = here->next; + } +} + +static char peektoken(void) +{ + int t; + + t = readtoken(); + tokpushback++; + return tokname_array[t][0]; +} + +static int +readtoken(void) +{ + int t; +#ifdef DEBUG + int alreadyseen = tokpushback; +#endif + +#ifdef CONFIG_ASH_ALIAS +top: +#endif + + t = xxreadtoken(); + + /* + * eat newlines + */ + if (checkkwd & CHKNL) { + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } + + if (t != TWORD || quoteflag) { + goto out; + } + + /* + * check for keywords + */ + if (checkkwd & CHKKWD) { + const char *const *pp; + + if ((pp = findkwd(wordtext))) { + lasttoken = t = pp - tokname_array; + TRACE(("keyword %s recognized\n", tokname(t))); + goto out; + } + } + + if (checkkwd & CHKALIAS) { +#ifdef CONFIG_ASH_ALIAS + struct alias *ap; + if ((ap = lookupalias(wordtext, 1)) != NULL) { + if (*ap->val) { + pushstring(ap->val, ap); + } + goto top; + } +#endif + } +out: + checkkwd = 0; +#ifdef DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * In all cases, the variable startlinno is set to the number of the line + * on which the token starts. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define NEW_xxreadtoken +#ifdef NEW_xxreadtoken + +/* singles must be first! */ +static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 }; + +static const char xxreadtoken_tokens[] = { + TNL, TLP, TRP, /* only single occurrence allowed */ + TBACKGND, TPIPE, TSEMI, /* if single occurrence */ + TEOF, /* corresponds to trailing nul */ + TAND, TOR, TENDCASE, /* if double occurrence */ +}; + +#define xxreadtoken_doubles \ + (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars)) +#define xxreadtoken_singles \ + (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1) + +static int xxreadtoken() +{ + int c; + + if (tokpushback) { + tokpushback = 0; + return lasttoken; + } + if (needprompt) { + setprompt(2); + needprompt = 0; + } + startlinno = plinno; + for (;;) { /* until token or start of word found */ + c = pgetc_macro(); + + if ((c != ' ') && (c != '\t') +#ifdef CONFIG_ASH_ALIAS + && (c != PEOA) +#endif + ) { + if (c == '#') { + while ((c = pgetc()) != '\n' && c != PEOF); + pungetc(); + } else if (c == '\\') { + if (pgetc() != '\n') { + pungetc(); + goto READTOKEN1; + } + startlinno = ++plinno; + if (doprompt) + setprompt(2); + } else { + const char *p + = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; + + if (c != PEOF) { + if (c == '\n') { + plinno++; + needprompt = doprompt; + } + + p = strchr(xxreadtoken_chars, c); + if (p == NULL) { + READTOKEN1: + return readtoken1(c, BASESYNTAX, (char *) NULL, 0); + } + + if (p - xxreadtoken_chars >= xxreadtoken_singles) { + if (pgetc() == *p) { /* double occurrence? */ + p += xxreadtoken_doubles + 1; + } else { + pungetc(); + } + } + } + + return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars]; + } + } + } +} + + +#else +#define RETURN(token) return lasttoken = token + +static int +xxreadtoken(void) +{ + int c; + + if (tokpushback) { + tokpushback = 0; + return lasttoken; + } + if (needprompt) { + setprompt(2); + needprompt = 0; + } + startlinno = plinno; + for (;;) { /* until token or start of word found */ + c = pgetc_macro(); + switch (c) { + case ' ': case '\t': +#ifdef CONFIG_ASH_ALIAS + case PEOA: +#endif + continue; + case '#': + while ((c = pgetc()) != '\n' && c != PEOF); + pungetc(); + continue; + case '\\': + if (pgetc() == '\n') { + startlinno = ++plinno; + if (doprompt) + setprompt(2); + continue; + } + pungetc(); + goto breakloop; + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + if (pgetc() == ';') + RETURN(TENDCASE); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + default: + goto breakloop; + } + } +breakloop: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); +#undef RETURN +} +#endif /* NEW_xxreadtoken */ + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEARITH() {goto parsearith; parsearith_return:;} + +static int +readtoken1(int firstc, int syntax, char *eofmark, int striptabs) +{ + int c = firstc; + char *out; + int len; + char line[EOFMARKLEN + 1]; + struct nodelist *bqlist; + int quotef; + int dblquote; + int varnest; /* levels of variables expansion */ + int arinest; /* levels of arithmetic expansion */ + int parenlevel; /* levels of parens in arithmetic */ + int dqvarnest; /* levels of variables expansion within double quotes */ + int oldstyle; + int prevsyntax; /* syntax before arithmetic */ +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &out; + (void) "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(4, out); /* permit 4 calls to USTPUTC */ + switch(SIT(c, syntax)) { + case CNL: /* '\n' */ + if (syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) + setprompt(2); + c = pgetc(); + goto loop; /* continue outer loop */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if (eofmark == NULL || dblquote) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + case CBACK: /* backslash */ + c = pgetc2(); + if (c == PEOF) { + USTPUTC(CTLESC, out); + USTPUTC('\\', out); + pungetc(); + } else if (c == '\n') { + if (doprompt) + setprompt(2); + } else { + if ( + dblquote && + c != '\\' && c != '`' && + c != '$' && ( + c != '"' || + eofmark != NULL + ) + ) { + USTPUTC(CTLESC, out); + USTPUTC('\\', out); + } + if (SIT(c, SQSYNTAX) == CCTL) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + quotef++; + } + break; + case CSQUOTE: + syntax = SQSYNTAX; +quotemark: + if (eofmark == NULL) { + USTPUTC(CTLQUOTEMARK, out); + } + break; + case CDQUOTE: + syntax = DQSYNTAX; + dblquote = 1; + goto quotemark; + case CENDQUOTE: + if (eofmark != NULL && arinest == 0 && + varnest == 0) { + USTPUTC(c, out); + } else { + if (dqvarnest == 0) { + syntax = BASESYNTAX; + dblquote = 0; + } + quotef++; + goto quotemark; + } + break; + case CVAR: /* '$' */ + PARSESUB(); /* parse substitution */ + break; + case CENDVAR: /* '}' */ + if (varnest > 0) { + varnest--; + if (dqvarnest > 0) { + dqvarnest--; + } + USTPUTC(CTLENDVAR, out); + } else { + USTPUTC(c, out); + } + break; +#ifdef CONFIG_ASH_MATH_SUPPORT + case CLP: /* '(' in arithmetic */ + parenlevel++; + USTPUTC(c, out); + break; + case CRP: /* ')' in arithmetic */ + if (parenlevel > 0) { + USTPUTC(c, out); + --parenlevel; + } else { + if (pgetc() == ')') { + if (--arinest == 0) { + USTPUTC(CTLENDARI, out); + syntax = prevsyntax; + if (syntax == DQSYNTAX) + dblquote = 1; + else + dblquote = 0; + } else + USTPUTC(')', out); + } else { + /* + * unbalanced parens + * (don't 2nd guess - no error) + */ + pungetc(); + USTPUTC(')', out); + } + } + break; +#endif + case CBQUOTE: /* '`' */ + PARSEBACKQOLD(); + break; + case CENDFILE: + goto endword; /* exit outer loop */ + case CIGN: + break; + default: + if (varnest == 0) + goto endword; /* exit outer loop */ +#ifdef CONFIG_ASH_ALIAS + if (c != PEOA) +#endif + USTPUTC(c, out); + + } + c = pgetc_macro(); + } + } +endword: +#ifdef CONFIG_ASH_MATH_SUPPORT + if (syntax == ARISYNTAX) + synerror("Missing '))'"); +#endif + if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + synerror("Unterminated quoted string"); + if (varnest != 0) { + startlinno = plinno; + /* { */ + synerror("Missing '}'"); + } + USTPUTC('\0', out); + len = out - (char *)stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + PARSEREDIR(); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + return lasttoken = TWORD; +/* end of readtoken routine */ + + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + */ + +checkend: { + if (eofmark) { +#ifdef CONFIG_ASH_ALIAS + if (c == PEOA) { + c = pgetc2(); + } +#endif + if (striptabs) { + while (c == '\t') { + c = pgetc2(); + } + } + if (c == *eofmark) { + if (pfgets(line, sizeof line) != NULL) { + char *p, *q; + + p = line; + for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); + if (*p == '\n' && *q == '\0') { + c = PEOF; + plinno++; + needprompt = doprompt; + } else { + pushstring(line, NULL); + } + } + } + } + goto checkend_return; +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +parseredir: { + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc(); + if (c == '>') + np->type = NAPPEND; + else if (c == '|') + np->type = NCLOBBER; + else if (c == '&') + np->type = NTOFD; + else { + np->type = NTO; + pungetc(); + } + } else { /* c == '<' */ + np->nfile.fd = 0; + switch (c = pgetc()) { + case '<': + if (sizeof (struct nfile) != sizeof (struct nhere)) { + np = (union node *)stalloc(sizeof (struct nhere)); + np->nfile.fd = 0; + } + np->type = NHERE; + heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); + heredoc->here = np; + if ((c = pgetc()) == '-') { + heredoc->striptabs = 1; + } else { + heredoc->striptabs = 0; + pungetc(); + } + break; + + case '&': + np->type = NFROMFD; + break; + + case '>': + np->type = NFROMTO; + break; + + default: + np->type = NFROM; + pungetc(); + break; + } + } + if (fd != '\0') + np->nfile.fd = digit_val(fd); + redirnode = np; + goto parseredir_return; +} + + +/* + * Parse a substitution. At this point, we have read the dollar sign + * and nothing else. + */ + +parsesub: { + int subtype; + int typeloc; + int flags; + char *p; + static const char types[] = "}-+?="; + + c = pgetc(); + if ( + c <= PEOA_OR_PEOF || + (c != '(' && c != '{' && !is_name(c) && !is_special(c)) + ) { + USTPUTC('$', out); + pungetc(); + } else if (c == '(') { /* $(command) or $((arith)) */ + if (pgetc() == '(') { +#ifdef CONFIG_ASH_MATH_SUPPORT + PARSEARITH(); +#else + synerror("We unsupport $((arith))"); +#endif + } else { + pungetc(); + PARSEBACKQNEW(); + } + } else { + USTPUTC(CTLVAR, out); + typeloc = out - (char *)stackblock(); + USTPUTC(VSNORMAL, out); + subtype = VSNORMAL; + if (c == '{') { + c = pgetc(); + if (c == '#') { + if ((c = pgetc()) == '}') + c = '#'; + else + subtype = VSLENGTH; + } + else + subtype = 0; + } + if (c > PEOA_OR_PEOF && is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (c > PEOA_OR_PEOF && is_in_name(c)); + } else if (is_digit(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_digit(c)); + } + else if (is_special(c)) { + USTPUTC(c, out); + c = pgetc(); + } + else +badsub: synerror("Bad substitution"); + + STPUTC('=', out); + flags = 0; + if (subtype == 0) { + switch (c) { + case ':': + flags = VSNUL; + c = pgetc(); + /*FALLTHROUGH*/ + default: + p = strchr(types, c); + if (p == NULL) + goto badsub; + subtype = p - types + VSNORMAL; + break; + case '%': + case '#': + { + int cc = c; + subtype = c == '#' ? VSTRIMLEFT : + VSTRIMRIGHT; + c = pgetc(); + if (c == cc) + subtype++; + else + pungetc(); + break; + } + } + } else { + pungetc(); + } + if (dblquote || arinest) + flags |= VSQUOTE; + *((char *)stackblock() + typeloc) = subtype | flags; + if (subtype != VSNORMAL) { + varnest++; + if (dblquote || arinest) { + dqvarnest++; + } + } + } + goto parsesub_return; +} + + +/* + * Called to parse command substitutions. Newstyle is set if the command + * is enclosed inside $(...); nlpp is a pointer to the head of the linked + * list of commands (passed by reference), and savelen is the number of + * characters on the top of the stack which must be preserved. + */ + +parsebackq: { + struct nodelist **nlpp; + int savepbq; + union node *n; + char *volatile str; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + size_t savelen; + int saveprompt; +#ifdef __GNUC__ + (void) &saveprompt; +#endif + + savepbq = parsebackquote; + if (setjmp(jmploc.loc)) { + if (str) + ckfree(str); + parsebackquote = 0; + handler = savehandler; + longjmp(handler->loc, 1); + } + INTOFF; + str = NULL; + savelen = out - (char *)stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + memcpy(str, stackblock(), savelen); + } + savehandler = handler; + handler = &jmploc; + INTON; + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + char *pout; + int pc; + size_t psavelen; + char *pstr; + + + STARTSTACKSTR(pout); + for (;;) { + if (needprompt) { + setprompt(2); + needprompt = 0; + } + switch (pc = pgetc()) { + case '`': + goto done; + + case '\\': + if ((pc = pgetc()) == '\n') { + plinno++; + if (doprompt) + setprompt(2); + /* + * If eating a newline, avoid putting + * the newline into the new character + * stream (via the STPUTC after the + * switch). + */ + continue; + } + if (pc != '\\' && pc != '`' && pc != '$' + && (!dblquote || pc != '"')) + STPUTC('\\', pout); + if (pc > PEOA_OR_PEOF) { + break; + } + /* fall through */ + + case PEOF: +#ifdef CONFIG_ASH_ALIAS + case PEOA: +#endif + startlinno = plinno; + synerror("EOF in backquote substitution"); + + case '\n': + plinno++; + needprompt = doprompt; + break; + + default: + break; + } + STPUTC(pc, pout); + } +done: + STPUTC('\0', pout); + psavelen = pout - (char *)stackblock(); + if (psavelen > 0) { + pstr = grabstackstr(pout); + setinputstring(pstr); + } + } + nlpp = &bqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + parsebackquote = oldstyle; + + if (oldstyle) { + saveprompt = doprompt; + doprompt = 0; + } + + n = list(2); + + if (oldstyle) + doprompt = saveprompt; + else { + if (readtoken() != TRP) + synexpect(TRP); + } + + (*nlpp)->n = n; + if (oldstyle) { + /* + * Start reading from old file again, ignoring any pushed back + * tokens left from the backquote parsing + */ + popfile(); + tokpushback = 0; + } + while (stackblocksize() <= savelen) + growstackblock(); + STARTSTACKSTR(out); + if (str) { + memcpy(out, str, savelen); + STADJUST(savelen, out); + INTOFF; + ckfree(str); + str = NULL; + INTON; + } + parsebackquote = savepbq; + handler = savehandler; + if (arinest || dblquote) + USTPUTC(CTLBACKQ | CTLQUOTE, out); + else + USTPUTC(CTLBACKQ, out); + if (oldstyle) + goto parsebackq_oldreturn; + else + goto parsebackq_newreturn; +} + +#ifdef CONFIG_ASH_MATH_SUPPORT +/* + * Parse an arithmetic expansion (indicate start of one and set state) + */ +parsearith: { + + if (++arinest == 1) { + prevsyntax = syntax; + syntax = ARISYNTAX; + USTPUTC(CTLARI, out); + if (dblquote) + USTPUTC('"',out); + else + USTPUTC(' ',out); + } else { + /* + * we collapse embedded arithmetic expansion to + * parenthesis, which should be equivalent + */ + USTPUTC('(', out); + } + goto parsearith_return; +} +#endif + +} /* end of readtoken */ + + + +/* + * Returns true if the text contains nothing to expand (no dollar signs + * or backquotes). + */ + +static int +noexpand(char *text) +{ + char *p; + char c; + + p = text; + while ((c = *p++) != '\0') { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + p++; + else if (SIT(c, BASESYNTAX) == CCTL) + return 0; + } + return 1; +} + + +/* + * Return of a legal variable name (a letter or underscore followed by zero or + * more letters, underscores, and digits). + */ + +static char * +endofname(const char *name) +{ + char *p; + + p = (char *) name; + if (! is_name(*p)) + return p; + while (*++p) { + if (! is_in_name(*p)) + break; + } + return p; +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +static void synexpect(int token) +{ + char msg[64]; + int l; + + l = sprintf(msg, "%s unexpected", tokname(lasttoken)); + if (token >= 0) + sprintf(msg + l, " (expecting %s)", tokname(token)); + synerror(msg); + /* NOTREACHED */ +} + +static void +synerror(const char *msg) +{ + error("Syntax error: %s", msg); + /* NOTREACHED */ +} + + +/* + * called by editline -- any expansions to the prompt + * should be added here. + */ + +static void setprompt(int whichprompt) +{ + const char *prompt; + + switch (whichprompt) { + case 1: + prompt = ps1val(); + break; + case 2: + prompt = ps2val(); + break; + default: /* 0 */ + prompt = nullstr; + } + putprompt(prompt); +} + + +static const char *const *findkwd(const char *s) +{ + return bsearch(s, tokname_array + KWDOFFSET, + (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET, + sizeof(const char *), pstrcmp); +} + +/* $NetBSD: redir.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */ + +/* + * Code for dealing with input/output redirection. + */ + +#define EMPTY -2 /* marks an unused slot in redirtab */ +#ifndef PIPE_BUF +# define PIPESIZE 4096 /* amount of buffering in a pipe */ +#else +# define PIPESIZE PIPE_BUF +#endif + +/* + * Open a file in noclobber mode. + * The code was copied from bash. + */ +static inline int +noclobberopen(const char *fname) +{ + int r, fd; + struct stat finfo, finfo2; + + /* + * If the file exists and is a regular file, return an error + * immediately. + */ + r = stat(fname, &finfo); + if (r == 0 && S_ISREG(finfo.st_mode)) { + errno = EEXIST; + return -1; + } + + /* + * If the file was not present (r != 0), make sure we open it + * exclusively so that if it is created before we open it, our open + * will fail. Make sure that we do not truncate an existing file. + * Note that we don't turn on O_EXCL unless the stat failed -- if the + * file was not a regular file, we leave O_EXCL off. + */ + if (r != 0) + return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); + fd = open(fname, O_WRONLY|O_CREAT, 0666); + + /* If the open failed, return the file descriptor right away. */ + if (fd < 0) + return fd; + + /* + * OK, the open succeeded, but the file may have been changed from a + * non-regular file to a regular file between the stat and the open. + * We are assuming that the O_EXCL open handles the case where FILENAME + * did not exist and is symlinked to an existing file between the stat + * and open. + */ + + /* + * If we can open it and fstat the file descriptor, and neither check + * revealed that it was a regular file, and the file has not been + * replaced, return the file descriptor. + */ + if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) && + finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino) + return fd; + + /* The file has been replaced. badness. */ + close(fd); + errno = EEXIST; + return -1; +} + +/* + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. + */ + +static inline int +openhere(union node *redir) +{ + int pip[2]; + size_t len = 0; + + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + bb_full_write(pip[1], redir->nhere.doc->narg.text, len); + goto out; + } + } + if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + signal(SIGPIPE, SIG_DFL); + if (redir->type == NHERE) + bb_full_write(pip[1], redir->nhere.doc->narg.text, len); + else + expandhere(redir->nhere.doc, pip[1]); + _exit(0); + } +out: + close(pip[1]); + return pip[0]; +} + +static int +openredirect(union node *redir) +{ + char *fname; + int f; + + switch (redir->nfile.type) { + case NFROM: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDONLY)) < 0) + goto eopen; + break; + case NFROMTO: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) + goto ecreate; + break; + case NTO: + /* Take care of noclobber mode. */ + if (Cflag) { + fname = redir->nfile.expfname; + if ((f = noclobberopen(fname)) < 0) + goto ecreate; + break; + } + /* FALLTHROUGH */ + case NCLOBBER: + fname = redir->nfile.expfname; + if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + goto ecreate; + break; + case NAPPEND: + fname = redir->nfile.expfname; + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + goto ecreate; + break; + default: +#ifdef DEBUG + abort(); +#endif + /* Fall through to eliminate warning. */ + case NTOFD: + case NFROMFD: + f = -1; + break; + case NHERE: + case NXHERE: + f = openhere(redir); + break; + } + + return f; +ecreate: + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +eopen: + error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); +} + +static inline void +dupredirect(union node *redir, int f) +{ + int fd = redir->nfile.fd; + + if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + copyfd(redir->ndup.dupfd, fd); + } + return; + } + + if (f != fd) { + copyfd(f, fd); + close(f); + } + return; +} + +/* + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout, is saved in memory. + */ + +static void +redirect(union node *redir, int flags) +{ + union node *n; + struct redirtab *sv; + int i; + int fd; + int newfd; + int *p; + nullredirs++; + if (!redir) { + return; + } + sv = NULL; + INTOFF; + if (flags & REDIR_PUSH) { + struct redirtab *q; + q = ckmalloc(sizeof (struct redirtab)); + q->next = redirlist; + redirlist = q; + q->nullredirs = nullredirs - 1; + for (i = 0 ; i < 10 ; i++) + q->renamed[i] = EMPTY; + nullredirs = 0; + sv = q; + } + n = redir; + do { + fd = n->nfile.fd; + if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && + n->ndup.dupfd == fd) + continue; /* redirect from/to same file descriptor */ + + newfd = openredirect(n); + if (fd == newfd) + continue; + if (sv && *(p = &sv->renamed[fd]) == EMPTY) { + i = fcntl(fd, F_DUPFD, 10); + + if (i == -1) { + i = errno; + if (i != EBADF) { + close(newfd); + errno = i; + error("%d: %m", fd); + /* NOTREACHED */ + } + } else { + *p = i; + close(fd); + } + } else { + close(fd); + } + dupredirect(n, newfd); + } while ((n = n->nfile.next)); + INTON; + if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0) + preverrout_fd = sv->renamed[2]; +} + + +/* + * Undo the effects of the last redirection. + */ + +void +popredir(int drop) +{ + struct redirtab *rp; + int i; + + if (--nullredirs >= 0) + return; + INTOFF; + rp = redirlist; + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] != EMPTY) { + if (!drop) { + close(i); + copyfd(rp->renamed[i], i); + } + close(rp->renamed[i]); + } + } + redirlist = rp->next; + nullredirs = rp->nullredirs; + ckfree(rp); + INTON; +} + +/* + * Undo all redirections. Called on error or interrupt. + */ + +/* + * Discard all saved file descriptors. + */ + +void +clearredir(int drop) +{ + for (;;) { + nullredirs = 0; + if (!redirlist) + break; + popredir(drop); + } +} + + +/* + * Copy a file descriptor to be >= to. Returns -1 + * if the source file descriptor is closed, EMPTY if there are no unused + * file descriptors left. + */ + +int +copyfd(int from, int to) +{ + int newfd; + + newfd = fcntl(from, F_DUPFD, to); + if (newfd < 0) { + if (errno == EMFILE) + return EMPTY; + else + error("%d: %m", from); + } + return newfd; +} + + +int +redirectsafe(union node *redir, int flags) +{ + int err; + volatile int saveint; + struct jmploc *volatile savehandler = handler; + struct jmploc jmploc; + + SAVEINT(saveint); + if (!(err = setjmp(jmploc.loc) * 2)) { + handler = &jmploc; + redirect(redir, flags); + } + handler = savehandler; + if (err && exception != EXERROR) + longjmp(handler->loc, 1); + RESTOREINT(saveint); + return err; +} + +/* $NetBSD: show.c,v 1.24 2003/01/22 20:36:04 dsl Exp $ */ + +#ifdef DEBUG +static void shtree(union node *, int, char *, FILE*); +static void shcmd(union node *, FILE *); +static void sharg(union node *, FILE *); +static void indent(int, char *, FILE *); +static void trstring(char *); + + +void +showtree(union node *n) +{ + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static void +shtree(union node *n, int ind, char *pfx, FILE *fp) +{ + struct nodelist *lp; + const char *s; + + if (n == NULL) + return; + + indent(ind, pfx, fp); + switch(n->type) { + case NSEMI: + s = "; "; + goto binop; + case NAND: + s = " && "; + goto binop; + case NOR: + s = " || "; +binop: + shtree(n->nbinary.ch1, ind, NULL, fp); + /* if (ind < 0) */ + fputs(s, fp); + shtree(n->nbinary.ch2, ind, NULL, fp); + break; + case NCMD: + shcmd(n, fp); + if (ind >= 0) + putc('\n', fp); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + shcmd(lp->n, fp); + if (lp->next) + fputs(" | ", fp); + } + if (n->npipe.backgnd) + fputs(" &", fp); + if (ind >= 0) + putc('\n', fp); + break; + default: + fprintf(fp, "", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + +static void +shcmd(union node *cmd, FILE *fp) +{ + union node *np; + int first; + const char *s; + int dftfd; + + first = 1; + for (np = cmd->ncmd.args ; np ; np = np->narg.next) { + if (! first) + putchar(' '); + sharg(np, fp); + first = 0; + } + for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { + if (! first) + putchar(' '); + switch (np->nfile.type) { + case NTO: s = ">"; dftfd = 1; break; + case NCLOBBER: s = ">|"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NFROM: s = "<"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + case NFROMTO: s = "<>"; dftfd = 0; break; + default: s = "*error*"; dftfd = 0; break; + } + if (np->nfile.fd != dftfd) + fprintf(fp, "%d", np->nfile.fd); + fputs(s, fp); + if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { + fprintf(fp, "%d", np->ndup.dupfd); + } else { + sharg(np->nfile.fname, fp); + } + first = 0; + } +} + + + +static void +sharg(union node *arg, FILE *fp) +{ + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + out1fmt("\n", arg->type); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch (*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + if (subtype == VSLENGTH) + putc('#', fp); + + while (*p != '=') + putc(*p++, fp); + + if (subtype & VSNUL) + putc(':', fp); + + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + case VSTRIMLEFT: + putc('#', fp); + break; + case VSTRIMLEFTMAX: + putc('#', fp); + putc('#', fp); + break; + case VSTRIMRIGHT: + putc('%', fp); + break; + case VSTRIMRIGHTMAX: + putc('%', fp); + putc('%', fp); + break; + case VSLENGTH: + break; + default: + out1fmt("", subtype); + } + break; + case CTLENDVAR: + putc('}', fp); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + putc('$', fp); + putc('(', fp); + shtree(bqlist->n, -1, NULL, fp); + putc(')', fp); + break; + default: + putc(*p, fp); + break; + } + } +} + + +static void +indent(int amount, char *pfx, FILE *fp) +{ + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} + + + +/* + * Debugging stuff. + */ + + +FILE *tracefile; + + +void +trputc(int c) +{ + if (debug != 1) + return; + putc(c, tracefile); +} + +void +trace(const char *fmt, ...) +{ + va_list va; + + if (debug != 1) + return; + va_start(va, fmt); + (void) vfprintf(tracefile, fmt, va); + va_end(va); +} + +void +tracev(const char *fmt, va_list va) +{ + if (debug != 1) + return; + (void) vfprintf(tracefile, fmt, va); +} + + +void +trputs(const char *s) +{ + if (debug != 1) + return; + fputs(s, tracefile); +} + + +static void +trstring(char *s) +{ + char *p; + char c; + + if (debug != 1) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch (*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; + case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +} + + +void +trargs(char **ap) +{ + if (debug != 1) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } +} + + +void +opentrace(void) +{ + char s[100]; +#ifdef O_APPEND + int flags; +#endif + + if (debug != 1) { + if (tracefile) + fflush(tracefile); + /* leave open because libedit might be using it */ + return; + } + scopy("./trace", s); + if (tracefile) { + if (!freopen(s, "a", tracefile)) { + fprintf(stderr, "Can't re-open %s\n", s); + debug = 0; + return; + } + } else { + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + debug = 0; + return; + } + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + setlinebuf(tracefile); + fputs("\nTracing started.\n", tracefile); +} +#endif /* DEBUG */ + + +/* $NetBSD: trap.c,v 1.28 2002/11/24 22:35:43 christos Exp $ */ + +/* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell, + */ + +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ + + + +/* + * The trap builtin. + */ + +int +trapcmd(int argc, char **argv) +{ + char *action; + char **ap; + int signo; + + nextopt(nullstr); + ap = argptr; + if (!*ap) { + for (signo = 0 ; signo < NSIG ; signo++) { + if (trap[signo] != NULL) { + const char *sn; + + sn = u_signal_names(0, &signo, 0); + if (sn == NULL) + sn = "???"; + out1fmt("trap -- %s %s\n", + single_quote(trap[signo]), sn); + } + } + return 0; + } + if (!ap[1]) + action = NULL; + else + action = *ap++; + while (*ap) { + if ((signo = decode_signal(*ap, 0)) < 0) + error("%s: bad trap", *ap); + INTOFF; + if (action) { + if (action[0] == '-' && action[1] == '\0') + action = NULL; + else + action = savestr(action); + } + if (trap[signo]) + ckfree(trap[signo]); + trap[signo] = action; + if (signo != 0) + setsignal(signo); + INTON; + ap++; + } + return 0; +} + + +/* + * Clear traps on a fork. + */ + +void +clear_traps(void) +{ + char **tp; + + for (tp = trap ; tp < &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + INTON; + } + } +} + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +void +setsignal(int signo) +{ + int action; + char *t, tsig; + struct sigaction act; + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (rootshell && action == S_DFL) { + switch (signo) { + case SIGINT: + if (iflag || minusc || sflag == 0) + action = S_CATCH; + break; + case SIGQUIT: +#ifdef DEBUG + if (debug) + break; +#endif + /* FALLTHROUGH */ + case SIGTERM: + if (iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (mflag) + action = S_IGN; + break; +#endif + } + } + + t = &sigmode[signo - 1]; + tsig = *t; + if (tsig == 0) { + /* + * current setting unknown + */ + if (sigaction(signo, 0, &act) == -1) { + /* + * Pretend it worked; maybe we should give a warning + * here, but other shells don't. We don't alter + * sigmode, so that we retry every time. + */ + return; + } + if (act.sa_handler == SIG_IGN) { + if (mflag && (signo == SIGTSTP || + signo == SIGTTIN || signo == SIGTTOU)) { + tsig = S_IGN; /* don't hard ignore these */ + } else + tsig = S_HARD_IGN; + } else { + tsig = S_RESET; /* force to be set */ + } + } + if (tsig == S_HARD_IGN || tsig == action) + return; + switch (action) { + case S_CATCH: + act.sa_handler = onsig; + break; + case S_IGN: + act.sa_handler = SIG_IGN; + break; + default: + act.sa_handler = SIG_DFL; + } + *t = action; + act.sa_flags = 0; + sigfillset(&act.sa_mask); + sigaction(signo, &act, 0); +} + +/* + * Ignore a signal. + */ + +void +ignoresig(int signo) +{ + if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { + signal(signo, SIG_IGN); + } + sigmode[signo - 1] = S_HARD_IGN; +} + + +/* + * Signal handler. + */ + +void +onsig(int signo) +{ + gotsig[signo - 1] = 1; + pendingsigs = signo; + + if (exsig || (signo == SIGINT && !trap[SIGINT])) { + if (!suppressint) + onint(); + intpending = 1; + } +} + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +void +dotrap(void) +{ + char *p; + char *q; + int savestatus; + + savestatus = exitstatus; + q = gotsig; + while (pendingsigs = 0, xbarrier(), (p = memchr(q, 1, NSIG - 1))) { + *p = 0; + p = trap[p - q + 1]; + if (!p) + continue; + evalstring(p); + exitstatus = savestatus; + } +} + + +/* + * Controls whether the shell is interactive or not. + */ + +void +setinteractive(int on) +{ + static int is_interactive; + + if (++on == is_interactive) + return; + is_interactive = on; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET + if(is_interactive > 1) { + /* Looks like they want an interactive shell */ + static int do_banner; + + if(!do_banner) { + out1fmt( + "\n\n" BB_BANNER " Built-in shell (ash)\n" + "Enter 'help' for a list of built-in commands.\n\n"); + do_banner++; + } + } +#endif +} + + +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET +/*** List the available builtins ***/ + +static int helpcmd(int argc, char **argv) +{ + int col, i; + + out1fmt("\nBuilt-in commands:\n-------------------\n"); + for (col = 0, i = 0; i < NUMBUILTINS; i++) { + col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), + builtincmd[i].name + 1); + if (col > 60) { + out1fmt("\n"); + col = 0; + } + } +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + { + extern const struct BB_applet applets[]; + extern const size_t NUM_APPLETS; + + for (i = 0; i < NUM_APPLETS; i++) { + + col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name); + if (col > 60) { + out1fmt("\n"); + col = 0; + } + } + } +#endif + out1fmt("\n\n"); + return EXIT_SUCCESS; +} +#endif /* CONFIG_FEATURE_SH_EXTRA_QUIET */ + +/* + * Called to exit the shell. + */ + +void +exitshell(void) +{ + struct jmploc loc; + char *p; + int status; + int jmp; + + jmp = setjmp(loc.loc); + status = exitstatus; + TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); + if (jmp) + goto out; + handler = &loc; + if ((p = trap[0]) != NULL && *p != '\0') { + trap[0] = NULL; + evalstring(p); + } + flushall(); + setjobctl(0); +#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY + if (iflag && rootshell) { + const char *hp = lookupvar("HISTFILE"); + + if(hp != NULL ) + save_history ( hp ); + } +#endif +out: + _exit(status); + /* NOTREACHED */ +} + +static int decode_signal(const char *string, int minsig) +{ + int signo; + const char *name = u_signal_names(string, &signo, minsig); + + return name ? signo : -1; +} + +/* $NetBSD: var.c,v 1.32 2003/01/22 20:36:04 dsl Exp $ */ + +static struct var *vartab[VTABSIZE]; + +static int vpcmp(const void *, const void *); +static struct var **findvar(struct var **, const char *); + +/* + * Initialize the variable symbol tables and import the environment + */ + + +#ifdef CONFIG_ASH_GETOPTS +/* + * Safe version of setvar, returns 1 on success 0 on failure. + */ + +int +setvarsafe(const char *name, const char *val, int flags) +{ + int err; + volatile int saveint; + struct jmploc *volatile savehandler = handler; + struct jmploc jmploc; + + SAVEINT(saveint); + if (setjmp(jmploc.loc)) + err = 1; + else { + handler = &jmploc; + setvar(name, val, flags); + err = 0; + } + handler = savehandler; + RESTOREINT(saveint); + return err; +} +#endif + +/* + * Set the value of a variable. The flags argument is ored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +static void +setvar(const char *name, const char *val, int flags) +{ + char *p, *q; + size_t namelen; + char *nameeq; + size_t vallen; + + q = endofname(name); + p = strchrnul(q, '='); + namelen = p - name; + if (!namelen || p != q) + error("%.*s: bad variable name", namelen, name); + vallen = 0; + if (val == NULL) { + flags |= VUNSET; + } else { + vallen = strlen(val); + } + INTOFF; + p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen); + *p++ = '\0'; + if (vallen) { + p[-1] = '='; + p = mempcpy(p, val, vallen); + } + *p = '\0'; + setvareq(nameeq, flags | VNOSAVE); + INTON; +} + + +/* + * Same as setvar except that the variable and value are passed in + * the first argument as name=value. Since the first argument will + * be actually stored in the table, it should not be a string that + * will go away. + * Called with interrupts off. + */ + +void +setvareq(char *s, int flags) +{ + struct var *vp, **vpp; + + vpp = hashvar(s); + flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); + vp = *findvar(vpp, s); + if (vp) { + if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { + const char *n; + + if (flags & VNOSAVE) + free(s); + n = vp->text; + error("%.*s: is read only", strchrnul(n, '=') - n, n); + } + + if (flags & VNOSET) + return; + + if (vp->func && (flags & VNOFUNC) == 0) + (*vp->func)(strchrnul(s, '=') + 1); + + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + + flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); + } else { + if (flags & VNOSET) + return; + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->next = *vpp; + vp->func = NULL; + *vpp = vp; + } + if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) + s = savestr(s); + vp->text = s; + vp->flags = flags; +} + + +/* + * Process a linked list of variable assignments. + */ + +static void +listsetvar(struct strlist *list_set_var, int flags) +{ + struct strlist *lp = list_set_var; + + if (!lp) + return; + INTOFF; + do { + setvareq(lp->text, flags); + } while ((lp = lp->next)); + INTON; +} + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +static char * +lookupvar(const char *name) +{ + struct var *v; + + if ((v = *findvar(hashvar(name), name))) { +#ifdef DYNAMIC_VAR + /* + * Dynamic variables are implemented roughly the same way they are + * in bash. Namely, they're "special" so long as they aren't unset. + * As soon as they're unset, they're no longer dynamic, and dynamic + * lookup will no longer happen at that point. -- PFM. + */ + if((v->flags & VDYNAMIC)) + (*v->func)(NULL); +#endif + if(!(v->flags & VUNSET)) + return strchrnul(v->text, '=') + 1; + } + + return NULL; +} + + +/* + * Search the environment of a builtin command. + */ + +static char * +bltinlookup(const char *name) +{ + struct strlist *sp; + + for (sp = cmdenviron ; sp ; sp = sp->next) { + if (varequal(sp->text, name)) + return strchrnul(sp->text, '=') + 1; + } + return lookupvar(name); +} + + +/* + * Generate a list of variables satisfying the given conditions. + */ + +static char ** +listvars(int on, int off, char ***end) +{ + struct var **vpp; + struct var *vp; + char **ep; + int mask; + + STARTSTACKSTR(ep); + vpp = vartab; + mask = on | off; + do { + for (vp = *vpp ; vp ; vp = vp->next) + if ((vp->flags & mask) == on) { + if (ep == stackstrend()) + ep = growstackstr(); + *ep++ = (char *) vp->text; + } + } while (++vpp < vartab + VTABSIZE); + if (ep == stackstrend()) + ep = growstackstr(); + if (end) + *end = ep; + *ep++ = NULL; + return grabstackstr(ep); +} + + +/* + * POSIX requires that 'set' (but not export or readonly) output the + * variables in lexicographic order - by the locale's collating order (sigh). + * Maybe we could keep them in an ordered balanced binary tree + * instead of hashed lists. + * For now just roll 'em through qsort for printing... + */ + +static int +showvars(const char *sep_prefix, int on, int off) +{ + const char *sep; + char **ep, **epend; + + ep = listvars(on, off, &epend); + qsort(ep, epend - ep, sizeof(char *), vpcmp); + + sep = *sep_prefix ? spcstr : sep_prefix; + + for (; ep < epend; ep++) { + const char *p; + const char *q; + + p = strchrnul(*ep, '='); + q = nullstr; + if (*p) + q = single_quote(++p); + + out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); + } + + return 0; +} + + + +/* + * The export and readonly commands. + */ + +static int +exportcmd(int argc, char **argv) +{ + struct var *vp; + char *name; + const char *p; + char **aptr; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + int notp; + + notp = nextopt("p") - 'p'; + if (notp && ((name = *(aptr = argptr)))) { + do { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + if ((vp = *findvar(hashvar(name), name))) { + vp->flags |= flag; + continue; + } + } + setvar(name, p, flag); + } while ((name = *++aptr) != NULL); + } else { + showvars(argv[0], flag, 0); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +static inline void +mklocal(char *name) +{ + struct localvar *lvp; + struct var **vpp; + struct var *vp; + + INTOFF; + lvp = ckmalloc(sizeof (struct localvar)); + if (name[0] == '-' && name[1] == '\0') { + char *p; + p = ckmalloc(sizeof(optlist)); + lvp->text = memcpy(p, optlist, sizeof(optlist)); + vp = NULL; + } else { + char *eq; + + vpp = hashvar(name); + vp = *findvar(vpp, name); + eq = strchr(name, '='); + if (vp == NULL) { + if (eq) + setvareq(name, VSTRFIXED); + else + setvar(name, NULL, VSTRFIXED); + vp = *vpp; /* the new variable */ + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (eq) + setvareq(name, 0); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + +/* + * The "local" command. + */ + +static int +localcmd(int argc, char **argv) +{ + char *name; + + argv = argptr; + while ((name = *argv++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Called after a function returns. + * Interrupts must be off. + */ + +static void +poplocalvars(void) +{ + struct localvar *lvp; + struct var *vp; + + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + TRACE(("poplocalvar %s", vp ? vp->text : "-")); + if (vp == NULL) { /* $- saved */ + memcpy(optlist, lvp->text, sizeof(optlist)); + ckfree(lvp->text); + optschanged(); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + unsetvar(vp->text); + } else { + if (vp->func) + (*vp->func)(strchrnul(lvp->text, '=') + 1); + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } +} + + +/* + * The unset builtin command. We unset the function before we unset the + * variable to allow a function to be unset when there is a readonly variable + * with the same name. + */ + +int +unsetcmd(int argc, char **argv) +{ + char **ap; + int i; + int flag = 0; + int ret = 0; + + while ((i = nextopt("vf")) != '\0') { + flag = i; + } + + for (ap = argptr; *ap ; ap++) { + if (flag != 'f') { + i = unsetvar(*ap); + ret |= i; + if (!(i & 2)) + continue; + } + if (flag != 'v') + unsetfunc(*ap); + } + return ret & 1; +} + + +/* + * Unset the specified variable. + */ + +int +unsetvar(const char *s) +{ + struct var **vpp; + struct var *vp; + int retval; + + vpp = findvar(hashvar(s), s); + vp = *vpp; + retval = 2; + if (vp) { + int flags = vp->flags; + + retval = 1; + if (flags & VREADONLY) + goto out; +#ifdef DYNAMIC_VAR + vp->flags &= ~VDYNAMIC; +#endif + if (flags & VUNSET) + goto ok; + if ((flags & VSTRFIXED) == 0) { + INTOFF; + if ((flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + INTON; + } else { + setvar(s, 0, 0); + vp->flags &= ~VEXPORT; + } +ok: + retval = 0; + } + +out: + return retval; +} + + + +/* + * Find the appropriate entry in the hash table from the name. + */ + +static struct var ** +hashvar(const char *p) +{ + unsigned int hashval; + + hashval = ((unsigned char) *p) << 4; + while (*p && *p != '=') + hashval += (unsigned char) *p++; + return &vartab[hashval % VTABSIZE]; +} + + + +/* + * Compares two strings up to the first = or '\0'. The first + * string must be terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +int +varcmp(const char *p, const char *q) +{ + int c, d; + + while ((c = *p) == (d = *q)) { + if (!c || c == '=') + goto out; + p++; + q++; + } + if (c == '=') + c = 0; + if (d == '=') + d = 0; +out: + return c - d; +} + +static int +vpcmp(const void *a, const void *b) +{ + return varcmp(*(const char **)a, *(const char **)b); +} + +static struct var ** +findvar(struct var **vpp, const char *name) +{ + for (; *vpp; vpp = &(*vpp)->next) { + if (varequal((*vpp)->text, name)) { + break; + } + } + return vpp; +} +/* $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $ */ + +#include + +static const unsigned char timescmd_str[] = { + ' ', offsetof(struct tms, tms_utime), + '\n', offsetof(struct tms, tms_stime), + ' ', offsetof(struct tms, tms_cutime), + '\n', offsetof(struct tms, tms_cstime), + 0 +}; + +static int timescmd(int ac, char **av) +{ + long int clk_tck, s, t; + const unsigned char *p; + struct tms buf; + + clk_tck = sysconf(_SC_CLK_TCK); + times(&buf); + + p = timescmd_str; + do { + t = *(clock_t *)(((char *) &buf) + p[1]); + s = t / clk_tck; + out1fmt("%ldm%ld.%.3lds%c", + s/60, s%60, + ((t - s * clk_tck) * 1000) / clk_tck, + p[0]); + } while (*(p += 2)); + + return 0; +} + +#ifdef CONFIG_ASH_MATH_SUPPORT +static arith_t +dash_arith(const char *s) +{ + arith_t result; + int errcode = 0; + + INTOFF; + result = arith(s, &errcode); + if (errcode < 0) { + if (errcode == -3) + error("exponent less than 0"); + else if (errcode == -2) + error("divide by zero"); + else if (errcode == -5) + error("expression recursion loop detected"); + else + synerror(s); + } + INTON; + + return (result); +} + + +/* + * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell. + * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. + * + * Copyright (C) 2003 Vladimir Oleynik + */ + +static int +letcmd(int argc, char **argv) +{ + char **ap; + arith_t i; + + ap = argv + 1; + if(!*ap) + error("expression expected"); + for (ap = argv + 1; *ap; ap++) { + i = dash_arith(*ap); + } + + return (!i); +} +#endif /* CONFIG_ASH_MATH_SUPPORT */ + +/* $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $ */ + +/* + * Miscellaneous builtins. + */ + +#undef rflag + +#ifdef __GLIBC__ +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 +typedef enum __rlimit_resource rlim_t; +#endif +#endif + + +/* + * The read builtin. The -e option causes backslashes to escape the + * following character. + * + * This uses unbuffered input, which may be avoidable in some cases. + */ + +static int +readcmd(int argc, char **argv) +{ + char **ap; + int backslash; + char c; + int rflag; + char *prompt; + const char *ifs; + char *p; + int startword; + int status; + int i; + + rflag = 0; + prompt = NULL; + while ((i = nextopt("p:r")) != '\0') { + if (i == 'p') + prompt = optionarg; + else + rflag = 1; + } + if (prompt && isatty(0)) { + out2str(prompt); + } + if (*(ap = argptr) == NULL) + error("arg count"); + if ((ifs = bltinlookup("IFS")) == NULL) + ifs = defifs; + status = 0; + startword = 1; + backslash = 0; + STARTSTACKSTR(p); + for (;;) { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + if (c == '\0') + continue; + if (backslash) { + backslash = 0; + if (c != '\n') + goto put; + continue; + } + if (!rflag && c == '\\') { + backslash++; + continue; + } + if (c == '\n') + break; + if (startword && *ifs == ' ' && strchr(ifs, c)) { + continue; + } + startword = 0; + if (ap[1] != NULL && strchr(ifs, c) != NULL) { + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + startword = 1; + STARTSTACKSTR(p); + } else { +put: + STPUTC(c, p); + } + } + STACKSTRNUL(p); + /* Remove trailing blanks */ + while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL) + *p = '\0'; + setvar(*ap, stackblock(), 0); + while (*++ap != NULL) + setvar(*ap, nullstr, 0); + return status; +} + + +static int umaskcmd(int argc, char **argv) +{ + static const char permuser[3] = "ugo"; + static const char permmode[3] = "rwx"; + static const short int permmask[] = { + S_IRUSR, S_IWUSR, S_IXUSR, + S_IRGRP, S_IWGRP, S_IXGRP, + S_IROTH, S_IWOTH, S_IXOTH + }; + + char *ap; + mode_t mask; + int i; + int symbolic_mode = 0; + + while (nextopt("S") != '\0') { + symbolic_mode = 1; + } + + INTOFF; + mask = umask(0); + umask(mask); + INTON; + + if ((ap = *argptr) == NULL) { + if (symbolic_mode) { + char buf[18]; + char *p = buf; + + for (i = 0; i < 3; i++) { + int j; + + *p++ = permuser[i]; + *p++ = '='; + for (j = 0; j < 3; j++) { + if ((mask & permmask[3 * i + j]) == 0) { + *p++ = permmode[j]; + } + } + *p++ = ','; + } + *--p = 0; + puts(buf); + } else { + out1fmt("%.4o\n", mask); + } + } else { + if (is_digit((unsigned char) *ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error(illnum, argv[1]); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else { + mask = ~mask & 0777; + if (!bb_parse_mode(ap, &mask)) { + error("Illegal mode: %s", ap); + } + umask(~mask & 0777); + } + } + return 0; +} + +/* + * ulimit builtin + * + * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and + * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with + * ash by J.T. Conklin. + * + * Public domain. + */ + +struct limits { + const char *name; + int cmd; + int factor; /* multiply by to get rlim_{cur,max} values */ + char option; +}; + +static const struct limits limits[] = { +#ifdef RLIMIT_CPU + { "time(seconds)", RLIMIT_CPU, 1, 't' }, +#endif +#ifdef RLIMIT_FSIZE + { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, +#endif +#ifdef RLIMIT_DATA + { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, +#endif +#ifdef RLIMIT_STACK + { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, +#endif +#ifdef RLIMIT_CORE + { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, +#endif +#ifdef RLIMIT_RSS + { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, +#endif +#ifdef RLIMIT_MEMLOCK + { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, +#endif +#ifdef RLIMIT_NPROC + { "process", RLIMIT_NPROC, 1, 'p' }, +#endif +#ifdef RLIMIT_NOFILE + { "nofiles", RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_AS + { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' }, +#endif +#ifdef RLIMIT_LOCKS + { "locks", RLIMIT_LOCKS, 1, 'w' }, +#endif + { (char *) 0, 0, 0, '\0' } +}; + +enum limtype { SOFT = 0x1, HARD = 0x2 }; + +static void printlim(enum limtype how, const struct rlimit *limit, + const struct limits *l) +{ + rlim_t val; + + val = limit->rlim_max; + if (how & SOFT) + val = limit->rlim_cur; + + if (val == RLIM_INFINITY) + out1fmt("unlimited\n"); + else { + val /= l->factor; + out1fmt("%lld\n", (long long) val); + } +} + +int +ulimitcmd(int argc, char **argv) +{ + int c; + rlim_t val = 0; + enum limtype how = SOFT | HARD; + const struct limits *l; + int set, all = 0; + int optc, what; + struct rlimit limit; + + what = 'f'; + while ((optc = nextopt("HSa" +#ifdef RLIMIT_CPU + "t" +#endif +#ifdef RLIMIT_FSIZE + "f" +#endif +#ifdef RLIMIT_DATA + "d" +#endif +#ifdef RLIMIT_STACK + "s" +#endif +#ifdef RLIMIT_CORE + "c" +#endif +#ifdef RLIMIT_RSS + "m" +#endif +#ifdef RLIMIT_MEMLOCK + "l" +#endif +#ifdef RLIMIT_NPROC + "p" +#endif +#ifdef RLIMIT_NOFILE + "n" +#endif +#ifdef RLIMIT_AS + "v" +#endif +#ifdef RLIMIT_LOCKS + "w" +#endif + )) != '\0') + switch (optc) { + case 'H': + how = HARD; + break; + case 'S': + how = SOFT; + break; + case 'a': + all = 1; + break; + default: + what = optc; + } + + for (l = limits; l->option != what; l++) + ; + + set = *argptr ? 1 : 0; + if (set) { + char *p = *argptr; + + if (all || argptr[1]) + error("too many arguments"); + if (strncmp(p, "unlimited\n", 9) == 0) + val = RLIM_INFINITY; + else { + val = (rlim_t) 0; + + while ((c = *p++) >= '0' && c <= '9') + { + val = (val * 10) + (long)(c - '0'); + if (val < (rlim_t) 0) + break; + } + if (c) + error("bad number"); + val *= l->factor; + } + } + if (all) { + for (l = limits; l->name; l++) { + getrlimit(l->cmd, &limit); + out1fmt("%-20s ", l->name); + printlim(how, &limit, l); + } + return 0; + } + + getrlimit(l->cmd, &limit); + if (set) { + if (how & HARD) + limit.rlim_max = val; + if (how & SOFT) + limit.rlim_cur = val; + if (setrlimit(l->cmd, &limit) < 0) + error("error setting limit (%m)"); + } else { + printlim(how, &limit, l); + } + return 0; +} + + +#ifdef CONFIG_ASH_MATH_SUPPORT + +/* Copyright (c) 2001 Aaron Lehmann + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* This is my infix parser/evaluator. It is optimized for size, intended + * as a replacement for yacc-based parsers. However, it may well be faster + * than a comparable parser written in yacc. The supported operators are + * listed in #defines below. Parens, order of operations, and error handling + * are supported. This code is thread safe. The exact expression format should + * be that which POSIX specifies for shells. */ + +/* The code uses a simple two-stack algorithm. See + * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html + * for a detailed explanation of the infix-to-postfix algorithm on which + * this is based (this code differs in that it applies operators immediately + * to the stack instead of adding them to a queue to end up with an + * expression). */ + +/* To use the routine, call it with an expression string and error return + * pointer */ + +/* + * Aug 24, 2001 Manuel Novoa III + * + * Reduced the generated code size by about 30% (i386) and fixed several bugs. + * + * 1) In arith_apply(): + * a) Cached values of *numptr and &(numptr[-1]). + * b) Removed redundant test for zero denominator. + * + * 2) In arith(): + * a) Eliminated redundant code for processing operator tokens by moving + * to a table-based implementation. Also folded handling of parens + * into the table. + * b) Combined all 3 loops which called arith_apply to reduce generated + * code size at the cost of speed. + * + * 3) The following expressions were treated as valid by the original code: + * 1() , 0! , 1 ( *3 ) . + * These bugs have been fixed by internally enclosing the expression in + * parens and then checking that all binary ops and right parens are + * preceded by a valid expression (NUM_TOKEN). + * + * Note: It may be desirable to replace Aaron's test for whitespace with + * ctype's isspace() if it is used by another busybox applet or if additional + * whitespace chars should be considered. Look below the "#include"s for a + * precompiler test. + */ + +/* + * Aug 26, 2001 Manuel Novoa III + * + * Return 0 for null expressions. Pointed out by Vladimir Oleynik. + * + * Merge in Aaron's comments previously posted to the busybox list, + * modified slightly to take account of my changes to the code. + * + */ + +/* + * (C) 2003 Vladimir Oleynik + * + * - allow access to variable, + * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6) + * - realize assign syntax (VAR=expr, +=, *= etc) + * - realize exponentiation (** operator) + * - realize comma separated - expr, expr + * - realise ++expr --expr expr++ expr-- + * - realise expr ? expr : expr (but, second expr calculate always) + * - allow hexadecimal and octal numbers + * - was restored loses XOR operator + * - remove one goto label, added three ;-) + * - protect $((num num)) as true zero expr (Manuel`s error) + * - always use special isspace(), see comment from bash ;-) + */ + + +#define arith_isspace(arithval) \ + (arithval == ' ' || arithval == '\n' || arithval == '\t') + + +typedef unsigned char operator; + +/* An operator's token id is a bit of a bitfield. The lower 5 bits are the + * precedence, and 3 high bits are an ID unique across operators of that + * precedence. The ID portion is so that multiple operators can have the + * same precedence, ensuring that the leftmost one is evaluated first. + * Consider * and /. */ + +#define tok_decl(prec,id) (((id)<<5)|(prec)) +#define PREC(op) ((op) & 0x1F) + +#define TOK_LPAREN tok_decl(0,0) + +#define TOK_COMMA tok_decl(1,0) + +#define TOK_ASSIGN tok_decl(2,0) +#define TOK_AND_ASSIGN tok_decl(2,1) +#define TOK_OR_ASSIGN tok_decl(2,2) +#define TOK_XOR_ASSIGN tok_decl(2,3) +#define TOK_PLUS_ASSIGN tok_decl(2,4) +#define TOK_MINUS_ASSIGN tok_decl(2,5) +#define TOK_LSHIFT_ASSIGN tok_decl(2,6) +#define TOK_RSHIFT_ASSIGN tok_decl(2,7) + +#define TOK_MUL_ASSIGN tok_decl(3,0) +#define TOK_DIV_ASSIGN tok_decl(3,1) +#define TOK_REM_ASSIGN tok_decl(3,2) + +/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */ +#define convert_prec_is_assing(prec) do { if(prec == 3) prec = 2; } while(0) + +/* conditional is right associativity too */ +#define TOK_CONDITIONAL tok_decl(4,0) +#define TOK_CONDITIONAL_SEP tok_decl(4,1) + +#define TOK_OR tok_decl(5,0) + +#define TOK_AND tok_decl(6,0) + +#define TOK_BOR tok_decl(7,0) + +#define TOK_BXOR tok_decl(8,0) + +#define TOK_BAND tok_decl(9,0) + +#define TOK_EQ tok_decl(10,0) +#define TOK_NE tok_decl(10,1) + +#define TOK_LT tok_decl(11,0) +#define TOK_GT tok_decl(11,1) +#define TOK_GE tok_decl(11,2) +#define TOK_LE tok_decl(11,3) + +#define TOK_LSHIFT tok_decl(12,0) +#define TOK_RSHIFT tok_decl(12,1) + +#define TOK_ADD tok_decl(13,0) +#define TOK_SUB tok_decl(13,1) + +#define TOK_MUL tok_decl(14,0) +#define TOK_DIV tok_decl(14,1) +#define TOK_REM tok_decl(14,2) + +/* exponent is right associativity */ +#define TOK_EXPONENT tok_decl(15,1) + +/* For now unary operators. */ +#define UNARYPREC 16 +#define TOK_BNOT tok_decl(UNARYPREC,0) +#define TOK_NOT tok_decl(UNARYPREC,1) + +#define TOK_UMINUS tok_decl(UNARYPREC+1,0) +#define TOK_UPLUS tok_decl(UNARYPREC+1,1) + +#define PREC_PRE (UNARYPREC+2) + +#define TOK_PRE_INC tok_decl(PREC_PRE, 0) +#define TOK_PRE_DEC tok_decl(PREC_PRE, 1) + +#define PREC_POST (UNARYPREC+3) + +#define TOK_POST_INC tok_decl(PREC_POST, 0) +#define TOK_POST_DEC tok_decl(PREC_POST, 1) + +#define SPEC_PREC (UNARYPREC+4) + +#define TOK_NUM tok_decl(SPEC_PREC, 0) +#define TOK_RPAREN tok_decl(SPEC_PREC, 1) + +#define NUMPTR (*numstackptr) + +static inline int tok_have_assign(operator op) +{ + operator prec = PREC(op); + + convert_prec_is_assing(prec); + return (prec == PREC(TOK_ASSIGN) || + prec == PREC_PRE || prec == PREC_POST); +} + +static inline int is_right_associativity(operator prec) +{ + return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) || + prec == PREC(TOK_CONDITIONAL)); +} + + +typedef struct ARITCH_VAR_NUM { + arith_t val; + arith_t contidional_second_val; + char contidional_second_val_initialized; + char *var; /* if NULL then is regular number, + else is variable name */ +} v_n_t; + + +typedef struct CHK_VAR_RECURSIVE_LOOPED { + const char *var; + struct CHK_VAR_RECURSIVE_LOOPED *next; +} chk_var_recursive_looped_t; + +static chk_var_recursive_looped_t *prev_chk_var_recursive; + + +static int arith_lookup_val(v_n_t *t) +{ + if(t->var) { + const char * p = lookupvar(t->var); + + if(p) { + int errcode; + + /* recursive try as expression */ + chk_var_recursive_looped_t *cur; + chk_var_recursive_looped_t cur_save; + + for(cur = prev_chk_var_recursive; cur; cur = cur->next) { + if(strcmp(cur->var, t->var) == 0) { + /* expression recursion loop detected */ + return -5; + } + } + /* save current lookuped var name */ + cur = prev_chk_var_recursive; + cur_save.var = t->var; + cur_save.next = cur; + prev_chk_var_recursive = &cur_save; + + t->val = arith (p, &errcode); + /* restore previous ptr after recursiving */ + prev_chk_var_recursive = cur; + return errcode; + } else { + /* allow undefined var as 0 */ + t->val = 0; + } + } + return 0; +} + +/* "applying" a token means performing it on the top elements on the integer + * stack. For a unary operator it will only change the top element, but a + * binary operator will pop two arguments and push a result */ +static inline int +arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr) +{ + v_n_t *numptr_m1; + arith_t numptr_val, rez; + int ret_arith_lookup_val; + + if (NUMPTR == numstack) goto err; /* There is no operator that can work + without arguments */ + numptr_m1 = NUMPTR - 1; + + /* check operand is var with noninteger value */ + ret_arith_lookup_val = arith_lookup_val(numptr_m1); + if(ret_arith_lookup_val) + return ret_arith_lookup_val; + + rez = numptr_m1->val; + if (op == TOK_UMINUS) + rez *= -1; + else if (op == TOK_NOT) + rez = !rez; + else if (op == TOK_BNOT) + rez = ~rez; + else if (op == TOK_POST_INC || op == TOK_PRE_INC) + rez++; + else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) + rez--; + else if (op != TOK_UPLUS) { + /* Binary operators */ + + /* check and binary operators need two arguments */ + if (numptr_m1 == numstack) goto err; + + /* ... and they pop one */ + --NUMPTR; + numptr_val = rez; + if (op == TOK_CONDITIONAL) { + if(! numptr_m1->contidional_second_val_initialized) { + /* protect $((expr1 ? expr2)) without ": expr" */ + goto err; + } + rez = numptr_m1->contidional_second_val; + } else if(numptr_m1->contidional_second_val_initialized) { + /* protect $((expr1 : expr2)) without "expr ? " */ + goto err; + } + numptr_m1 = NUMPTR - 1; + if(op != TOK_ASSIGN) { + /* check operand is var with noninteger value for not '=' */ + ret_arith_lookup_val = arith_lookup_val(numptr_m1); + if(ret_arith_lookup_val) + return ret_arith_lookup_val; + } + if (op == TOK_CONDITIONAL) { + numptr_m1->contidional_second_val = rez; + } + rez = numptr_m1->val; + if (op == TOK_BOR || op == TOK_OR_ASSIGN) + rez |= numptr_val; + else if (op == TOK_OR) + rez = numptr_val || rez; + else if (op == TOK_BAND || op == TOK_AND_ASSIGN) + rez &= numptr_val; + else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) + rez ^= numptr_val; + else if (op == TOK_AND) + rez = rez && numptr_val; + else if (op == TOK_EQ) + rez = (rez == numptr_val); + else if (op == TOK_NE) + rez = (rez != numptr_val); + else if (op == TOK_GE) + rez = (rez >= numptr_val); + else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) + rez >>= numptr_val; + else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) + rez <<= numptr_val; + else if (op == TOK_GT) + rez = (rez > numptr_val); + else if (op == TOK_LT) + rez = (rez < numptr_val); + else if (op == TOK_LE) + rez = (rez <= numptr_val); + else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) + rez *= numptr_val; + else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) + rez += numptr_val; + else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) + rez -= numptr_val; + else if (op == TOK_ASSIGN || op == TOK_COMMA) + rez = numptr_val; + else if (op == TOK_CONDITIONAL_SEP) { + if (numptr_m1 == numstack) { + /* protect $((expr : expr)) without "expr ? " */ + goto err; + } + numptr_m1->contidional_second_val_initialized = op; + numptr_m1->contidional_second_val = numptr_val; + } + else if (op == TOK_CONDITIONAL) { + rez = rez ? + numptr_val : numptr_m1->contidional_second_val; + } + else if(op == TOK_EXPONENT) { + if(numptr_val < 0) + return -3; /* exponent less than 0 */ + else { + arith_t c = 1; + + if(numptr_val) + while(numptr_val--) + c *= rez; + rez = c; + } + } + else if(numptr_val==0) /* zero divisor check */ + return -2; + else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) + rez /= numptr_val; + else if (op == TOK_REM || op == TOK_REM_ASSIGN) + rez %= numptr_val; + } + if(tok_have_assign(op)) { + char buf[32]; + + if(numptr_m1->var == NULL) { + /* Hmm, 1=2 ? */ + goto err; + } + /* save to shell variable */ + snprintf(buf, sizeof(buf), "%lld", (long long) rez); + setvar(numptr_m1->var, buf, 0); + /* after saving, make previous value for v++ or v-- */ + if(op == TOK_POST_INC) + rez--; + else if(op == TOK_POST_DEC) + rez++; + } + numptr_m1->val = rez; + /* protect geting var value, is number now */ + numptr_m1->var = NULL; + return 0; +err: return(-1); +} + +/* longest must first */ +static const char op_tokens[] = { + '<','<','=',0, TOK_LSHIFT_ASSIGN, + '>','>','=',0, TOK_RSHIFT_ASSIGN, + '<','<', 0, TOK_LSHIFT, + '>','>', 0, TOK_RSHIFT, + '|','|', 0, TOK_OR, + '&','&', 0, TOK_AND, + '!','=', 0, TOK_NE, + '<','=', 0, TOK_LE, + '>','=', 0, TOK_GE, + '=','=', 0, TOK_EQ, + '|','=', 0, TOK_OR_ASSIGN, + '&','=', 0, TOK_AND_ASSIGN, + '*','=', 0, TOK_MUL_ASSIGN, + '/','=', 0, TOK_DIV_ASSIGN, + '%','=', 0, TOK_REM_ASSIGN, + '+','=', 0, TOK_PLUS_ASSIGN, + '-','=', 0, TOK_MINUS_ASSIGN, + '-','-', 0, TOK_POST_DEC, + '^','=', 0, TOK_XOR_ASSIGN, + '+','+', 0, TOK_POST_INC, + '*','*', 0, TOK_EXPONENT, + '!', 0, TOK_NOT, + '<', 0, TOK_LT, + '>', 0, TOK_GT, + '=', 0, TOK_ASSIGN, + '|', 0, TOK_BOR, + '&', 0, TOK_BAND, + '*', 0, TOK_MUL, + '/', 0, TOK_DIV, + '%', 0, TOK_REM, + '+', 0, TOK_ADD, + '-', 0, TOK_SUB, + '^', 0, TOK_BXOR, + /* uniq */ + '~', 0, TOK_BNOT, + ',', 0, TOK_COMMA, + '?', 0, TOK_CONDITIONAL, + ':', 0, TOK_CONDITIONAL_SEP, + ')', 0, TOK_RPAREN, + '(', 0, TOK_LPAREN, + 0 +}; +/* ptr to ")" */ +#define endexpression &op_tokens[sizeof(op_tokens)-7] + + +static arith_t arith (const char *expr, int *perrcode) +{ + register char arithval; /* Current character under analysis */ + operator lasttok, op; + operator prec; + + const char *p = endexpression; + int errcode; + + size_t datasizes = strlen(expr) + 2; + + /* Stack of integers */ + /* The proof that there can be no more than strlen(startbuf)/2+1 integers + * in any given correct or incorrect expression is left as an exercise to + * the reader. */ + v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)), + *numstackptr = numstack; + /* Stack of operator tokens */ + operator *stack = alloca((datasizes) * sizeof(operator)), + *stackptr = stack; + + *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ + *perrcode = errcode = 0; + + while(1) { + if ((arithval = *expr) == 0) { + if (p == endexpression) { + /* Null expression. */ + return 0; + } + + /* This is only reached after all tokens have been extracted from the + * input stream. If there are still tokens on the operator stack, they + * are to be applied in order. At the end, there should be a final + * result on the integer stack */ + + if (expr != endexpression + 1) { + /* If we haven't done so already, */ + /* append a closing right paren */ + expr = endexpression; + /* and let the loop process it. */ + continue; + } + /* At this point, we're done with the expression. */ + if (numstackptr != numstack+1) { + /* ... but if there isn't, it's bad */ + err: + return (*perrcode = -1); + } + if(numstack->var) { + /* expression is $((var)) only, lookup now */ + errcode = arith_lookup_val(numstack); + } + ret: + *perrcode = errcode; + return numstack->val; + } else { + /* Continue processing the expression. */ + if (arith_isspace(arithval)) { + /* Skip whitespace */ + goto prologue; + } + if((p = endofname(expr)) != expr) { + size_t var_name_size = (p-expr) + 1; /* trailing zero */ + + numstackptr->var = alloca(var_name_size); + safe_strncpy(numstackptr->var, expr, var_name_size); + expr = p; + num: + numstackptr->contidional_second_val_initialized = 0; + numstackptr++; + lasttok = TOK_NUM; + continue; + } else if (is_digit(arithval)) { + numstackptr->var = NULL; + numstackptr->val = strtoll(expr, (char **) &expr, 0); + goto num; + } + for(p = op_tokens; ; p++) { + const char *o; + + if(*p == 0) { + /* strange operator not found */ + goto err; + } + for(o = expr; *p && *o == *p; p++) + o++; + if(! *p) { + /* found */ + expr = o - 1; + break; + } + /* skip tail uncompared token */ + while(*p) + p++; + /* skip zero delim */ + p++; + } + op = p[1]; + + /* post grammar: a++ reduce to num */ + if(lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) + lasttok = TOK_NUM; + + /* Plus and minus are binary (not unary) _only_ if the last + * token was as number, or a right paren (which pretends to be + * a number, since it evaluates to one). Think about it. + * It makes sense. */ + if (lasttok != TOK_NUM) { + switch(op) { + case TOK_ADD: + op = TOK_UPLUS; + break; + case TOK_SUB: + op = TOK_UMINUS; + break; + case TOK_POST_INC: + op = TOK_PRE_INC; + break; + case TOK_POST_DEC: + op = TOK_PRE_DEC; + break; + } + } + /* We don't want a unary operator to cause recursive descent on the + * stack, because there can be many in a row and it could cause an + * operator to be evaluated before its argument is pushed onto the + * integer stack. */ + /* But for binary operators, "apply" everything on the operator + * stack until we find an operator with a lesser priority than the + * one we have just extracted. */ + /* Left paren is given the lowest priority so it will never be + * "applied" in this way. + * if associativity is right and priority eq, applied also skip + */ + prec = PREC(op); + if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { + /* not left paren or unary */ + if (lasttok != TOK_NUM) { + /* binary op must be preceded by a num */ + goto err; + } + while (stackptr != stack) { + if (op == TOK_RPAREN) { + /* The algorithm employed here is simple: while we don't + * hit an open paren nor the bottom of the stack, pop + * tokens and apply them */ + if (stackptr[-1] == TOK_LPAREN) { + --stackptr; + /* Any operator directly after a */ + lasttok = TOK_NUM; + /* close paren should consider itself binary */ + goto prologue; + } + } else { + operator prev_prec = PREC(stackptr[-1]); + + convert_prec_is_assing(prec); + convert_prec_is_assing(prev_prec); + if (prev_prec < prec) + break; + /* check right assoc */ + if(prev_prec == prec && is_right_associativity(prec)) + break; + } + errcode = arith_apply(*--stackptr, numstack, &numstackptr); + if(errcode) goto ret; + } + if (op == TOK_RPAREN) { + goto err; + } + } + + /* Push this operator to the stack and remember it. */ + *stackptr++ = lasttok = op; + + prologue: + ++expr; + } + } +} +#endif /* CONFIG_ASH_MATH_SUPPORT */ + + +#ifdef DEBUG +const char *bb_applet_name = "debug stuff usage"; +int main(int argc, char **argv) +{ + return ash_main(argc, argv); +} +#endif + +/*- + * Copyright (c) 1989, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * 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..56b789ab6 --- /dev/null +++ b/busybox/shell/cmdedit.c @@ -0,0 +1,1582 @@ +/* vi: set sw=4 ts=4: */ +/* + * Termios command line History and Editing. + * + * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. + * Written by: Vladimir Oleynik + * + * 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" + +#include "../shell/cmdedit.h" + + +#ifdef CONFIG_LOCALE_SUPPORT +#define Isprint(c) isprint((c)) +#else +#define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') ) +#endif + +#ifdef TEST + +/* pretect redefined for test */ +#undef CONFIG_FEATURE_COMMAND_EDITING +#undef CONFIG_FEATURE_COMMAND_TAB_COMPLETION +#undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION +#undef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT +#undef CONFIG_FEATURE_CLEAN_UP + +#define CONFIG_FEATURE_COMMAND_EDITING +#define CONFIG_FEATURE_COMMAND_TAB_COMPLETION +#define CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION +#define CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT +#define CONFIG_FEATURE_CLEAN_UP + +#endif /* TEST */ + +#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION +#include +#include +#endif + +#ifdef CONFIG_FEATURE_COMMAND_EDITING + +#if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT) +#define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR +#endif + +#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR +#include "pwd_.h" +#endif /* advanced FEATURES */ + + +/* Maximum length of the linked list for the command line history */ +#ifndef CONFIG_FEATURE_COMMAND_HISTORY +#define MAX_HISTORY 15 +#else +#define MAX_HISTORY CONFIG_FEATURE_COMMAND_HISTORY +#endif + +#if MAX_HISTORY < 1 +#warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off. +#else +static char *history[MAX_HISTORY+1]; /* history + current */ +/* saved history lines */ +static int n_history; +/* current pointer to history line */ +static int cur_history; +#endif + +#include +#define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp) +#define getTermSettings(fd,argp) tcgetattr(fd, argp); + +/* Current termio and the previous termio before starting sh */ +static struct termios initial_settings, new_settings; + + +static +volatile int cmdedit_termw = 80; /* actual terminal width */ +static +volatile int handlers_sets = 0; /* Set next bites: */ + +enum { + SET_ATEXIT = 1, /* when atexit() has been called + and get euid,uid,gid to fast compare */ + SET_WCHG_HANDLERS = 2, /* winchg signal handler */ + SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */ +}; + + +static int cmdedit_x; /* real x terminal position */ +static int cmdedit_y; /* pseudoreal y terminal position */ +static int cmdedit_prmt_len; /* lenght prompt without colores string */ + +static int cursor; /* required global for signal handler */ +static int len; /* --- "" - - "" - -"- --""-- --""--- */ +static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */ +static +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + const +#endif +char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */ + +#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR +static char *user_buf = ""; +static char *home_pwd_buf = ""; +static int my_euid; +#endif + +#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT +static char *hostname_buf; +static int num_ok_lines = 1; +#endif + + +#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION + +#ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR +static int my_euid; +#endif + +static int my_uid; +static int my_gid; + +#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ + +static void cmdedit_setwidth(int w, int redraw_flg); + +static void win_changed(int nsig) +{ + static sighandler_t previous_SIGWINCH_handler; /* for reset */ + + /* emulate || signal call */ + if (nsig == -SIGWINCH || nsig == SIGWINCH) { + int width = 0; + get_terminal_width_height(0, &width, NULL); + cmdedit_setwidth(width, nsig == SIGWINCH); + } + /* Unix not all standart in recall signal */ + + if (nsig == -SIGWINCH) /* save previous handler */ + previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); + else if (nsig == SIGWINCH) /* signaled called handler */ + signal(SIGWINCH, win_changed); /* set for next call */ + else /* nsig == 0 */ + /* set previous handler */ + signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */ +} + +static void cmdedit_reset_term(void) +{ + if ((handlers_sets & SET_RESET_TERM) != 0) { +/* sparc and other have broken termios support: use old termio handling. */ + setTermSettings(STDIN_FILENO, (void *) &initial_settings); + handlers_sets &= ~SET_RESET_TERM; + } + if ((handlers_sets & SET_WCHG_HANDLERS) != 0) { + /* reset SIGWINCH handler to previous (default) */ + win_changed(0); + handlers_sets &= ~SET_WCHG_HANDLERS; + } + fflush(stdout); +} + + +/* special for recount position for scroll and remove terminal margin effect */ +static void cmdedit_set_out_char(int next_char) +{ + + int c = (int)((unsigned char) command_ps[cursor]); + + if (c == 0) + c = ' '; /* destroy end char? */ +#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT + if (!Isprint(c)) { /* Inverse put non-printable characters */ + if (c >= 128) + c -= 128; + if (c < ' ') + c += '@'; + if (c == 127) + c = '?'; + printf("\033[7m%c\033[0m", c); + } else +#endif + putchar(c); + if (++cmdedit_x >= cmdedit_termw) { + /* terminal is scrolled down */ + cmdedit_y++; + cmdedit_x = 0; + + if (!next_char) + next_char = ' '; + /* destroy "(auto)margin" */ + putchar(next_char); + putchar('\b'); + } + cursor++; +} + +/* Move to end line. Bonus: rewrite line from cursor */ +static void input_end(void) +{ + while (cursor < len) + cmdedit_set_out_char(0); +} + +/* Go to the next line */ +static void goto_new_line(void) +{ + input_end(); + if (cmdedit_x) + putchar('\n'); +} + + +static inline void out1str(const char *s) +{ + if ( s ) + fputs(s, stdout); +} + +static inline void beep(void) +{ + putchar('\007'); +} + +/* Move back one character */ +/* special for slow terminal */ +static void input_backward(int num) +{ + if (num > cursor) + num = cursor; + cursor -= num; /* new cursor (in command, not terminal) */ + + if (cmdedit_x >= num) { /* no to up line */ + cmdedit_x -= num; + if (num < 4) + while (num-- > 0) + putchar('\b'); + + else + printf("\033[%dD", num); + } else { + int count_y; + + if (cmdedit_x) { + putchar('\r'); /* back to first terminal pos. */ + num -= cmdedit_x; /* set previous backward */ + } + count_y = 1 + num / cmdedit_termw; + printf("\033[%dA", count_y); + cmdedit_y -= count_y; + /* require forward after uping */ + cmdedit_x = cmdedit_termw * count_y - num; + printf("\033[%dC", cmdedit_x); /* set term cursor */ + } +} + +static void put_prompt(void) +{ + out1str(cmdedit_prompt); + cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ + cursor = 0; + cmdedit_y = 0; /* new quasireal y */ +} + +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT +static void parse_prompt(const char *prmt_ptr) +{ + cmdedit_prompt = prmt_ptr; + cmdedit_prmt_len = strlen(prmt_ptr); + put_prompt(); +} +#else +static void parse_prompt(const char *prmt_ptr) +{ + int prmt_len = 0; + int sub_len = 0; + char flg_not_length = '['; + char *prmt_mem_ptr = xcalloc(1, 1); + char *pwd_buf = xgetcwd(0); + char buf2[PATH_MAX + 1]; + char buf[2]; + char c; + char *pbuf; + + if (!pwd_buf) { + pwd_buf=(char *)bb_msg_unknown; + } + + while (*prmt_ptr) { + pbuf = buf; + pbuf[1] = 0; + c = *prmt_ptr++; + if (c == '\\') { + const char *cp = prmt_ptr; + int l; + + c = bb_process_escape_sequence(&prmt_ptr); + if(prmt_ptr==cp) { + if (*cp == 0) + break; + c = *prmt_ptr++; + switch (c) { +#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR + case 'u': + pbuf = user_buf; + break; +#endif + case 'h': + pbuf = hostname_buf; + if (pbuf == 0) { + pbuf = xcalloc(256, 1); + if (gethostname(pbuf, 255) < 0) { + strcpy(pbuf, "?"); + } else { + char *s = strchr(pbuf, '.'); + + if (s) + *s = 0; + } + hostname_buf = pbuf; + } + break; + case '$': + c = my_euid == 0 ? '#' : '$'; + break; +#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR + case 'w': + pbuf = pwd_buf; + l = strlen(home_pwd_buf); + if (home_pwd_buf[0] != 0 && + strncmp(home_pwd_buf, pbuf, l) == 0 && + (pbuf[l]=='/' || pbuf[l]=='\0') && + strlen(pwd_buf+l) UCHAR_MAX || (pbuf - buf2) < l) { + l--; + break; + } + prmt_ptr++; + } + buf2[l] = 0; + c = (char)strtol(buf2, 0, 16); + if(c==0) + c = '?'; + pbuf = buf; + break; + case '[': case ']': + if (c == flg_not_length) { + flg_not_length = flg_not_length == '[' ? ']' : '['; + continue; + } + break; + } + } + } + if(pbuf == buf) + *pbuf = c; + prmt_len += strlen(pbuf); + prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); + if (flg_not_length == ']') + sub_len++; + } + if(pwd_buf!=(char *)bb_msg_unknown) + free(pwd_buf); + cmdedit_prompt = prmt_mem_ptr; + cmdedit_prmt_len = prmt_len - sub_len; + put_prompt(); +} +#endif + + +/* draw prompt, editor line, and clear tail */ +static void redraw(int y, int back_cursor) +{ + if (y > 0) /* up to start y */ + printf("\033[%dA", y); + putchar('\r'); + put_prompt(); + input_end(); /* rewrite */ + printf("\033[J"); /* destroy tail after cursor */ + input_backward(back_cursor); +} + +/* Delete the char in front of the cursor */ +static void input_delete(void) +{ + int j = cursor; + + if (j == len) + return; + + strcpy(command_ps + j, command_ps + j + 1); + len--; + input_end(); /* rewtite new line */ + cmdedit_set_out_char(0); /* destroy end char */ + input_backward(cursor - j); /* back to old pos cursor */ +} + +/* Delete the char in back of the cursor */ +static void input_backspace(void) +{ + if (cursor > 0) { + input_backward(1); + input_delete(); + } +} + + +/* Move forward one character */ +static void input_forward(void) +{ + if (cursor < len) + cmdedit_set_out_char(command_ps[cursor + 1]); +} + + +static void cmdedit_setwidth(int w, int redraw_flg) +{ + cmdedit_termw = cmdedit_prmt_len + 2; + if (w <= cmdedit_termw) { + cmdedit_termw = cmdedit_termw % w; + } + if (w > cmdedit_termw) { + cmdedit_termw = w; + + if (redraw_flg) { + /* new y for current cursor */ + int new_y = (cursor + cmdedit_prmt_len) / w; + + /* redraw */ + redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor); + fflush(stdout); + } + } +} + +static void cmdedit_init(void) +{ + cmdedit_prmt_len = 0; + if ((handlers_sets & SET_WCHG_HANDLERS) == 0) { + /* emulate usage handler to set handler and call yours work */ + win_changed(-SIGWINCH); + handlers_sets |= SET_WCHG_HANDLERS; + } + + if ((handlers_sets & SET_ATEXIT) == 0) { +#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR + struct passwd *entry; + + my_euid = geteuid(); + entry = getpwuid(my_euid); + if (entry) { + user_buf = bb_xstrdup(entry->pw_name); + home_pwd_buf = bb_xstrdup(entry->pw_dir); + } +#endif + +#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION + +#ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR + my_euid = geteuid(); +#endif + my_uid = getuid(); + my_gid = getgid(); +#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ + handlers_sets |= SET_ATEXIT; + atexit(cmdedit_reset_term); /* be sure to do this only once */ + } +} + +#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION + +static int is_execute(const struct stat *st) +{ + if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) || + (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) || + (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) || + (st->st_mode & S_IXOTH)) return TRUE; + return FALSE; +} + +#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION + +static char **username_tab_completion(char *ud, int *num_matches) +{ + struct passwd *entry; + int userlen; + char *temp; + + + ud++; /* ~user/... to user/... */ + userlen = strlen(ud); + + if (num_matches == 0) { /* "~/..." or "~user/..." */ + char *sav_ud = ud - 1; + char *home = 0; + + if (*ud == '/') { /* "~/..." */ + home = home_pwd_buf; + } else { + /* "~user/..." */ + temp = strchr(ud, '/'); + *temp = 0; /* ~user\0 */ + entry = getpwnam(ud); + *temp = '/'; /* restore ~user/... */ + ud = temp; + if (entry) + home = entry->pw_dir; + } + if (home) { + if ((userlen + strlen(home) + 1) < BUFSIZ) { + char temp2[BUFSIZ]; /* argument size */ + + /* /home/user/... */ + sprintf(temp2, "%s%s", home, ud); + strcpy(sav_ud, temp2); + } + } + return 0; /* void, result save to argument :-) */ + } else { + /* "~[^/]*" */ + char **matches = (char **) NULL; + int nm = 0; + + setpwent(); + + while ((entry = getpwent()) != NULL) { + /* Null usernames should result in all users as possible completions. */ + if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) { + + bb_xasprintf(&temp, "~%s/", entry->pw_name); + matches = xrealloc(matches, (nm + 1) * sizeof(char *)); + + matches[nm++] = temp; + } + } + + endpwent(); + (*num_matches) = nm; + return (matches); + } +} +#endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */ + +enum { + FIND_EXE_ONLY = 0, + FIND_DIR_ONLY = 1, + FIND_FILE_ONLY = 2, +}; + +#ifdef CONFIG_ASH +const char *cmdedit_path_lookup; +#else +#define cmdedit_path_lookup getenv("PATH") +#endif + +static int path_parse(char ***p, int flags) +{ + int npth; + const char *tmp; + const char *pth; + + /* if not setenv PATH variable, to search cur dir "." */ + if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 || + /* PATH= 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] = bb_xstrdup(tmp); + npth = 1; /* count words is + 1 count ':' */ + + for (;;) { + tmp = strchr(tmp, ':'); + if (tmp) { + (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */ + if (*++tmp == 0) + break; /* : */ + } else + break; + (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */ + } + + return npth; +} + +static char *add_quote_for_spec_chars(char *found) +{ + int l = 0; + char *s = xmalloc((strlen(found) + 1) * 2); + + while (*found) { + if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found)) + s[l++] = '\\'; + s[l++] = *found++; + } + s[l] = 0; + return s; +} + +static char **exe_n_cwd_tab_completion(char *command, int *num_matches, + int type) +{ + + char **matches = 0; + DIR *dir; + struct dirent *next; + char dirbuf[BUFSIZ]; + int nm = *num_matches; + struct stat st; + char *path1[1]; + char **paths = path1; + int npaths; + int i; + char *found; + char *pfind = strrchr(command, '/'); + + path1[0] = "."; + + if (pfind == NULL) { + /* no dir, if flags==EXE_ONLY - get paths, else "." */ + npaths = path_parse(&paths, type); + pfind = command; + } else { + /* with dir */ + /* save for change */ + strcpy(dirbuf, command); + /* set dir only */ + dirbuf[(pfind - command) + 1] = 0; +#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION + if (dirbuf[0] == '~') /* ~/... or ~user/... */ + username_tab_completion(dirbuf, 0); +#endif + /* "strip" dirname in command */ + pfind++; + + paths[0] = dirbuf; + npaths = 1; /* only 1 dir */ + } + + for (i = 0; i < npaths; i++) { + + dir = opendir(paths[i]); + if (!dir) /* Don't print an error */ + continue; + + while ((next = readdir(dir)) != NULL) { + char *str_found = next->d_name; + + /* matched ? */ + if (strncmp(str_found, pfind, strlen(pfind))) + continue; + /* not see .name without .match */ + if (*str_found == '.' && *pfind == 0) { + if (*paths[i] == '/' && paths[i][1] == 0 + && str_found[1] == 0) str_found = ""; /* only "/" */ + else + continue; + } + found = concat_path_file(paths[i], str_found); + /* hmm, remover in progress? */ + if (stat(found, &st) < 0) + goto cont; + /* find with dirs ? */ + if (paths[i] != dirbuf) + strcpy(found, next->d_name); /* only name */ + if (S_ISDIR(st.st_mode)) { + /* name is directory */ + str_found = found; + found = concat_path_file(found, ""); + free(str_found); + str_found = add_quote_for_spec_chars(found); + } else { + /* not put found file if search only dirs for cd */ + if (type == FIND_DIR_ONLY) + goto cont; + str_found = add_quote_for_spec_chars(found); + if (type == FIND_FILE_ONLY || + (type == FIND_EXE_ONLY && is_execute(&st))) + strcat(str_found, " "); + } + /* Add it to the list */ + matches = xrealloc(matches, (nm + 1) * sizeof(char *)); + + matches[nm++] = str_found; +cont: + free(found); + } + closedir(dir); + } + if (paths != path1) { + free(paths[0]); /* allocated memory only in first member */ + free(paths); + } + *num_matches = nm; + return (matches); +} + +static int match_compare(const void *a, const void *b) +{ + return strcmp(*(char **) a, *(char **) b); +} + + + +#define QUOT (UCHAR_MAX+1) + +#define collapse_pos(is, in) { \ + memcpy(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \ + memcpy(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); } + +static int find_match(char *matchBuf, int *len_with_quotes) +{ + int i, j; + int command_mode; + int c, c2; + int int_buf[BUFSIZ + 1]; + int pos_buf[BUFSIZ + 1]; + + /* set to integer dimension characters and own positions */ + for (i = 0;; i++) { + int_buf[i] = (int) ((unsigned char) matchBuf[i]); + if (int_buf[i] == 0) { + pos_buf[i] = -1; /* indicator end line */ + break; + } else + pos_buf[i] = i; + } + + /* mask \+symbol and convert '\t' to ' ' */ + for (i = j = 0; matchBuf[i]; i++, j++) + if (matchBuf[i] == '\\') { + collapse_pos(j, j + 1); + int_buf[j] |= QUOT; + i++; +#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT + if (matchBuf[i] == '\t') /* algorithm equivalent */ + int_buf[j] = ' ' | QUOT; +#endif + } +#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT + else if (matchBuf[i] == '\t') + int_buf[j] = ' '; +#endif + + /* mask "symbols" or 'symbols' */ + c2 = 0; + for (i = 0; int_buf[i]; i++) { + c = int_buf[i]; + if (c == '\'' || c == '"') { + if (c2 == 0) + c2 = c; + else { + if (c == c2) + c2 = 0; + else + int_buf[i] |= QUOT; + } + } else if (c2 != 0 && c != '$') + int_buf[i] |= QUOT; + } + + /* skip commands with arguments if line have commands delimiters */ + /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ + for (i = 0; int_buf[i]; i++) { + c = int_buf[i]; + c2 = int_buf[i + 1]; + j = i ? int_buf[i - 1] : -1; + command_mode = 0; + if (c == ';' || c == '&' || c == '|') { + command_mode = 1 + (c == c2); + if (c == '&') { + if (j == '>' || j == '<') + command_mode = 0; + } else if (c == '|' && j == '>') + command_mode = 0; + } + if (command_mode) { + collapse_pos(0, i + command_mode); + i = -1; /* hack incremet */ + } + } + /* collapse `command...` */ + for (i = 0; int_buf[i]; i++) + if (int_buf[i] == '`') { + for (j = i + 1; int_buf[j]; j++) + if (int_buf[j] == '`') { + collapse_pos(i, j + 1); + j = 0; + break; + } + if (j) { + /* not found close ` - command mode, collapse all previous */ + collapse_pos(0, i + 1); + break; + } else + i--; /* hack incremet */ + } + + /* collapse (command...(command...)...) or {command...{command...}...} */ + c = 0; /* "recursive" level */ + c2 = 0; + for (i = 0; int_buf[i]; i++) + if (int_buf[i] == '(' || int_buf[i] == '{') { + if (int_buf[i] == '(') + c++; + else + c2++; + collapse_pos(0, i + 1); + i = -1; /* hack incremet */ + } + for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) + if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { + if (int_buf[i] == ')') + c--; + else + c2--; + collapse_pos(0, i + 1); + i = -1; /* hack incremet */ + } + + /* skip first not quote space */ + for (i = 0; int_buf[i]; i++) + if (int_buf[i] != ' ') + break; + if (i) + collapse_pos(0, i); + + /* set find mode for completion */ + command_mode = FIND_EXE_ONLY; + for (i = 0; int_buf[i]; i++) + if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { + if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY + && matchBuf[pos_buf[0]]=='c' + && matchBuf[pos_buf[1]]=='d' ) + command_mode = FIND_DIR_ONLY; + else { + command_mode = FIND_FILE_ONLY; + break; + } + } + /* "strlen" */ + for (i = 0; int_buf[i]; i++); + /* find last word */ + for (--i; i >= 0; i--) { + c = int_buf[i]; + if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { + collapse_pos(0, i + 1); + break; + } + } + /* skip first not quoted '\'' or '"' */ + for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++); + /* collapse quote or unquote // or /~ */ + while ((int_buf[i] & ~QUOT) == '/' && + ((int_buf[i + 1] & ~QUOT) == '/' + || (int_buf[i + 1] & ~QUOT) == '~')) { + i++; + } + + /* set only match and destroy quotes */ + j = 0; + for (c = 0; pos_buf[i] >= 0; i++) { + matchBuf[c++] = matchBuf[pos_buf[i]]; + j = pos_buf[i] + 1; + } + matchBuf[c] = 0; + /* old lenght matchBuf with quotes symbols */ + *len_with_quotes = j ? j - pos_buf[0] : 0; + + return command_mode; +} + +/* + display by column original ideas from ls applet, + very optimize by my :) +*/ +static void showfiles(char **matches, int nfiles) +{ + int ncols, row; + int column_width = 0; + int nrows = nfiles; + + /* find the longest file name- use that as the column width */ + for (row = 0; row < nrows; row++) { + int l = strlen(matches[row]); + + if (column_width < l) + column_width = l; + } + column_width += 2; /* min space for columns */ + ncols = cmdedit_termw / column_width; + + if (ncols > 1) { + nrows /= ncols; + if(nfiles % ncols) + nrows++; /* round up fractionals */ + column_width = -column_width; /* for printf("%-Ns", ...); */ + } else { + ncols = 1; + } + for (row = 0; row < nrows; row++) { + int n = row; + int nc; + + for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) + printf("%*s", column_width, matches[n]); + printf("%s\n", matches[n]); + } +} + + +static void input_tab(int *lastWasTab) +{ + /* Do TAB completion */ + static int num_matches; + static char **matches; + + if (lastWasTab == 0) { /* free all memory */ + if (matches) { + while (num_matches > 0) + free(matches[--num_matches]); + free(matches); + matches = (char **) NULL; + } + return; + } + if (! *lastWasTab) { + + char *tmp; + int len_found; + char matchBuf[BUFSIZ]; + int find_type; + int recalc_pos; + + *lastWasTab = TRUE; /* flop trigger */ + + /* Make a local copy of the string -- up + * to the position of the cursor */ + tmp = strncpy(matchBuf, command_ps, cursor); + tmp[cursor] = 0; + + find_type = find_match(matchBuf, &recalc_pos); + + /* Free up any memory already allocated */ + input_tab(0); + +#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION + /* If the word starts with `~' and there is no slash in the word, + * then try completing this word as a username. */ + + if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) + matches = username_tab_completion(matchBuf, &num_matches); +#endif + /* Try to match any executable in our path and everything + * in the current working directory that matches. */ + if (!matches) + matches = + exe_n_cwd_tab_completion(matchBuf, + &num_matches, find_type); + /* Remove duplicate found */ + if(matches) { + int i, j; + /* bubble */ + for(i=0; i<(num_matches-1); i++) + for(j=i+1; j 1) { + char *tmp1; + + beep(); + if (!matches) + return; /* not found */ + /* sort */ + qsort(matches, num_matches, sizeof(char *), match_compare); + + /* find minimal match */ + tmp = bb_xstrdup(matches[0]); + for (tmp1 = tmp; *tmp1; tmp1++) + for (len_found = 1; len_found < num_matches; len_found++) + if (matches[len_found][(tmp1 - tmp)] != *tmp1) { + *tmp1 = 0; + break; + } + if (*tmp == 0) { /* have unique */ + free(tmp); + return; + } + } else { /* one match */ + tmp = matches[0]; + /* for next completion current found */ + *lastWasTab = FALSE; + } + + len_found = strlen(tmp); + /* have space to placed match? */ + if ((len_found - strlen(matchBuf) + len) < BUFSIZ) { + + /* before word for match */ + command_ps[cursor - recalc_pos] = 0; + /* save tail line */ + strcpy(matchBuf, command_ps + cursor); + /* add match */ + strcat(command_ps, tmp); + /* add tail */ + strcat(command_ps, matchBuf); + /* back to begin word for match */ + input_backward(recalc_pos); + /* new pos */ + recalc_pos = cursor + len_found; + /* new len */ + len = strlen(command_ps); + /* write out the matched command */ + redraw(cmdedit_y, len - recalc_pos); + } + if (tmp != matches[0]) + free(tmp); + } else { + /* Ok -- the last char was a TAB. Since they + * just hit TAB again, print a list of all the + * available choices... */ + if (matches && num_matches > 0) { + int sav_cursor = cursor; /* change goto_new_line() */ + + /* Go to the next line */ + goto_new_line(); + showfiles(matches, num_matches); + redraw(0, len - sav_cursor); + } + } +} +#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */ + +#if MAX_HISTORY >= 1 +static void get_previous_history(void) +{ + if(command_ps[0] != 0 || history[cur_history] == 0) { + free(history[cur_history]); + history[cur_history] = bb_xstrdup(command_ps); + } + cur_history--; +} + +static int get_next_history(void) +{ + int ch = cur_history; + + if (ch < n_history) { + get_previous_history(); /* save the current history line */ + return (cur_history = ch+1); + } else { + beep(); + return 0; + } +} + +#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY +extern void load_history ( const char *fromfile ) +{ + FILE *fp; + int hi; + + /* cleanup old */ + + for(hi = n_history; hi > 0; ) { + hi--; + free ( history [hi] ); + } + + if (( fp = fopen ( fromfile, "r" ))) { + + for ( hi = 0; hi < MAX_HISTORY; ) { + char * hl = bb_get_chomped_line_from_file(fp); + int l; + + if(!hl) + break; + l = strlen(hl); + if(l >= BUFSIZ) + hl[BUFSIZ-1] = 0; + if(l == 0 || hl[0] == ' ') { + free(hl); + continue; + } + history [hi++] = hl; + } + fclose ( fp ); + } + cur_history = n_history = hi; +} + +extern void save_history ( const char *tofile ) +{ + FILE *fp = fopen ( tofile, "w" ); + + if ( fp ) { + int i; + + for ( i = 0; i < n_history; i++ ) { + fprintf(fp, "%s\n", history [i]); + } + fclose ( fp ); + } +} +#endif + +#endif + +enum { + ESC = 27, + DEL = 127, +}; + + +/* + * This function is used to grab a character buffer + * from the input file descriptor and allows you to + * a string with full command editing (sort of like + * a mini readline). + * + * The following standard commands are not implemented: + * ESC-b -- Move back one word + * ESC-f -- Move forward one word + * ESC-d -- Delete back one word + * ESC-h -- Delete forward one word + * CTL-t -- Transpose two characters + * + * Furthermore, the "vi" command editing keys are not implemented. + * + */ + + +int cmdedit_read_input(char *prompt, char command[BUFSIZ]) +{ + + int break_out = 0; + int lastWasTab = FALSE; + unsigned char c = 0; + + /* prepare before init handlers */ + cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */ + len = 0; + command_ps = command; + + getTermSettings(0, (void *) &initial_settings); + memcpy(&new_settings, &initial_settings, sizeof(struct termios)); + new_settings.c_lflag &= ~ICANON; /* unbuffered input */ + /* Turn off echoing and CTRL-C, so we can trap it */ + new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG); + /* Hmm, in linux c_cc[] not parsed if set ~ICANON */ + new_settings.c_cc[VMIN] = 1; + new_settings.c_cc[VTIME] = 0; + /* Turn off CTRL-C, so we can trap it */ +# ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE '\0' +# endif + new_settings.c_cc[VINTR] = _POSIX_VDISABLE; + command[0] = 0; + + setTermSettings(0, (void *) &new_settings); + handlers_sets |= SET_RESET_TERM; + + /* Now initialize things */ + cmdedit_init(); + /* Print out the command prompt */ + parse_prompt(prompt); + + while (1) { + + fflush(stdout); /* buffered out to fast */ + + if (safe_read(0, &c, 1) < 1) + /* if we can't read input then exit */ + goto prepare_to_die; + + switch (c) { + case '\n': + case '\r': + /* Enter */ + goto_new_line(); + break_out = 1; + break; + case 1: + /* Control-a -- Beginning of line */ + input_backward(cursor); + break; + case 2: + /* Control-b -- Move back one character */ + input_backward(1); + break; + case 3: + /* Control-c -- stop gathering input */ + goto_new_line(); +#ifndef CONFIG_ASH + command[0] = 0; + len = 0; + lastWasTab = FALSE; + put_prompt(); +#else + len = 0; + break_out = -1; /* to control traps */ +#endif + break; + case 4: + /* Control-d -- Delete one character, or exit + * if the len=0 and no chars to delete */ + if (len == 0) { + errno = 0; +prepare_to_die: +#if !defined(CONFIG_ASH) + printf("exit"); + goto_new_line(); + /* cmdedit_reset_term() called in atexit */ + exit(EXIT_SUCCESS); +#else + /* to control stopped jobs */ + len = break_out = -1; + break; +#endif + } else { + input_delete(); + } + break; + case 5: + /* Control-e -- End of line */ + input_end(); + break; + case 6: + /* Control-f -- Move forward one character */ + input_forward(); + break; + case '\b': + case DEL: + /* Control-h and DEL */ + input_backspace(); + break; + case '\t': +#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION + input_tab(&lastWasTab); +#endif + break; + case 11: + /* Control-k -- clear to end of line */ + *(command + cursor) = 0; + len = cursor; + printf("\033[J"); + break; + case 12: + /* Control-l -- clear screen */ + printf("\033[H"); + redraw(0, len-cursor); + break; +#if MAX_HISTORY >= 1 + case 14: + /* Control-n -- Get next command in history */ + if (get_next_history()) + goto rewrite_line; + break; + case 16: + /* Control-p -- Get previous command from history */ + if (cur_history > 0) { + get_previous_history(); + goto rewrite_line; + } else { + beep(); + } + break; +#endif + case 21: + /* Control-U -- Clear line before cursor */ + if (cursor) { + strcpy(command, command + cursor); + redraw(cmdedit_y, len -= cursor); + } + break; + case 23: + /* Control-W -- Remove the last word */ + while (cursor > 0 && isspace(command[cursor-1])) + input_backspace(); + while (cursor > 0 &&!isspace(command[cursor-1])) + input_backspace(); + break; + case ESC:{ + /* escape sequence follows */ + if (safe_read(0, &c, 1) < 1) + goto prepare_to_die; + /* different vt100 emulations */ + if (c == '[' || c == 'O') { + if (safe_read(0, &c, 1) < 1) + goto prepare_to_die; + } + if (c >= '1' && c <= '9') { + unsigned char dummy; + + if (safe_read(0, &dummy, 1) < 1) + goto prepare_to_die; + if(dummy != '~') + c = 0; + } + switch (c) { +#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION + case '\t': /* Alt-Tab */ + + input_tab(&lastWasTab); + break; +#endif +#if MAX_HISTORY >= 1 + case 'A': + /* Up Arrow -- Get previous command from history */ + if (cur_history > 0) { + get_previous_history(); + goto rewrite_line; + } else { + beep(); + } + break; + case 'B': + /* Down Arrow -- Get next command in history */ + if (!get_next_history()) + break; + /* Rewrite the line with the selected history item */ +rewrite_line: + /* change command */ + len = strlen(strcpy(command, history[cur_history])); + /* redraw and go to end line */ + redraw(cmdedit_y, 0); + break; +#endif + case 'C': + /* Right Arrow -- Move forward one character */ + input_forward(); + break; + case 'D': + /* Left Arrow -- Move back one character */ + input_backward(1); + break; + case '3': + /* Delete */ + input_delete(); + break; + case '1': + case 'H': + /* */ + input_backward(cursor); + break; + case '4': + case 'F': + /* */ + input_end(); + break; + default: + c = 0; + beep(); + } + break; + } + + default: /* If it's regular input, do the normal thing */ +#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT + /* Control-V -- Add non-printable symbol */ + if (c == 22) { + if (safe_read(0, &c, 1) < 1) + goto prepare_to_die; + if (c == 0) { + beep(); + break; + } + } else +#endif + if (!Isprint(c)) /* Skip non-printable characters */ + break; + + if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ + break; + + len++; + + if (cursor == (len - 1)) { /* Append if at the end of the line */ + *(command + cursor) = c; + *(command + cursor + 1) = 0; + cmdedit_set_out_char(0); + } else { /* Insert otherwise */ + int sc = cursor; + + memmove(command + sc + 1, command + sc, len - sc); + *(command + sc) = c; + sc++; + /* rewrite from cursor */ + input_end(); + /* to prev x pos + 1 */ + input_backward(cursor - sc); + } + + break; + } + if (break_out) /* Enter is the command terminator, no more input. */ + break; + + if (c != '\t') + lastWasTab = FALSE; + } + + setTermSettings(0, (void *) &initial_settings); + handlers_sets &= ~SET_RESET_TERM; + +#if MAX_HISTORY >= 1 + /* Handle command history log */ + /* cleanup may be saved current command line */ + if (len> 0) { /* no put empty line */ + int i = n_history; + + free(history[MAX_HISTORY]); + history[MAX_HISTORY] = 0; + /* After max history, remove the oldest command */ + if (i >= MAX_HISTORY) { + free(history[0]); + for(i = 0; i < (MAX_HISTORY-1); i++) + history[i] = history[i+1]; + } + history[i++] = bb_xstrdup(command); + cur_history = i; + n_history = i; +#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT) + num_ok_lines++; +#endif + } +#else /* MAX_HISTORY < 1 */ +#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT) + if (len > 0) { /* no put empty line */ + num_ok_lines++; + } +#endif +#endif /* MAX_HISTORY >= 1 */ + if (break_out > 0) { + command[len++] = '\n'; /* set '\n' */ + command[len] = 0; + } +#if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION) + input_tab(0); /* strong free */ +#endif +#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT) + free(cmdedit_prompt); +#endif + cmdedit_reset_term(); + return len; +} + + + +#endif /* CONFIG_FEATURE_COMMAND_EDITING */ + + +#ifdef TEST + +const char *bb_applet_name = "debug stuff usage"; + +#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT +#include +#endif + +int main(int argc, char **argv) +{ + char buff[BUFSIZ]; + char *prompt = +#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT) + "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\ +\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \ +\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]"; +#else + "% "; +#endif + +#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT + setlocale(LC_ALL, ""); +#endif + while(1) { + int l; + l = cmdedit_read_input(prompt, buff); + if(l > 0 && buff[l-1] == '\n') { + buff[l-1] = 0; + printf("*** cmdedit_read_input() returned line =%s=\n", buff); + } else { + break; + } + } + printf("*** cmdedit_read_input() detect ^D\n"); + return 0; +} + +#endif /* TEST */ diff --git a/busybox/shell/cmdedit.h b/busybox/shell/cmdedit.h new file mode 100644 index 000000000..4c0c09d76 --- /dev/null +++ b/busybox/shell/cmdedit.h @@ -0,0 +1,15 @@ +#ifndef CMDEDIT_H +#define CMDEDIT_H + +int cmdedit_read_input(char* promptStr, char* command); + +#ifdef CONFIG_ASH +extern const char *cmdedit_path_lookup; +#endif + +#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY +void load_history ( const char *fromfile ); +void save_history ( const char *tofile ); +#endif + +#endif /* CMDEDIT_H */ diff --git a/busybox/shell/hush.c b/busybox/shell/hush.c new file mode 100644 index 000000000..49e2397ad --- /dev/null +++ b/busybox/shell/hush.c @@ -0,0 +1,2963 @@ +/* vi: set sw=4 ts=4: */ +/* + * sh.c -- a prototype Bourne shell grammar parser + * Intended to follow the original Thompson and Ritchie + * "small and simple is beautiful" philosophy, which + * incidentally is a good match to today's BusyBox. + * + * Copyright (C) 2000,2001 Larry Doolittle + * + * Credits: + * The parser routines proper are all original material, first + * written Dec 2000 and Jan 2001 by Larry Doolittle. The + * execution engine, the builtins, and much of the underlying + * support has been adapted from busybox-0.49pre's lash, which is + * Copyright (C) 1999-2004 by Erik Andersen + * 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 */ + +#if 1 +#include "busybox.h" +#include "cmdedit.h" +#else +#define bb_applet_name "hush" +#include "standalone.h" +#define hush_main main +#undef CONFIG_FEATURE_SH_FANCY_PROMPT +#define BB_BANNER +#endif +#define SPECIAL_VAR_SYMBOL 03 +#define FLAG_EXIT_FROM_LOOP 1 +#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */ +#define FLAG_REPARSING (1 << 2) /* >=2nd pass */ + +typedef enum { + REDIRECT_INPUT = 1, + REDIRECT_OVERWRITE = 2, + REDIRECT_APPEND = 3, + REDIRECT_HEREIS = 4, + REDIRECT_IO = 5 +} redir_type; + +/* The descrip member of this structure is only used to make debugging + * output pretty */ +struct {int mode; int default_fd; char *descrip;} redir_table[] = { + { 0, 0, "()" }, + { O_RDONLY, 0, "<" }, + { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, + { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, + { O_RDONLY, -1, "<<" }, + { O_RDWR, 1, "<>" } +}; + +typedef enum { + PIPE_SEQ = 1, + PIPE_AND = 2, + PIPE_OR = 3, + PIPE_BG = 4, +} pipe_style; + +/* might eventually control execution */ +typedef enum { + RES_NONE = 0, + RES_IF = 1, + RES_THEN = 2, + RES_ELIF = 3, + RES_ELSE = 4, + RES_FI = 5, + RES_FOR = 6, + RES_WHILE = 7, + RES_UNTIL = 8, + RES_DO = 9, + RES_DONE = 10, + RES_XXXX = 11, + RES_IN = 12, + RES_SNTX = 13 +} reserved_style; +#define FLAG_END (1<, but protected with __USE_GNU */ + +/* "globals" within this file */ +static char *ifs; +static char map[256]; +static int fake_mode; +static int interactive; +static struct close_me *close_me_head; +static const char *cwd; +static struct pipe *job_list; +static unsigned int last_bg_pid; +static unsigned int last_jobid; +static unsigned int shell_terminal; +static char *PS1; +static char *PS2; +struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; +struct variables *top_vars = &shell_ver; + + +#define B_CHUNK (100) +#define B_NOSPAC 1 + +typedef struct { + char *data; + int length; + int maxlen; + int quote; + int nonnull; +} o_string; +#define NULL_O_STRING {NULL,0,0,0,0} +/* used for initialization: + o_string foo = NULL_O_STRING; */ + +/* I can almost use ordinary FILE *. Is open_memstream() universally + * available? Where is it documented? */ +struct in_str { + const char *p; + char peek_buf[2]; + int __promptme; + int promptmode; + FILE *file; + int (*get) (struct in_str *); + int (*peek) (struct in_str *); +}; +#define b_getch(input) ((input)->get(input)) +#define b_peek(input) ((input)->peek(input)) + +#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" + +struct built_in_command { + char *cmd; /* name */ + char *descr; /* description */ + int (*function) (struct child_prog *); /* function ptr */ +}; + +/* belongs in busybox.h */ +static inline int max(int a, int b) { + return (a>b)?a:b; +} + +/* This should be in utility.c */ +#ifdef DEBUG_SHELL +static void debug_printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} +#else +static inline void debug_printf(const char *format, ...) { } +#endif +#define final_printf debug_printf + +static void __syntax(char *file, int line) { + bb_error_msg("syntax error %s:%d", file, line); +} +#define syntax() __syntax(__FILE__, __LINE__) + +/* Index of subroutines: */ +/* function prototypes for builtins */ +static int builtin_cd(struct child_prog *child); +static int builtin_env(struct child_prog *child); +static int builtin_eval(struct child_prog *child); +static int builtin_exec(struct child_prog *child); +static int builtin_exit(struct child_prog *child); +static int builtin_export(struct child_prog *child); +static int builtin_fg_bg(struct child_prog *child); +static int builtin_help(struct child_prog *child); +static int builtin_jobs(struct child_prog *child); +static int builtin_pwd(struct child_prog *child); +static int builtin_read(struct child_prog *child); +static int builtin_set(struct child_prog *child); +static int builtin_shift(struct child_prog *child); +static int builtin_source(struct child_prog *child); +static int builtin_umask(struct child_prog *child); +static int builtin_unset(struct child_prog *child); +static int builtin_not_written(struct child_prog *child); +/* o_string manipulation: */ +static int b_check_space(o_string *o, int len); +static int b_addchr(o_string *o, int ch); +static void b_reset(o_string *o); +static int b_addqchr(o_string *o, int ch, int quote); +static int b_adduint(o_string *o, unsigned int i); +/* in_str manipulations: */ +static int static_get(struct in_str *i); +static int static_peek(struct in_str *i); +static int file_get(struct in_str *i); +static int file_peek(struct in_str *i); +static void setup_file_in_str(struct in_str *i, FILE *f); +static void setup_string_in_str(struct in_str *i, const char *s); +/* close_me manipulations: */ +static void mark_open(int fd); +static void mark_closed(int fd); +static void close_all(void); +/* "run" the final data structures: */ +static char *indenter(int i); +static int free_pipe_list(struct pipe *head, int indent); +static int free_pipe(struct pipe *pi, int indent); +/* really run the final data structures: */ +static int setup_redirects(struct child_prog *prog, int squirrel[]); +static int run_list_real(struct pipe *pi); +static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn)); +static int run_pipe_real(struct pipe *pi); +/* extended glob support: */ +static int globhack(const char *src, int flags, glob_t *pglob); +static int glob_needed(const char *s); +static int xglob(o_string *dest, int flags, glob_t *pglob); +/* variable assignment: */ +static int is_assignment(const char *s); +/* data structure manipulation: */ +static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input); +static void initialize_context(struct p_context *ctx); +static int done_word(o_string *dest, struct p_context *ctx); +static int done_command(struct p_context *ctx); +static int done_pipe(struct p_context *ctx, pipe_style type); +/* primary string parsing: */ +static int redirect_dup_num(struct in_str *input); +static int redirect_opt_num(o_string *o); +static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end); +static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); +static char *lookup_param(char *src); +static char *make_string(char **inp); +static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); +static int parse_string(o_string *dest, struct p_context *ctx, const char *src); +static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger); +/* setup: */ +static int parse_stream_outer(struct in_str *inp, int flag); +static int parse_string_outer(const char *s, int flag); +static int parse_file_outer(FILE *f); +/* job management: */ +static int checkjobs(struct pipe* fg_pipe); +static void insert_bg_job(struct pipe *pi); +static void remove_bg_job(struct pipe *pi); +/* local variable support */ +static char **make_list_in(char **inp, char *name); +static char *insert_var_value(char *inp); +static char *get_local_var(const char *var); +static void unset_local_var(const char *name); +static int set_local_var(const char *s, int flg_export); + +/* Table of built-in functions. They can be forked or not, depending on + * context: within pipes, they fork. As simple commands, they do not. + * When used in non-forking context, they can change global variables + * in the parent shell process. If forked, of course they can not. + * For example, 'unset foo | whatever' will parse and run, but foo will + * still be set at the end. */ +static struct built_in_command bltins[] = { + {"bg", "Resume a job in the background", builtin_fg_bg}, + {"break", "Exit for, while or until loop", builtin_not_written}, + {"cd", "Change working directory", builtin_cd}, + {"continue", "Continue for, while or until loop", builtin_not_written}, + {"env", "Print all environment variables", builtin_env}, + {"eval", "Construct and run shell command", builtin_eval}, + {"exec", "Exec command, replacing this shell with the exec'd process", + builtin_exec}, + {"exit", "Exit from shell()", builtin_exit}, + {"export", "Set environment variable", builtin_export}, + {"fg", "Bring job into the foreground", builtin_fg_bg}, + {"jobs", "Lists the active jobs", builtin_jobs}, + {"pwd", "Print current directory", builtin_pwd}, + {"read", "Input environment variable", builtin_read}, + {"return", "Return from a function", builtin_not_written}, + {"set", "Set/unset shell local variables", builtin_set}, + {"shift", "Shift positional parameters", builtin_shift}, + {"trap", "Trap signals", builtin_not_written}, + {"ulimit","Controls resource limits", builtin_not_written}, + {"umask","Sets file creation mask", builtin_umask}, + {"unset", "Unset environment variable", builtin_unset}, + {".", "Source-in and run commands in a file", builtin_source}, + {"help", "List shell built-in commands", builtin_help}, + {NULL, NULL, NULL} +}; + +static const char *set_cwd(void) +{ + if(cwd==bb_msg_unknown) + cwd = NULL; /* xgetcwd(arg) called free(arg) */ + cwd = xgetcwd((char *)cwd); + if (!cwd) + cwd = bb_msg_unknown; + return cwd; +} + +/* built-in 'eval' handler */ +static int builtin_eval(struct child_prog *child) +{ + char *str = NULL; + int rcode = EXIT_SUCCESS; + + if (child->argv[1]) { + str = make_string(child->argv + 1); + parse_string_outer(str, FLAG_EXIT_FROM_LOOP | + FLAG_PARSE_SEMICOLON); + free(str); + rcode = last_return_code; + } + return rcode; +} + +/* built-in 'cd ' handler */ +static int builtin_cd(struct child_prog *child) +{ + char *newdir; + if (child->argv[1] == NULL) + newdir = getenv("HOME"); + else + newdir = child->argv[1]; + if (chdir(newdir)) { + printf("cd: %s: %s\n", newdir, strerror(errno)); + return EXIT_FAILURE; + } + set_cwd(); + return EXIT_SUCCESS; +} + +/* built-in 'env' handler */ +static int builtin_env(struct child_prog *dummy) +{ + char **e = environ; + if (e == NULL) return EXIT_FAILURE; + for (; *e; e++) { + puts(*e); + } + return EXIT_SUCCESS; +} + +/* built-in 'exec' handler */ +static int builtin_exec(struct child_prog *child) +{ + if (child->argv[1] == NULL) + return EXIT_SUCCESS; /* Really? */ + child->argv++; + pseudo_exec(child); + /* never returns */ +} + +/* built-in 'exit' handler */ +static int builtin_exit(struct child_prog *child) +{ + if (child->argv[1] == NULL) + exit(last_return_code); + exit (atoi(child->argv[1])); +} + +/* built-in 'export VAR=value' handler */ +static int builtin_export(struct child_prog *child) +{ + int res = 0; + char *name = child->argv[1]; + + if (name == NULL) { + return (builtin_env(child)); + } + + name = strdup(name); + + if(name) { + char *value = strchr(name, '='); + + if (!value) { + char *tmp; + /* They are exporting something without an =VALUE */ + + value = get_local_var(name); + if (value) { + size_t ln = strlen(name); + + tmp = realloc(name, ln+strlen(value)+2); + if(tmp==NULL) + res = -1; + else { + sprintf(tmp+ln, "=%s", value); + name = tmp; + } + } else { + /* bash does not return an error when trying to export + * an undefined variable. Do likewise. */ + res = 1; + } + } + } + if (res<0) + bb_perror_msg("export"); + else if(res==0) + res = set_local_var(name, 1); + else + res = 0; + free(name); + return res; +} + +/* built-in 'fg' and 'bg' handler */ +static int builtin_fg_bg(struct child_prog *child) +{ + int i, jobnum; + struct pipe *pi=NULL; + + if (!interactive) + return EXIT_FAILURE; + /* If they gave us no args, assume they want the last backgrounded task */ + if (!child->argv[1]) { + for (pi = job_list; pi; pi = pi->next) { + if (pi->jobid == last_jobid) { + break; + } + } + if (!pi) { + bb_error_msg("%s: no current job", child->argv[0]); + return EXIT_FAILURE; + } + } else { + if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { + bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); + return EXIT_FAILURE; + } + for (pi = job_list; pi; pi = pi->next) { + if (pi->jobid == jobnum) { + break; + } + } + if (!pi) { + bb_error_msg("%s: %d: no such job", child->argv[0], jobnum); + return EXIT_FAILURE; + } + } + + if (*child->argv[0] == 'f') { + /* Put the job into the foreground. */ + tcsetpgrp(shell_terminal, pi->pgrp); + } + + /* Restart the processes in the job */ + for (i = 0; i < pi->num_progs; i++) + pi->progs[i].is_stopped = 0; + + if ( (i=kill(- pi->pgrp, SIGCONT)) < 0) { + if (i == ESRCH) { + remove_bg_job(pi); + } else { + bb_perror_msg("kill (SIGCONT)"); + } + } + + pi->stopped_progs = 0; + return EXIT_SUCCESS; +} + +/* built-in 'help' handler */ +static int builtin_help(struct child_prog *dummy) +{ + struct built_in_command *x; + + printf("\nBuilt-in commands:\n"); + printf("-------------------\n"); + for (x = bltins; x->cmd; x++) { + if (x->descr==NULL) + continue; + printf("%s\t%s\n", x->cmd, x->descr); + } + printf("\n\n"); + return EXIT_SUCCESS; +} + +/* built-in 'jobs' handler */ +static int builtin_jobs(struct child_prog *child) +{ + struct pipe *job; + char *status_string; + + for (job = job_list; job; job = job->next) { + if (job->running_progs == job->stopped_progs) + status_string = "Stopped"; + else + status_string = "Running"; + + printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text); + } + return EXIT_SUCCESS; +} + + +/* built-in 'pwd' handler */ +static int builtin_pwd(struct child_prog *dummy) +{ + puts(set_cwd()); + return EXIT_SUCCESS; +} + +/* built-in 'read VAR' handler */ +static int builtin_read(struct child_prog *child) +{ + int res; + + if (child->argv[1]) { + char string[BUFSIZ]; + char *var = 0; + + string[0] = 0; /* In case stdin has only EOF */ + /* read string */ + fgets(string, sizeof(string), stdin); + chomp(string); + var = malloc(strlen(child->argv[1])+strlen(string)+2); + if(var) { + sprintf(var, "%s=%s", child->argv[1], string); + res = set_local_var(var, 0); + } else + res = -1; + if (res) + fprintf(stderr, "read: %m\n"); + free(var); /* So not move up to avoid breaking errno */ + return res; + } else { + do res=getchar(); while(res!='\n' && res!=EOF); + return 0; + } +} + +/* built-in 'set VAR=value' handler */ +static int builtin_set(struct child_prog *child) +{ + char *temp = child->argv[1]; + struct variables *e; + + if (temp == NULL) + for(e = top_vars; e; e=e->next) + printf("%s=%s\n", e->name, e->value); + else + set_local_var(temp, 0); + + return EXIT_SUCCESS; +} + + +/* Built-in 'shift' handler */ +static int builtin_shift(struct child_prog *child) +{ + int n=1; + if (child->argv[1]) { + n=atoi(child->argv[1]); + } + if (n>=0 && nargv[1] == NULL) + return EXIT_FAILURE; + + /* XXX search through $PATH is missing */ + input = fopen(child->argv[1], "r"); + if (!input) { + bb_error_msg("Couldn't open file '%s'", child->argv[1]); + return EXIT_FAILURE; + } + + /* Now run the file */ + /* XXX argv and argc are broken; need to save old global_argv + * (pointer only is OK!) on this stack frame, + * set global_argv=child->argv+1, recurse, and restore. */ + mark_open(fileno(input)); + status = parse_file_outer(input); + mark_closed(fileno(input)); + fclose(input); + return (status); +} + +static int builtin_umask(struct child_prog *child) +{ + mode_t new_umask; + const char *arg = child->argv[1]; + char *end; + if (arg) { + new_umask=strtoul(arg, &end, 8); + if (*end!='\0' || end == arg) { + return EXIT_FAILURE; + } + } else { + printf("%.3o\n", (unsigned int) (new_umask=umask(0))); + } + umask(new_umask); + return EXIT_SUCCESS; +} + +/* built-in 'unset VAR' handler */ +static int builtin_unset(struct child_prog *child) +{ + /* bash returned already true */ + unset_local_var(child->argv[1]); + return EXIT_SUCCESS; +} + +static int builtin_not_written(struct child_prog *child) +{ + printf("builtin_%s not written\n",child->argv[0]); + return EXIT_FAILURE; +} + +static int b_check_space(o_string *o, int len) +{ + /* It would be easy to drop a more restrictive policy + * in here, such as setting a maximum string length */ + if (o->length + len > o->maxlen) { + char *old_data = o->data; + /* assert (data == NULL || o->maxlen != 0); */ + o->maxlen += max(2*len, B_CHUNK); + o->data = realloc(o->data, 1 + o->maxlen); + if (o->data == NULL) { + free(old_data); + } + } + return o->data == NULL; +} + +static int b_addchr(o_string *o, int ch) +{ + debug_printf("b_addchr: %c %d %p\n", ch, o->length, o); + if (b_check_space(o, 1)) return B_NOSPAC; + o->data[o->length] = ch; + o->length++; + o->data[o->length] = '\0'; + return 0; +} + +static void b_reset(o_string *o) +{ + o->length = 0; + o->nonnull = 0; + if (o->data != NULL) *o->data = '\0'; +} + +static void b_free(o_string *o) +{ + b_reset(o); + free(o->data); + o->data = NULL; + o->maxlen = 0; +} + +/* My analysis of quoting semantics tells me that state information + * is associated with a destination, not a source. + */ +static int b_addqchr(o_string *o, int ch, int quote) +{ + if (quote && strchr("*?[\\",ch)) { + int rc; + rc = b_addchr(o, '\\'); + if (rc) return rc; + } + return b_addchr(o, ch); +} + +/* belongs in utility.c */ +char *simple_itoa(unsigned int i) +{ + /* 21 digits plus null terminator, good for 64-bit or smaller ints */ + static char local[22]; + char *p = &local[21]; + *p-- = '\0'; + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + return p + 1; +} + +static int b_adduint(o_string *o, unsigned int i) +{ + int r; + char *p = simple_itoa(i); + /* no escape checking necessary */ + do r=b_addchr(o, *p++); while (r==0 && *p); + return r; +} + +static int static_get(struct in_str *i) +{ + int ch=*i->p++; + if (ch=='\0') return EOF; + return ch; +} + +static int static_peek(struct in_str *i) +{ + return *i->p; +} + +static inline void cmdedit_set_initial_prompt(void) +{ +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + PS1 = NULL; +#else + PS1 = getenv("PS1"); + if(PS1==0) + PS1 = "\\w \\$ "; +#endif +} + +static inline void setup_prompt_string(int promptmode, char **prompt_str) +{ + debug_printf("setup_prompt_string %d ",promptmode); +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + /* Set up the prompt */ + if (promptmode == 1) { + free(PS1); + PS1=xmalloc(strlen(cwd)+4); + sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# "); + *prompt_str = PS1; + } else { + *prompt_str = PS2; + } +#else + *prompt_str = (promptmode==1)? PS1 : PS2; +#endif + debug_printf("result %s\n",*prompt_str); +} + +static void get_user_input(struct in_str *i) +{ + char *prompt_str; + static char the_command[BUFSIZ]; + + setup_prompt_string(i->promptmode, &prompt_str); +#ifdef CONFIG_FEATURE_COMMAND_EDITING + /* + ** enable command line editing only while a command line + ** is actually being read; otherwise, we'll end up bequeathing + ** atexit() handlers and other unwanted stuff to our + ** child processes (rob@sysgo.de) + */ + cmdedit_read_input(prompt_str, the_command); +#else + fputs(prompt_str, stdout); + fflush(stdout); + the_command[0]=fgetc(i->file); + the_command[1]='\0'; +#endif + fflush(stdout); + i->p = the_command; +} + +/* This is the magic location that prints prompts + * and gets data back from the user */ +static int file_get(struct in_str *i) +{ + int ch; + + ch = 0; + /* If there is data waiting, eat it up */ + if (i->p && *i->p) { + ch=*i->p++; + } else { + /* need to double check i->file because we might be doing something + * more complicated by now, like sourcing or substituting. */ + if (i->__promptme && interactive && i->file == stdin) { + while(! i->p || (interactive && strlen(i->p)==0) ) { + get_user_input(i); + } + i->promptmode=2; + i->__promptme = 0; + if (i->p && *i->p) { + ch=*i->p++; + } + } else { + ch = fgetc(i->file); + } + + debug_printf("b_getch: got a %d\n", ch); + } + if (ch == '\n') i->__promptme=1; + return ch; +} + +/* All the callers guarantee this routine will never be + * used right after a newline, so prompting is not needed. + */ +static int file_peek(struct in_str *i) +{ + if (i->p && *i->p) { + return *i->p; + } else { + i->peek_buf[0] = fgetc(i->file); + i->peek_buf[1] = '\0'; + i->p = i->peek_buf; + debug_printf("b_peek: got a %d\n", *i->p); + return *i->p; + } +} + +static void setup_file_in_str(struct in_str *i, FILE *f) +{ + i->peek = file_peek; + i->get = file_get; + i->__promptme=1; + i->promptmode=1; + i->file = f; + i->p = NULL; +} + +static void setup_string_in_str(struct in_str *i, const char *s) +{ + i->peek = static_peek; + i->get = static_get; + i->__promptme=1; + i->promptmode=1; + i->p = s; +} + +static void mark_open(int fd) +{ + struct close_me *new = xmalloc(sizeof(struct close_me)); + new->fd = fd; + new->next = close_me_head; + close_me_head = new; +} + +static void mark_closed(int fd) +{ + struct close_me *tmp; + if (close_me_head == NULL || close_me_head->fd != fd) + bb_error_msg_and_die("corrupt close_me"); + tmp = close_me_head; + close_me_head = close_me_head->next; + free(tmp); +} + +static void close_all(void) +{ + struct close_me *c; + for (c=close_me_head; c; c=c->next) { + close(c->fd); + } + close_me_head = NULL; +} + +/* squirrel != NULL means we squirrel away copies of stdin, stdout, + * and stderr if they are redirected. */ +static int setup_redirects(struct child_prog *prog, int squirrel[]) +{ + int openfd, mode; + struct redir_struct *redir; + + for (redir=prog->redirects; redir; redir=redir->next) { + if (redir->dup == -1 && redir->word.gl_pathv == NULL) { + /* something went wrong in the parse. Pretend it didn't happen */ + continue; + } + if (redir->dup == -1) { + mode=redir_table[redir->type].mode; + openfd = open(redir->word.gl_pathv[0], mode, 0666); + if (openfd < 0) { + /* this could get lost if stderr has been redirected, but + bash and ash both lose it as well (though zsh doesn't!) */ + bb_perror_msg("error opening %s", redir->word.gl_pathv[0]); + return 1; + } + } else { + openfd = redir->dup; + } + + if (openfd != redir->fd) { + if (squirrel && redir->fd < 3) { + squirrel[redir->fd] = dup(redir->fd); + } + if (openfd == -3) { + close(openfd); + } else { + dup2(openfd, redir->fd); + if (redir->dup == -1) + close (openfd); + } + } + } + return 0; +} + +static void restore_redirects(int squirrel[]) +{ + int i, fd; + for (i=0; i<3; i++) { + fd = squirrel[i]; + if (fd != -1) { + /* No error checking. I sure wouldn't know what + * to do with an error if I found one! */ + dup2(fd, i); + close(fd); + } + } +} + +/* never returns */ +/* XXX no exit() here. If you don't exec, use _exit instead. + * The at_exit handlers apparently confuse the calling process, + * in particular stdin handling. Not sure why? */ +static void pseudo_exec(struct child_prog *child) +{ + int i, rcode; + char *p; + struct built_in_command *x; + if (child->argv) { + for (i=0; is_assignment(child->argv[i]); i++) { + debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]); + p = insert_var_value(child->argv[i]); + putenv(strdup(p)); + if (p != child->argv[i]) free(p); + } + child->argv+=i; /* XXX this hack isn't so horrible, since we are about + to exit, and therefore don't need to keep data + structures consistent for free() use. */ + /* If a variable is assigned in a forest, and nobody listens, + * was it ever really set? + */ + if (child->argv[0] == NULL) { + _exit(EXIT_SUCCESS); + } + + /* + * Check if the command matches any of the builtins. + * Depending on context, this might be redundant. But it's + * easier to waste a few CPU cycles than it is to figure out + * if this is one of those cases. + */ + for (x = bltins; x->cmd; x++) { + if (strcmp(child->argv[0], x->cmd) == 0 ) { + debug_printf("builtin exec %s\n", child->argv[0]); + rcode = x->function(child); + fflush(stdout); + _exit(rcode); + } + } + + /* Check if the command matches any busybox internal commands + * ("applets") here. + * FIXME: This feature is not 100% safe, since + * BusyBox is not fully reentrant, so we have no guarantee the things + * from the .bss are still zeroed, or that things from .data are still + * at their defaults. We could exec ourself from /proc/self/exe, but I + * really dislike relying on /proc for things. We could exec ourself + * from global_argv[0], but if we are in a chroot, we may not be able + * to find ourself... */ +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + { + int argc_l; + char** argv_l=child->argv; + char *name = child->argv[0]; + + /* Count argc for use in a second... */ + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++); + optind = 1; + debug_printf("running applet %s\n", name); + run_applet_by_name(name, argc_l, child->argv); + } +#endif + debug_printf("exec of %s\n",child->argv[0]); + execvp(child->argv[0],child->argv); + bb_perror_msg("couldn't exec: %s",child->argv[0]); + _exit(1); + } else if (child->group) { + debug_printf("runtime nesting to group\n"); + interactive=0; /* crucial!!!! */ + rcode = run_list_real(child->group); + /* OK to leak memory by not calling free_pipe_list, + * since this process is about to exit */ + _exit(rcode); + } else { + /* Can happen. See what bash does with ">foo" by itself. */ + debug_printf("trying to pseudo_exec null command\n"); + _exit(EXIT_SUCCESS); + } +} + +static void insert_bg_job(struct pipe *pi) +{ + struct pipe *thejob; + + /* Linear search for the ID of the job to use */ + pi->jobid = 1; + for (thejob = job_list; thejob; thejob = thejob->next) + if (thejob->jobid >= pi->jobid) + pi->jobid = thejob->jobid + 1; + + /* add thejob to the list of running jobs */ + if (!job_list) { + thejob = job_list = xmalloc(sizeof(*thejob)); + } else { + for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */; + thejob->next = xmalloc(sizeof(*thejob)); + thejob = thejob->next; + } + + /* physically copy the struct job */ + memcpy(thejob, pi, sizeof(struct pipe)); + thejob->next = NULL; + thejob->running_progs = thejob->num_progs; + thejob->stopped_progs = 0; + thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */ + + //if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0]) + { + char *bar=thejob->text; + char **foo=pi->progs[0].argv; + while(foo && *foo) { + bar += sprintf(bar, "%s ", *foo++); + } + } + + /* we don't wait for background thejobs to return -- append it + to the list of backgrounded thejobs and leave it alone */ + printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); + last_bg_pid = thejob->progs[0].pid; + last_jobid = thejob->jobid; +} + +/* remove a backgrounded job */ +static void remove_bg_job(struct pipe *pi) +{ + struct pipe *prev_pipe; + + if (pi == job_list) { + job_list = pi->next; + } else { + prev_pipe = job_list; + while (prev_pipe->next != pi) + prev_pipe = prev_pipe->next; + prev_pipe->next = pi->next; + } + if (job_list) + last_jobid = job_list->jobid; + else + last_jobid = 0; + + pi->stopped_progs = 0; + free_pipe(pi, 0); + free(pi); +} + +/* Checks to see if any processes have exited -- if they + have, figure out why and see if a job has completed */ +static int checkjobs(struct pipe* fg_pipe) +{ + int attributes; + int status; + int prognum = 0; + struct pipe *pi; + pid_t childpid; + + attributes = WUNTRACED; + if (fg_pipe==NULL) { + attributes |= WNOHANG; + } + + while ((childpid = waitpid(-1, &status, attributes)) > 0) { + if (fg_pipe) { + int i, rcode = 0; + for (i=0; i < fg_pipe->num_progs; i++) { + if (fg_pipe->progs[i].pid == childpid) { + if (i==fg_pipe->num_progs-1) + rcode=WEXITSTATUS(status); + (fg_pipe->num_progs)--; + return(rcode); + } + } + } + + for (pi = job_list; pi; pi = pi->next) { + prognum = 0; + while (prognum < pi->num_progs && pi->progs[prognum].pid != childpid) { + prognum++; + } + if (prognum < pi->num_progs) + break; + } + + if(pi==NULL) { + debug_printf("checkjobs: pid %d was not in our list!\n", childpid); + continue; + } + + if (WIFEXITED(status) || WIFSIGNALED(status)) { + /* child exited */ + pi->running_progs--; + pi->progs[prognum].pid = 0; + + if (!pi->running_progs) { + printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->text); + remove_bg_job(pi); + } + } else { + /* child stopped */ + pi->stopped_progs++; + pi->progs[prognum].is_stopped = 1; + +#if 0 + /* Printing this stuff is a pain, since it tends to + * overwrite the prompt an inconveinient moments. So + * don't do that. */ + if (pi->stopped_progs == pi->num_progs) { + printf("\n"JOB_STATUS_FORMAT, pi->jobid, "Stopped", pi->text); + } +#endif + } + } + + if (childpid == -1 && errno != ECHILD) + bb_perror_msg("waitpid"); + + /* move the shell to the foreground */ + //if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) + // bb_perror_msg("tcsetpgrp-2"); + return -1; +} + +/* Figure out our controlling tty, checking in order stderr, + * stdin, and stdout. If check_pgrp is set, also check that + * we belong to the foreground process group associated with + * that tty. The value of shell_terminal is needed in order to call + * tcsetpgrp(shell_terminal, ...); */ +void controlling_tty(int check_pgrp) +{ + pid_t curpgrp; + + if ((curpgrp = tcgetpgrp(shell_terminal = 2)) < 0 + && (curpgrp = tcgetpgrp(shell_terminal = 0)) < 0 + && (curpgrp = tcgetpgrp(shell_terminal = 1)) < 0) + goto shell_terminal_error; + + if (check_pgrp && curpgrp != getpgid(0)) + goto shell_terminal_error; + + return; + +shell_terminal_error: + shell_terminal = -1; + return; +} + +/* run_pipe_real() starts all the jobs, but doesn't wait for anything + * to finish. See checkjobs(). + * + * return code is normally -1, when the caller has to wait for children + * to finish to determine the exit status of the pipe. If the pipe + * is a simple builtin command, however, the action is done by the + * time run_pipe_real returns, and the exit code is provided as the + * return value. + * + * The input of the pipe is always stdin, the output is always + * stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus, + * because it tries to avoid running the command substitution in + * subshell, when that is in fact necessary. The subshell process + * now has its stdout directed to the input of the appropriate pipe, + * so this routine is noticeably simpler. + */ +static int run_pipe_real(struct pipe *pi) +{ + int i; + int nextin, nextout; + int pipefds[2]; /* pipefds[0] is for reading */ + struct child_prog *child; + struct built_in_command *x; + char *p; + + nextin = 0; + pi->pgrp = -1; + + /* Check if this is a simple builtin (not part of a pipe). + * Builtins within pipes have to fork anyway, and are handled in + * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. + */ + if (pi->num_progs == 1) child = & (pi->progs[0]); + if (pi->num_progs == 1 && child->group && child->subshell == 0) { + int squirrel[] = {-1, -1, -1}; + int rcode; + debug_printf("non-subshell grouping\n"); + setup_redirects(child, squirrel); + /* XXX could we merge code with following builtin case, + * by creating a pseudo builtin that calls run_list_real? */ + rcode = run_list_real(child->group); + restore_redirects(squirrel); + return rcode; + } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) { + for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ } + if (i!=0 && child->argv[i]==NULL) { + /* assignments, but no command: set the local environment */ + for (i=0; child->argv[i]!=NULL; i++) { + + /* Ok, this case is tricky. We have to decide if this is a + * local variable, or an already exported variable. If it is + * already exported, we have to export the new value. If it is + * not exported, we need only set this as a local variable. + * This junk is all to decide whether or not to export this + * variable. */ + int export_me=0; + char *name, *value; + name = bb_xstrdup(child->argv[i]); + debug_printf("Local environment set: %s\n", name); + value = strchr(name, '='); + if (value) + *value=0; + if ( get_local_var(name)) { + export_me=1; + } + free(name); + p = insert_var_value(child->argv[i]); + set_local_var(p, export_me); + if (p != child->argv[i]) free(p); + } + return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ + } + for (i = 0; is_assignment(child->argv[i]); i++) { + p = insert_var_value(child->argv[i]); + putenv(strdup(p)); + if (p != child->argv[i]) { + child->sp--; + free(p); + } + } + if (child->sp) { + char * str = NULL; + + str = make_string((child->argv + i)); + parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING); + free(str); + return last_return_code; + } + for (x = bltins; x->cmd; x++) { + if (strcmp(child->argv[i], x->cmd) == 0 ) { + int squirrel[] = {-1, -1, -1}; + int rcode; + if (x->function == builtin_exec && child->argv[i+1]==NULL) { + debug_printf("magic exec\n"); + setup_redirects(child,NULL); + return EXIT_SUCCESS; + } + debug_printf("builtin inline %s\n", child->argv[0]); + /* XXX setup_redirects acts on file descriptors, not FILEs. + * This is perfect for work that comes after exec(). + * Is it really safe for inline use? Experimentally, + * things seem to work with glibc. */ + setup_redirects(child, squirrel); + child->argv+=i; /* XXX horrible hack */ + rcode = x->function(child); + child->argv-=i; /* XXX restore hack so free() can work right */ + restore_redirects(squirrel); + return rcode; + } + } + } + + for (i = 0; i < pi->num_progs; i++) { + child = & (pi->progs[i]); + + /* pipes are inserted between pairs of commands */ + if ((i + 1) < pi->num_progs) { + if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe"); + nextout = pipefds[1]; + } else { + nextout=1; + pipefds[0] = -1; + } + + /* XXX test for failed fork()? */ +#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) + if (!(child->pid = fork())) +#else + if (!(child->pid = vfork())) +#endif + { + /* Set the handling for job control signals back to the default. */ + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + close_all(); + + if (nextin != 0) { + dup2(nextin, 0); + close(nextin); + } + if (nextout != 1) { + dup2(nextout, 1); + close(nextout); + } + if (pipefds[0]!=-1) { + close(pipefds[0]); /* opposite end of our output pipe */ + } + + /* Like bash, explicit redirects override pipes, + * and the pipe fd is available for dup'ing. */ + setup_redirects(child,NULL); + + if (interactive && pi->followup!=PIPE_BG) { + /* If we (the child) win the race, put ourselves in the process + * group whose leader is the first process in this pipe. */ + if (pi->pgrp < 0) { + pi->pgrp = getpid(); + } + if (setpgid(0, pi->pgrp) == 0) { + tcsetpgrp(2, pi->pgrp); + } + } + + pseudo_exec(child); + } + + + /* put our child in the process group whose leader is the + first process in this pipe */ + if (pi->pgrp < 0) { + pi->pgrp = child->pid; + } + /* Don't check for errors. The child may be dead already, + * in which case setpgid returns error code EACCES. */ + setpgid(child->pid, pi->pgrp); + + if (nextin != 0) + close(nextin); + if (nextout != 1) + close(nextout); + + /* If there isn't another process, nextin is garbage + but it doesn't matter */ + nextin = pipefds[0]; + } + return -1; +} + +static int run_list_real(struct pipe *pi) +{ + char *save_name = NULL; + char **list = NULL; + char **save_list = NULL; + struct pipe *rpipe; + int flag_rep = 0; + int save_num_progs; + int rcode=0, flag_skip=1; + int flag_restore = 0; + int if_code=0, next_if_code=0; /* need double-buffer to handle elif */ + reserved_style rmode, skip_more_in_this_rmode=RES_XXXX; + /* check syntax for "for" */ + for (rpipe = pi; rpipe; rpipe = rpipe->next) { + if ((rpipe->r_mode == RES_IN || + rpipe->r_mode == RES_FOR) && + (rpipe->next == NULL)) { + syntax(); + return 1; + } + if ((rpipe->r_mode == RES_IN && + (rpipe->next->r_mode == RES_IN && + rpipe->next->progs->argv != NULL))|| + (rpipe->r_mode == RES_FOR && + rpipe->next->r_mode != RES_IN)) { + syntax(); + return 1; + } + } + for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) { + if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL || + pi->r_mode == RES_FOR) { + flag_restore = 0; + if (!rpipe) { + flag_rep = 0; + rpipe = pi; + } + } + rmode = pi->r_mode; + debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode); + if (rmode == skip_more_in_this_rmode && flag_skip) { + if (pi->followup == PIPE_SEQ) flag_skip=0; + continue; + } + flag_skip = 1; + skip_more_in_this_rmode = RES_XXXX; + if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code; + if (rmode == RES_THEN && if_code) continue; + if (rmode == RES_ELSE && !if_code) continue; + if (rmode == RES_ELIF && !if_code) break; + if (rmode == RES_FOR && pi->num_progs) { + if (!list) { + /* if no variable values after "in" we skip "for" */ + if (!pi->next->progs->argv) continue; + /* create list of variable values */ + list = make_list_in(pi->next->progs->argv, + pi->progs->argv[0]); + save_list = list; + save_name = pi->progs->argv[0]; + pi->progs->argv[0] = NULL; + flag_rep = 1; + } + if (!(*list)) { + free(pi->progs->argv[0]); + free(save_list); + list = NULL; + flag_rep = 0; + pi->progs->argv[0] = save_name; + pi->progs->glob_result.gl_pathv[0] = + pi->progs->argv[0]; + continue; + } else { + /* insert new value from list for variable */ + if (pi->progs->argv[0]) + free(pi->progs->argv[0]); + pi->progs->argv[0] = *list++; + pi->progs->glob_result.gl_pathv[0] = + pi->progs->argv[0]; + } + } + if (rmode == RES_IN) continue; + if (rmode == RES_DO) { + if (!flag_rep) continue; + } + if ((rmode == RES_DONE)) { + if (flag_rep) { + flag_restore = 1; + } else { + rpipe = NULL; + } + } + if (pi->num_progs == 0) continue; + save_num_progs = pi->num_progs; /* save number of programs */ + rcode = run_pipe_real(pi); + debug_printf("run_pipe_real returned %d\n",rcode); + if (rcode!=-1) { + /* We only ran a builtin: rcode was set by the return value + * of run_pipe_real(), and we don't need to wait for anything. */ + } else if (pi->followup==PIPE_BG) { + /* XXX check bash's behavior with nontrivial pipes */ + /* XXX compute jobid */ + /* XXX what does bash do with attempts to background builtins? */ + insert_bg_job(pi); + rcode = EXIT_SUCCESS; + } else { + if (interactive) { + /* move the new process group into the foreground */ + if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY) + bb_perror_msg("tcsetpgrp-3"); + rcode = checkjobs(pi); + /* move the shell to the foreground */ + if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) + bb_perror_msg("tcsetpgrp-4"); + } else { + rcode = checkjobs(pi); + } + debug_printf("checkjobs returned %d\n",rcode); + } + last_return_code=rcode; + pi->num_progs = save_num_progs; /* restore number of programs */ + if ( rmode == RES_IF || rmode == RES_ELIF ) + next_if_code=rcode; /* can be overwritten a number of times */ + if (rmode == RES_WHILE) + flag_rep = !last_return_code; + if (rmode == RES_UNTIL) + flag_rep = last_return_code; + if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) || + (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) + skip_more_in_this_rmode=rmode; + checkjobs(NULL); + } + return rcode; +} + +/* broken, of course, but OK for testing */ +static char *indenter(int i) +{ + static char blanks[]=" "; + return &blanks[sizeof(blanks)-i-1]; +} + +/* return code is the exit status of the pipe */ +static int free_pipe(struct pipe *pi, int indent) +{ + char **p; + struct child_prog *child; + struct redir_struct *r, *rnext; + int a, i, ret_code=0; + char *ind = indenter(indent); + + if (pi->stopped_progs > 0) + return ret_code; + final_printf("%s run pipe: (pid %d)\n",ind,getpid()); + for (i=0; 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) + bb_error_msg_and_die("out of memory during glob"); + if (gr != 0) { /* GLOB_ABORTED ? */ + bb_error_msg("glob(3) error %d",gr); + } + /* globprint(glob_target); */ + return gr; +} + +/* This is used to get/check local shell variables */ +static char *get_local_var(const char *s) +{ + struct variables *cur; + + if (!s) + return NULL; + for (cur = top_vars; cur; cur=cur->next) + if(strcmp(cur->name, s)==0) + return cur->value; + return NULL; +} + +/* This is used to set local shell variables + flg_export==0 if only local (not exporting) variable + flg_export==1 if "new" exporting environ + flg_export>1 if current startup environ (not call putenv()) */ +static int set_local_var(const char *s, int flg_export) +{ + char *name, *value; + int result=0; + struct variables *cur; + + name=strdup(s); + + /* Assume when we enter this function that we are already in + * NAME=VALUE format. So the first order of business is to + * split 's' on the '=' into 'name' and 'value' */ + value = strchr(name, '='); + if (value==0 && ++value==0) { + free(name); + return -1; + } + *value++ = 0; + + for(cur = top_vars; cur; cur = cur->next) { + if(strcmp(cur->name, name)==0) + break; + } + + if(cur) { + if(strcmp(cur->value, value)==0) { + if(flg_export>0 && cur->flg_export==0) + cur->flg_export=flg_export; + else + result++; + } else { + if(cur->flg_read_only) { + bb_error_msg("%s: readonly variable", name); + result = -1; + } else { + if(flg_export>0 || cur->flg_export>1) + cur->flg_export=1; + free(cur->value); + + cur->value = strdup(value); + } + } + } else { + cur = malloc(sizeof(struct variables)); + if(!cur) { + result = -1; + } else { + cur->name = strdup(name); + if(cur->name == 0) { + free(cur); + result = -1; + } else { + struct variables *bottom = top_vars; + cur->value = strdup(value); + cur->next = 0; + cur->flg_export = flg_export; + cur->flg_read_only = 0; + while(bottom->next) bottom=bottom->next; + bottom->next = cur; + } + } + } + + if(result==0 && cur->flg_export==1) { + *(value-1) = '='; + result = putenv(name); + } else { + free(name); + if(result>0) /* equivalent to previous set */ + result = 0; + } + return result; +} + +static void unset_local_var(const char *name) +{ + struct variables *cur; + + if (name) { + for (cur = top_vars; cur; cur=cur->next) { + if(strcmp(cur->name, name)==0) + break; + } + if(cur!=0) { + struct variables *next = top_vars; + if(cur->flg_read_only) { + bb_error_msg("%s: readonly variable", name); + return; + } else { + if(cur->flg_export) + unsetenv(cur->name); + free(cur->name); + free(cur->value); + while (next->next != cur) + next = next->next; + next->next = cur->next; + } + free(cur); + } + } +} + +static int is_assignment(const char *s) +{ + if (s==NULL || !isalpha(*s)) return 0; + ++s; + while(isalnum(*s) || *s=='_') ++s; + return *s=='='; +} + +/* the src parameter allows us to peek forward to a possible &n syntax + * for file descriptor duplication, e.g., "2>&1". + * Return code is 0 normally, 1 if a syntax error is detected in src. + * Resource errors (in xmalloc) cause the process to exit */ +static int setup_redirect(struct p_context *ctx, int fd, redir_type style, + struct in_str *input) +{ + struct child_prog *child=ctx->child; + struct redir_struct *redir = child->redirects; + struct redir_struct *last_redir=NULL; + + /* Create a new redir_struct and drop it onto the end of the linked list */ + while(redir) { + last_redir=redir; + redir=redir->next; + } + redir = xmalloc(sizeof(struct redir_struct)); + redir->next=NULL; + redir->word.gl_pathv=NULL; + if (last_redir) { + last_redir->next=redir; + } else { + child->redirects=redir; + } + + redir->type=style; + redir->fd= (fd==-1) ? redir_table[style].default_fd : fd ; + + debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); + + /* Check for a '2>&1' type redirect */ + redir->dup = redirect_dup_num(input); + if (redir->dup == -2) return 1; /* syntax error */ + if (redir->dup != -1) { + /* Erik had a check here that the file descriptor in question + * is legit; I postpone that to "run time" + * A "-" representation of "close me" shows up as a -3 here */ + debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); + } else { + /* We do _not_ try to open the file that src points to, + * since we need to return and let src be expanded first. + * Set ctx->pending_redirect, so we know what to do at the + * end of the next parsed word. + */ + ctx->pending_redirect = redir; + } + return 0; +} + +struct pipe *new_pipe(void) { + struct pipe *pi; + pi = xmalloc(sizeof(struct pipe)); + pi->num_progs = 0; + pi->progs = NULL; + pi->next = NULL; + pi->followup = 0; /* invalid */ + return pi; +} + +static void initialize_context(struct p_context *ctx) +{ + ctx->pipe=NULL; + ctx->pending_redirect=NULL; + ctx->child=NULL; + ctx->list_head=new_pipe(); + ctx->pipe=ctx->list_head; + ctx->w=RES_NONE; + ctx->stack=NULL; + ctx->old_flag=0; + done_command(ctx); /* creates the memory for working child */ +} + +/* normal return is 0 + * if a reserved word is found, and processed, return 1 + * should handle if, then, elif, else, fi, for, while, until, do, done. + * case, function, and select are obnoxious, save those for later. + */ +int reserved_word(o_string *dest, struct p_context *ctx) +{ + struct reserved_combo { + char *literal; + int code; + long flag; + }; + /* Mostly a list of accepted follow-up reserved words. + * FLAG_END means we are done with the sequence, and are ready + * to turn the compound list into a command. + * FLAG_START means the word must start a new compound list. + */ + static struct reserved_combo reserved_list[] = { + { "if", RES_IF, FLAG_THEN | FLAG_START }, + { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, + { "elif", RES_ELIF, FLAG_THEN }, + { "else", RES_ELSE, FLAG_FI }, + { "fi", RES_FI, FLAG_END }, + { "for", RES_FOR, FLAG_IN | FLAG_START }, + { "while", RES_WHILE, FLAG_DO | FLAG_START }, + { "until", RES_UNTIL, FLAG_DO | FLAG_START }, + { "in", RES_IN, FLAG_DO }, + { "do", RES_DO, FLAG_DONE }, + { "done", RES_DONE, FLAG_END } + }; + struct reserved_combo *r; + for (r=reserved_list; +#define NRES sizeof(reserved_list)/sizeof(struct reserved_combo) + 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"); + if (ctx->w == RES_IN || ctx->w == RES_FOR) { + syntax(); + free(new); + ctx->w = RES_SNTX; + b_reset(dest); + return 1; + } + *new = *ctx; /* physical copy */ + initialize_context(ctx); + ctx->stack=new; + } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<code))) { + syntax(); + ctx->w = RES_SNTX; + b_reset(dest); + return 1; + } + ctx->w=r->code; + ctx->old_flag = r->flag; + if (ctx->old_flag & FLAG_END) { + struct p_context *old; + debug_printf("pop stack\n"); + done_pipe(ctx,PIPE_SEQ); + old = ctx->stack; + old->child->group = ctx->list_head; + old->child->subshell = 0; + *ctx = *old; /* physical copy */ + free(old); + } + b_reset (dest); + return 1; + } + } + return 0; +} + +/* normal return is 0. + * Syntax or xglob errors return 1. */ +static int done_word(o_string *dest, struct p_context *ctx) +{ + struct child_prog *child=ctx->child; + glob_t *glob_target; + int gr, flags = 0; + + debug_printf("done_word: %s %p\n", dest->data, child); + if (dest->length == 0 && !dest->nonnull) { + debug_printf(" true null, ignored\n"); + return 0; + } + if (ctx->pending_redirect) { + glob_target = &ctx->pending_redirect->word; + } else { + if (child->group) { + syntax(); + return 1; /* syntax error, groups and arglists don't mix */ + } + if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) { + debug_printf("checking %s for reserved-ness\n",dest->data); + if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX; + } + glob_target = &child->glob_result; + if (child->argv) flags |= GLOB_APPEND; + } + gr = xglob(dest, flags, glob_target); + if (gr != 0) return 1; + + b_reset(dest); + if (ctx->pending_redirect) { + ctx->pending_redirect=NULL; + if (glob_target->gl_pathc != 1) { + bb_error_msg("ambiguous redirect"); + return 1; + } + } else { + child->argv = glob_target->gl_pathv; + } + if (ctx->w == RES_FOR) { + done_word(dest,ctx); + done_pipe(ctx,PIPE_SEQ); + } + return 0; +} + +/* The only possible error here is out of memory, in which case + * xmalloc exits. */ +static int done_command(struct p_context *ctx) +{ + /* The child is really already in the pipe structure, so + * advance the pipe counter and make a new, null child. + * Only real trickiness here is that the uncommitted + * child structure, to which ctx->child points, is not + * counted in pi->num_progs. */ + struct pipe *pi=ctx->pipe; + struct child_prog *prog=ctx->child; + + if (prog && prog->group == NULL + && prog->argv == NULL + && prog->redirects == NULL) { + debug_printf("done_command: skipping null command\n"); + return 0; + } else if (prog) { + pi->num_progs++; + debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs); + } else { + debug_printf("done_command: initializing\n"); + } + pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1)); + + prog = pi->progs + pi->num_progs; + prog->redirects = NULL; + prog->argv = NULL; + prog->is_stopped = 0; + prog->group = NULL; + prog->glob_result.gl_pathv = NULL; + prog->family = pi; + prog->sp = 0; + ctx->child = prog; + prog->type = ctx->type; + + /* but ctx->pipe and ctx->list_head remain unchanged */ + return 0; +} + +static int done_pipe(struct p_context *ctx, pipe_style type) +{ + struct pipe *new_p; + done_command(ctx); /* implicit closure of previous command */ + debug_printf("done_pipe, type %d\n", type); + ctx->pipe->followup = type; + ctx->pipe->r_mode = ctx->w; + new_p=new_pipe(); + ctx->pipe->next = new_p; + ctx->pipe = new_p; + ctx->child = NULL; + done_command(ctx); /* set up new pipe to accept commands */ + return 0; +} + +/* peek ahead in the in_str to find out if we have a "&n" construct, + * as in "2>&1", that represents duplicating a file descriptor. + * returns either -2 (syntax error), -1 (no &), or the number found. + */ +static int redirect_dup_num(struct in_str *input) +{ + int ch, d=0, ok=0; + ch = b_peek(input); + if (ch != '&') return -1; + + b_getch(input); /* get the & */ + ch=b_peek(input); + if (ch == '-') { + b_getch(input); + return -3; /* "-" represents "close me" */ + } + while (isdigit(ch)) { + d = d*10+(ch-'0'); + ok=1; + b_getch(input); + ch = b_peek(input); + } + if (ok) return d; + + bb_error_msg("ambiguous redirect"); + return -2; +} + +/* If a redirect is immediately preceded by a number, that number is + * supposed to tell which file descriptor to redirect. This routine + * looks for such preceding numbers. In an ideal world this routine + * needs to handle all the following classes of redirects... + * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo + * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo + * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo + * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo + * A -1 output from this program means no valid number was found, so the + * caller should use the appropriate default for this redirection. + */ +static int redirect_opt_num(o_string *o) +{ + int num; + + if (o->length==0) return -1; + for(num=0; 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) bb_perror_msg_and_die("pipe"); +#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) + pid=fork(); +#else + pid=vfork(); +#endif + if (pid<0) { + bb_perror_msg_and_die("fork"); + } else if (pid==0) { + close(channel[0]); + if (channel[1] != 1) { + dup2(channel[1],1); + close(channel[1]); + } +#if 0 +#define SURROGATE "surrogate response" + write(1,SURROGATE,sizeof(SURROGATE)); + _exit(run_list(head)); +#else + _exit(run_list_real(head)); /* leaks memory */ +#endif + } + debug_printf("forked child %d\n",pid); + close(channel[1]); + pf = fdopen(channel[0],"r"); + debug_printf("pipe on FILE *%p\n",pf); +#else + free_pipe_list(head,0); + pf=popen("echo surrogate response","r"); + debug_printf("started fake pipe on FILE *%p\n",pf); +#endif + return pf; +} + +/* this version hacked for testing purposes */ +/* return code is exit status of the process that is run. */ +static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end) +{ + int retcode; + o_string result=NULL_O_STRING; + struct p_context inner; + FILE *p; + struct in_str pipe_str; + initialize_context(&inner); + + /* recursion to generate command */ + retcode = parse_stream(&result, &inner, input, subst_end); + if (retcode != 0) return retcode; /* syntax error or EOF */ + done_word(&result, &inner); + done_pipe(&inner, PIPE_SEQ); + b_free(&result); + + p=generate_stream_from_list(inner.list_head); + if (p==NULL) return 1; + mark_open(fileno(p)); + setup_file_in_str(&pipe_str, p); + + /* now send results of command back into original context */ + retcode = parse_stream(dest, ctx, &pipe_str, '\0'); + /* XXX In case of a syntax error, should we try to kill the child? + * That would be tough to do right, so just read until EOF. */ + if (retcode == 1) { + while (b_getch(&pipe_str)!=EOF) { /* discard */ }; + } + + debug_printf("done reading from pipe, pclose()ing\n"); + /* This is the step that wait()s for the child. Should be pretty + * safe, since we just read an EOF from its stdout. We could try + * to better, by using wait(), and keeping track of background jobs + * at the same time. That would be a lot of work, and contrary + * to the KISS philosophy of this program. */ + mark_closed(fileno(p)); + retcode=pclose(p); + free_pipe_list(inner.list_head,0); + debug_printf("pclosed, retcode=%d\n",retcode); + /* XXX this process fails to trim a single trailing newline */ + return retcode; +} + +static int parse_group(o_string *dest, struct p_context *ctx, + struct in_str *input, int ch) +{ + int rcode, endch=0; + struct p_context sub; + struct child_prog *child = ctx->child; + if (child->argv) { + syntax(); + return 1; /* syntax error, groups and arglists don't mix */ + } + initialize_context(&sub); + switch(ch) { + case '(': endch=')'; child->subshell=1; break; + case '{': endch='}'; break; + default: syntax(); /* really logic error */ + } + rcode=parse_stream(dest,&sub,input,endch); + done_word(dest,&sub); /* finish off the final word in the subcontext */ + done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */ + child->group = sub.list_head; + return rcode; + /* child remains "open", available for possible redirects */ +} + +/* basically useful version until someone wants to get fancier, + * see the bash man page under "Parameter Expansion" */ +static char *lookup_param(char *src) +{ + char *p=NULL; + if (src) { + p = getenv(src); + if (!p) + p = get_local_var(src); + } + return p; +} + +/* return code: 0 for OK, 1 for syntax error */ +static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) +{ + int i, advance=0; + char sep[]=" "; + int ch = input->peek(input); /* first character after the $ */ + debug_printf("handle_dollar: ch=%c\n",ch); + if (isalpha(ch)) { + b_addchr(dest, SPECIAL_VAR_SYMBOL); + ctx->child->sp++; + while(ch=b_peek(input),isalnum(ch) || ch=='_') { + b_getch(input); + b_addchr(dest,ch); + } + b_addchr(dest, SPECIAL_VAR_SYMBOL); + } else if (isdigit(ch)) { + i = ch-'0'; /* XXX is $0 special? */ + if (i 0) b_adduint(dest, last_bg_pid); + advance = 1; + break; + case '?': + b_adduint(dest,last_return_code); + advance = 1; + break; + case '#': + b_adduint(dest,global_argc ? global_argc-1 : 0); + advance = 1; + break; + case '{': + b_addchr(dest, SPECIAL_VAR_SYMBOL); + ctx->child->sp++; + b_getch(input); + /* XXX maybe someone will try to escape the '}' */ + while(ch=b_getch(input),ch!=EOF && ch!='}') { + b_addchr(dest,ch); + } + if (ch != '}') { + syntax(); + return 1; + } + b_addchr(dest, SPECIAL_VAR_SYMBOL); + break; + case '(': + b_getch(input); + process_command_subs(dest, ctx, input, ')'); + break; + case '*': + sep[0]=ifs[0]; + for (i=1; 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 */ + if (done_word(dest, ctx)) { + return 1; + } + /* If we aren't performing a substitution, treat a newline as a + * command separator. */ + if (end_trigger != '\0' && ch=='\n') + done_pipe(ctx,PIPE_SEQ); + } + if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) { + debug_printf("leaving parse_stream (triggered)\n"); + return 0; + } +#if 0 + if (ch=='\n') { + /* Yahoo! Time to run with it! */ + done_pipe(ctx,PIPE_SEQ); + run_list(ctx->list_head); + initialize_context(ctx); + } +#endif + if (m!=2) switch (ch) { + case '#': + if (dest->length == 0 && !dest->quote) { + while(ch=b_peek(input),ch!=EOF && ch!='\n') { b_getch(input); } + } else { + b_addqchr(dest, ch, dest->quote); + } + break; + case '\\': + if (next == EOF) { + syntax(); + return 1; + } + b_addqchr(dest, '\\', dest->quote); + b_addqchr(dest, b_getch(input), dest->quote); + break; + case '$': + if (handle_dollar(dest, ctx, input)!=0) return 1; + break; + case '\'': + dest->nonnull = 1; + while(ch=b_getch(input),ch!=EOF && ch!='\'') { + b_addchr(dest,ch); + } + if (ch==EOF) { + syntax(); + return 1; + } + break; + case '"': + dest->nonnull = 1; + dest->quote = !dest->quote; + break; + case '`': + process_command_subs(dest, ctx, input, '`'); + break; + case '>': + redir_fd = redirect_opt_num(dest); + done_word(dest, ctx); + redir_style=REDIRECT_OVERWRITE; + if (next == '>') { + redir_style=REDIRECT_APPEND; + b_getch(input); + } else if (next == '(') { + syntax(); /* until we support >(list) Process Substitution */ + return 1; + } + setup_redirect(ctx, redir_fd, redir_style, input); + break; + case '<': + redir_fd = redirect_opt_num(dest); + done_word(dest, ctx); + redir_style=REDIRECT_INPUT; + if (next == '<') { + redir_style=REDIRECT_HEREIS; + b_getch(input); + } else if (next == '>') { + redir_style=REDIRECT_IO; + b_getch(input); + } else if (next == '(') { + syntax(); /* until we support <(list) Process Substitution */ + return 1; + } + setup_redirect(ctx, redir_fd, redir_style, input); + break; + case ';': + done_word(dest, ctx); + done_pipe(ctx,PIPE_SEQ); + break; + case '&': + done_word(dest, ctx); + if (next=='&') { + b_getch(input); + done_pipe(ctx,PIPE_AND); + } else { + done_pipe(ctx,PIPE_BG); + } + break; + case '|': + done_word(dest, ctx); + if (next=='|') { + b_getch(input); + done_pipe(ctx,PIPE_OR); + } else { + /* we could pick up a file descriptor choice here + * with redirect_opt_num(), but bash doesn't do it. + * "echo foo 2| cat" yields "foo 2". */ + done_command(ctx); + } + break; + case '(': + case '{': + if (parse_group(dest, ctx, input, ch)!=0) return 1; + break; + case ')': + case '}': + syntax(); /* Proper use of this character caught by end_trigger */ + return 1; + break; + default: + syntax(); /* this is really an internal logic error */ + return 1; + } + } + } + /* complain if quote? No, maybe we just finished a command substitution + * that was quoted. Example: + * $ echo "`cat foo` plus more" + * and we just got the EOF generated by the subshell that ran "cat foo" + * The only real complaint is if we got an EOF when end_trigger != '\0', + * that is, we were really supposed to get end_trigger, and never got + * one before the EOF. Can't use the standard "syntax error" return code, + * so that parse_stream_outer can distinguish the EOF and exit smoothly. */ + debug_printf("leaving parse_stream (EOF)\n"); + if (end_trigger != '\0') return -1; + return 0; +} + +void mapset(const unsigned char *set, int code) +{ + const unsigned char *s; + for (s=set; *s; s++) map[*s] = code; +} + +void update_ifs_map(void) +{ + /* char *ifs and char map[256] are both globals. */ + ifs = getenv("IFS"); + if (ifs == NULL) ifs=" \t\n"; + /* Precompute a list of 'flow through' behavior so it can be treated + * quickly up front. Computation is necessary because of IFS. + * Special case handling of IFS == " \t\n" is not implemented. + * The map[] array only really needs two bits each, and on most machines + * that would be faster because of the reduced L1 cache footprint. + */ + memset(map,0,sizeof(map)); /* most characters flow through always */ + mapset("\\$'\"`", 3); /* never flow through */ + mapset("<>;&|(){}#", 1); /* flow through if quoted */ + mapset(ifs, 2); /* also flow through if quoted */ +} + +/* most recursion does not come through here, the exception is + * from builtin_source() */ +int parse_stream_outer(struct in_str *inp, int flag) +{ + + struct p_context ctx; + o_string temp=NULL_O_STRING; + int rcode; + do { + ctx.type = flag; + initialize_context(&ctx); + update_ifs_map(); + if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset(";$&|", 0); + inp->promptmode=1; + rcode = parse_stream(&temp, &ctx, inp, '\n'); + if (rcode != 1 && ctx.old_flag != 0) { + syntax(); + } + if (rcode != 1 && ctx.old_flag == 0) { + done_word(&temp, &ctx); + done_pipe(&ctx,PIPE_SEQ); + run_list(ctx.list_head); + } else { + if (ctx.old_flag != 0) { + free(ctx.stack); + b_reset(&temp); + } + temp.nonnull = 0; + temp.quote = 0; + inp->p = NULL; + free_pipe_list(ctx.list_head,0); + } + b_free(&temp); + } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */ + return 0; +} + +static int parse_string_outer(const char *s, int flag) +{ + struct in_str input; + setup_string_in_str(&input, s); + return parse_stream_outer(&input, flag); +} + +static int parse_file_outer(FILE *f) +{ + int rcode; + struct in_str input; + setup_file_in_str(&input, f); + rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); + return rcode; +} + +/* Make sure we have a controlling tty. If we get started under a job + * aware app (like bash for example), make sure we are now in charge so + * we don't fight over who gets the foreground */ +static void setup_job_control(void) +{ + static pid_t shell_pgrp; + /* Loop until we are in the foreground. */ + while (tcgetpgrp (shell_terminal) != (shell_pgrp = getpgrp ())) + kill (- shell_pgrp, SIGTTIN); + + /* Ignore interactive and job-control signals. */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + + /* Put ourselves in our own process group. */ + setsid(); + shell_pgrp = getpid (); + setpgid (shell_pgrp, shell_pgrp); + + /* Grab control of the terminal. */ + tcsetpgrp(shell_terminal, shell_pgrp); +} + +int hush_main(int argc, char **argv) +{ + int opt; + FILE *input; + char **e = environ; + + /* XXX what should these be while sourcing /etc/profile? */ + global_argc = argc; + global_argv = argv; + + /* (re?) initialize globals. Sometimes hush_main() ends up calling + * hush_main(), therefore we cannot rely on the BSS to zero out this + * stuff. Reset these to 0 every time. */ + ifs = NULL; + /* map[] is taken care of with call to update_ifs_map() */ + fake_mode = 0; + interactive = 0; + close_me_head = NULL; + last_bg_pid = 0; + job_list = NULL; + last_jobid = 0; + + /* Initialize some more globals to non-zero values */ + set_cwd(); +#ifdef CONFIG_FEATURE_COMMAND_EDITING + cmdedit_set_initial_prompt(); +#else + PS1 = NULL; +#endif + PS2 = "> "; + + /* initialize our shell local variables with the values + * currently living in the environment */ + if (e) { + for (; *e; e++) + set_local_var(*e, 2); /* without call putenv() */ + } + + last_return_code=EXIT_SUCCESS; + + + if (argv[0] && argv[0][0] == '-') { + debug_printf("\nsourcing /etc/profile\n"); + if ((input = fopen("/etc/profile", "r")) != NULL) { + mark_open(fileno(input)); + parse_file_outer(input); + mark_closed(fileno(input)); + fclose(input); + } + } + input=stdin; + + while ((opt = getopt(argc, argv, "c:xif")) > 0) { + switch (opt) { + case 'c': + { + global_argv = argv+optind; + global_argc = argc-optind; + opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON); + goto final_return; + } + break; + case 'i': + interactive++; + break; + case 'f': + fake_mode++; + break; + default: +#ifndef BB_VER + fprintf(stderr, "Usage: sh [FILE]...\n" + " or: sh -c command [args]...\n\n"); + exit(EXIT_FAILURE); +#else + bb_show_usage(); +#endif + } + } + /* A shell is interactive if the `-i' flag was given, or if all of + * the following conditions are met: + * no -c command + * no arguments remaining or the -s flag given + * standard input is a terminal + * standard output is a terminal + * Refer to Posix.2, the description of the `sh' utility. */ + if (argv[optind]==NULL && input==stdin && + isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { + interactive++; + } + + debug_printf("\ninteractive=%d\n", interactive); + if (interactive) { + /* Looks like they want an interactive shell */ +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET + printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); +#endif + setup_job_control(); + } + + if (argv[optind]==NULL) { + opt=parse_file_outer(stdin); + goto final_return; + } + + debug_printf("\nrunning script '%s'\n", argv[optind]); + global_argv = argv+optind; + global_argc = argc-optind; + input = bb_xfopen(argv[optind], "r"); + opt = parse_file_outer(input); + +#ifdef CONFIG_FEATURE_CLEAN_UP + fclose(input); + if (cwd && cwd != bb_msg_unknown) + free((char*)cwd); + { + struct variables *cur, *tmp; + for(cur = top_vars; cur; cur = tmp) { + tmp = cur->next; + if (!cur->flg_read_only) { + free(cur->name); + free(cur->value); + free(cur); + } + } + } +#endif + +final_return: + return(opt?opt:last_return_code); +} + +static char *insert_var_value(char *inp) +{ + int res_str_len = 0; + int len; + int done = 0; + char *p, *p1, *res_str = NULL; + + while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) { + if (p != inp) { + len = p - inp; + res_str = xrealloc(res_str, (res_str_len + len)); + strncpy((res_str + res_str_len), inp, len); + res_str_len += len; + } + inp = ++p; + p = strchr(inp, SPECIAL_VAR_SYMBOL); + *p = '\0'; + if ((p1 = lookup_param(inp))) { + len = res_str_len + strlen(p1); + res_str = xrealloc(res_str, (1 + len)); + strcpy((res_str + res_str_len), p1); + res_str_len = len; + } + *p = SPECIAL_VAR_SYMBOL; + inp = ++p; + done = 1; + } + if (done) { + res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp))); + strcpy((res_str + res_str_len), inp); + while ((p = strchr(res_str, '\n'))) { + *p = ' '; + } + } + return (res_str == NULL) ? inp : res_str; +} + +static char **make_list_in(char **inp, char *name) +{ + int len, i; + int name_len = strlen(name); + int n = 0; + char **list; + char *p1, *p2, *p3; + + /* create list of variable values */ + list = xmalloc(sizeof(*list)); + for (i = 0; inp[i]; i++) { + p3 = insert_var_value(inp[i]); + p1 = p3; + while (*p1) { + if ((*p1 == ' ')) { + p1++; + continue; + } + if ((p2 = strchr(p1, ' '))) { + len = p2 - p1; + } else { + len = strlen(p1); + p2 = p1 + len; + } + /* we use n + 2 in realloc for list,because we add + * new element and then we will add NULL element */ + list = xrealloc(list, sizeof(*list) * (n + 2)); + list[n] = xmalloc(2 + name_len + len); + strcpy(list[n], name); + strcat(list[n], "="); + strncat(list[n], p1, len); + list[n++][name_len + len + 1] = '\0'; + p1 = p2; + } + if (p3 != inp[i]) free(p3); + } + list[n] = NULL; + return list; +} + +/* Make new string for parser */ +static char * make_string(char ** inp) +{ + char *p; + char *str = NULL; + int n; + int len = 2; + + for (n = 0; inp[n]; n++) { + p = insert_var_value(inp[n]); + str = xrealloc(str, (len + strlen(p))); + if (n) { + strcat(str, " "); + } else { + *str = '\0'; + } + strcat(str, p); + len = strlen(str) + 3; + if (p != inp[n]) free(p); + } + len = strlen(str); + *(str + len) = '\n'; + *(str + len + 1) = '\0'; + return str; +} diff --git a/busybox/shell/lash.c b/busybox/shell/lash.c new file mode 100644 index 000000000..f454e6990 --- /dev/null +++ b/busybox/shell/lash.c @@ -0,0 +1,1696 @@ +/* vi: set sw=4 ts=4: */ +/* + * lash -- the BusyBox Lame-Ass SHell + * + * Copyright (C) 1999-2004 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, msh, or ash. This is + * still a very useful, small shell -- it just don't need any more + * features beyond what it already has... + */ + +//For debugging/development on the shell only... +//#define DEBUG_SHELL + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "cmdedit.h" + +#ifdef CONFIG_LOCALE_SUPPORT +#include +#endif + +#include +#define expand_t glob_t + +/* Always enable for the moment... */ +#define CONFIG_LASH_PIPE_N_REDIRECTS +#define CONFIG_LASH_JOB_CONTROL + +static const int MAX_READ = 128; /* size of input buffer for `read' builtin */ +#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" + + +#ifdef CONFIG_LASH_PIPE_N_REDIRECTS +enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE, + REDIRECT_APPEND +}; +#endif + +static const unsigned int DEFAULT_CONTEXT=0x1; +static const unsigned int IF_TRUE_CONTEXT=0x2; +static const unsigned int IF_FALSE_CONTEXT=0x4; +static const unsigned int THEN_EXP_CONTEXT=0x8; +static const unsigned int ELSE_EXP_CONTEXT=0x10; + +#ifdef CONFIG_LASH_PIPE_N_REDIRECTS +struct redir_struct { + enum redir_type type; /* type of redirection */ + int fd; /* file descriptor being redirected */ + char *filename; /* file to redirect fd to */ +}; +#endif + +struct child_prog { + pid_t pid; /* 0 if exited */ + char **argv; /* program name and arguments */ + int num_redirects; /* elements in redirection array */ + int is_stopped; /* is the program currently running? */ + struct job *family; /* pointer back to the child's parent job */ +#ifdef CONFIG_LASH_PIPE_N_REDIRECTS + struct redir_struct *redirects; /* I/O redirects */ +#endif +}; + +struct jobset { + struct job *head; /* head of list of running jobs */ + struct job *fg; /* current foreground job */ +}; + +struct job { + int jobid; /* job number */ + int num_progs; /* total number of programs in job */ + int running_progs; /* number of programs running */ + char *text; /* name of job */ + char *cmdbuf; /* buffer various argv's point into */ + pid_t pgrp; /* process group ID for the job */ + struct child_prog *progs; /* array of programs in job */ + struct job *next; /* to track background commands */ + int stopped_progs; /* number of programs alive, but stopped */ + unsigned int job_context; /* bitmask defining current context */ + struct jobset *job_list; +}; + +struct built_in_command { + char *cmd; /* name */ + char *descr; /* description */ + int (*function) (struct child_prog *); /* function ptr */ +}; + +struct close_me { + int fd; + struct close_me *next; +}; + +/* function prototypes for builtins */ +static int builtin_cd(struct child_prog *cmd); +static int builtin_exec(struct child_prog *cmd); +static int builtin_exit(struct child_prog *cmd); +static int builtin_fg_bg(struct child_prog *cmd); +static int builtin_help(struct child_prog *cmd); +static int builtin_jobs(struct child_prog *dummy); +static int builtin_pwd(struct child_prog *dummy); +static int builtin_export(struct child_prog *cmd); +static int builtin_source(struct child_prog *cmd); +static int builtin_unset(struct child_prog *cmd); +static int builtin_read(struct child_prog *cmd); + + +/* function prototypes for shell stuff */ +static void mark_open(int fd); +static void mark_closed(int fd); +static void close_all(void); +static void checkjobs(struct jobset *job_list); +static void remove_job(struct jobset *j_list, struct job *job); +static int get_command(FILE * source, char *command); +static int parse_command(char **command_ptr, struct job *job, int *inbg); +static int run_command(struct job *newjob, int inbg, int outpipe[2]); +static int pseudo_exec(struct child_prog *cmd) __attribute__ ((noreturn)); +static int busy_loop(FILE * input); + + +/* Table of built-in functions (these are non-forking builtins, meaning they + * can change global variables in the parent shell process but they will not + * work with pipes and redirects; 'unset foo | whatever' will not work) */ +static struct built_in_command bltins[] = { + {"bg", "Resume a job in the background", builtin_fg_bg}, + {"cd", "Change working directory", builtin_cd}, + {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, + {"exit", "Exit from shell()", builtin_exit}, + {"fg", "Bring job into the foreground", builtin_fg_bg}, + {"jobs", "Lists the active jobs", builtin_jobs}, + {"export", "Set environment variable", builtin_export}, + {"unset", "Unset environment variable", builtin_unset}, + {"read", "Input environment variable", builtin_read}, + {".", "Source-in and run commands in a file", builtin_source}, + /* to do: add ulimit */ + {NULL, NULL, NULL} +}; + +/* Table of forking built-in functions (things that fork cannot change global + * variables in the parent process, such as the current working directory) */ +static struct built_in_command bltins_forking[] = { + {"pwd", "Print current directory", builtin_pwd}, + {"help", "List shell built-in commands", builtin_help}, + {NULL, NULL, NULL} +}; + + +static int shell_context; /* Type prompt trigger (PS1 or PS2) */ + + +/* Globals that are static to this file */ +static const char *cwd; +static char *local_pending_command = NULL; +static struct jobset job_list = { NULL, NULL }; +static int argc; +static char **argv; +static struct close_me *close_me_head; +static int last_return_code; +static int last_bg_pid; +static unsigned int last_jobid; +static int shell_terminal; +static char *PS1; +static char *PS2 = "> "; + + +#ifdef DEBUG_SHELL +static inline void debug_printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} +#else +static inline void debug_printf(const char *format, ...) { } +#endif + +/* + Most builtins need access to the struct child_prog that has + their arguments, previously coded as cmd->progs[0]. That coding + can exhibit a bug, if the builtin is not the first command in + a pipeline: "echo foo | exec sort" will attempt to exec foo. + +builtin previous use notes +------ ----------------- --------- +cd cmd->progs[0] +exec cmd->progs[0] squashed bug: didn't look for applets or forking builtins +exit cmd->progs[0] +fg_bg cmd->progs[0], job_list->head, job_list->fg +help 0 +jobs job_list->head +pwd 0 +export cmd->progs[0] +source cmd->progs[0] +unset cmd->progs[0] +read cmd->progs[0] + +I added "struct job *family;" to struct child_prog, +and switched API to builtin_foo(struct child_prog *child); +So cmd->text becomes child->family->text + cmd->job_context becomes child->family->job_context + cmd->progs[0] becomes *child + job_list becomes child->family->job_list + */ + +/* built-in 'cd ' handler */ +static int builtin_cd(struct child_prog *child) +{ + char *newdir; + + if (child->argv[1] == NULL) + newdir = getenv("HOME"); + else + newdir = child->argv[1]; + if (chdir(newdir)) { + printf("cd: %s: %m\n", newdir); + return EXIT_FAILURE; + } + cwd = xgetcwd((char *)cwd); + if (!cwd) + cwd = bb_msg_unknown; + return EXIT_SUCCESS; +} + +/* built-in 'exec' handler */ +static int builtin_exec(struct child_prog *child) +{ + if (child->argv[1] == NULL) + return EXIT_SUCCESS; /* Really? */ + child->argv++; + close_all(); + pseudo_exec(child); + /* never returns */ +} + +/* built-in 'exit' handler */ +static int builtin_exit(struct child_prog *child) +{ + if (child->argv[1] == NULL) + exit(EXIT_SUCCESS); + + exit (atoi(child->argv[1])); +} + +/* built-in 'fg' and 'bg' handler */ +static int builtin_fg_bg(struct child_prog *child) +{ + int i, jobnum; + struct job *job=NULL; + + /* If they gave us no args, assume they want the last backgrounded task */ + if (!child->argv[1]) { + for (job = child->family->job_list->head; job; job = job->next) { + if (job->jobid == last_jobid) { + break; + } + } + if (!job) { + bb_error_msg("%s: no current job", child->argv[0]); + return EXIT_FAILURE; + } + } else { + if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { + bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); + return EXIT_FAILURE; + } + for (job = child->family->job_list->head; job; job = job->next) { + if (job->jobid == jobnum) { + break; + } + } + if (!job) { + bb_error_msg("%s: %d: no such job", child->argv[0], jobnum); + return EXIT_FAILURE; + } + } + + if (*child->argv[0] == 'f') { + /* Put the job into the foreground. */ + tcsetpgrp(shell_terminal, job->pgrp); + + child->family->job_list->fg = job; + } + + /* Restart the processes in the job */ + for (i = 0; i < job->num_progs; i++) + job->progs[i].is_stopped = 0; + + job->stopped_progs = 0; + + if ( (i=kill(- job->pgrp, SIGCONT)) < 0) { + if (i == ESRCH) { + remove_job(&job_list, job); + } else { + bb_perror_msg("kill (SIGCONT)"); + } + } + + return EXIT_SUCCESS; +} + +/* built-in 'help' handler */ +static int builtin_help(struct child_prog *dummy) +{ + struct built_in_command *x; + + printf("\nBuilt-in commands:\n"); + printf("-------------------\n"); + for (x = bltins; x->cmd; x++) { + if (x->descr==NULL) + continue; + printf("%s\t%s\n", x->cmd, x->descr); + } + for (x = bltins_forking; x->cmd; x++) { + if (x->descr==NULL) + continue; + printf("%s\t%s\n", x->cmd, x->descr); + } + printf("\n\n"); + return EXIT_SUCCESS; +} + +/* built-in 'jobs' handler */ +static int builtin_jobs(struct child_prog *child) +{ + struct job *job; + char *status_string; + + for (job = child->family->job_list->head; job; job = job->next) { + if (job->running_progs == job->stopped_progs) + status_string = "Stopped"; + else + status_string = "Running"; + + printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text); + } + return EXIT_SUCCESS; +} + + +/* built-in 'pwd' handler */ +static int builtin_pwd(struct child_prog *dummy) +{ + cwd = xgetcwd((char *)cwd); + if (!cwd) + cwd = bb_msg_unknown; + puts(cwd); + return EXIT_SUCCESS; +} + +/* built-in 'export VAR=value' handler */ +static int builtin_export(struct child_prog *child) +{ + int res; + char *v = child->argv[1]; + + if (v == NULL) { + char **e; + for (e = environ; *e; e++) { + puts(*e); + } + return 0; + } + res = putenv(v); + if (res) + fprintf(stderr, "export: %m\n"); +#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT + if (strncmp(v, "PS1=", 4)==0) + PS1 = getenv("PS1"); +#endif + +#ifdef CONFIG_LOCALE_SUPPORT + if(strncmp(v, "LC_ALL=", 7)==0) + setlocale(LC_ALL, getenv("LC_ALL")); + if(strncmp(v, "LC_CTYPE=", 9)==0) + setlocale(LC_CTYPE, getenv("LC_CTYPE")); +#endif + + return (res); +} + +/* built-in 'read VAR' handler */ +static int builtin_read(struct child_prog *child) +{ + int res = 0, len, newlen; + char *s; + char string[MAX_READ]; + + if (child->argv[1]) { + /* argument (VAR) given: put "VAR=" into buffer */ + safe_strncpy(string, child->argv[1], MAX_READ-1); + len = strlen(string); + string[len++] = '='; + string[len] = '\0'; + fgets(&string[len], sizeof(string) - len, stdin); /* read string */ + newlen = strlen(string); + if(newlen > len) + string[--newlen] = '\0'; /* chomp trailing newline */ + /* + ** string should now contain "VAR=" + ** copy it (putenv() won't do that, so we must make sure + ** the string resides in a static buffer!) + */ + res = -1; + if((s = strdup(string))) + res = putenv(s); + if (res) + fprintf(stderr, "read: %m\n"); + } + else + fgets(string, sizeof(string), stdin); + + return (res); +} + +/* Built-in '.' handler (read-in and execute commands from file) */ +static int builtin_source(struct child_prog *child) +{ + FILE *input; + int status; + int fd; + + if (child->argv[1] == NULL) + return EXIT_FAILURE; + + input = fopen(child->argv[1], "r"); + if (!input) { + printf( "Couldn't open file '%s'\n", child->argv[1]); + return EXIT_FAILURE; + } + + fd=fileno(input); + mark_open(fd); + /* Now run the file */ + status = busy_loop(input); + fclose(input); + mark_closed(fd); + return (status); +} + +/* built-in 'unset VAR' handler */ +static int builtin_unset(struct child_prog *child) +{ + if (child->argv[1] == NULL) { + printf( "unset: parameter required.\n"); + return EXIT_FAILURE; + } + unsetenv(child->argv[1]); + return EXIT_SUCCESS; +} + +static void mark_open(int fd) +{ + struct close_me *new = xmalloc(sizeof(struct close_me)); + new->fd = fd; + new->next = close_me_head; + close_me_head = new; +} + +static void mark_closed(int fd) +{ + struct close_me *tmp; + if (close_me_head == NULL || close_me_head->fd != fd) + bb_error_msg_and_die("corrupt close_me"); + tmp = close_me_head; + close_me_head = close_me_head->next; + free(tmp); +} + +static void close_all() +{ + struct close_me *c, *tmp; + for (c=close_me_head; c; c=tmp) { + close(c->fd); + tmp=c->next; + free(c); + } + close_me_head = NULL; +} + + +#ifdef CONFIG_LASH_JOB_CONTROL +/* free up all memory from a job */ +static void free_job(struct job *cmd) +{ + int i; + struct jobset *keep; + + for (i = 0; i < cmd->num_progs; i++) { + free(cmd->progs[i].argv); +#ifdef CONFIG_LASH_PIPE_N_REDIRECTS + if (cmd->progs[i].redirects) + free(cmd->progs[i].redirects); +#endif + } + free(cmd->progs); + free(cmd->text); + free(cmd->cmdbuf); + keep = cmd->job_list; + memset(cmd, 0, sizeof(struct job)); + cmd->job_list = keep; +} + +/* remove a job from a jobset */ +static void remove_job(struct jobset *j_list, struct job *job) +{ + struct job *prevjob; + + free_job(job); + if (job == j_list->head) { + j_list->head = job->next; + } else { + prevjob = j_list->head; + while (prevjob->next != job) + prevjob = prevjob->next; + prevjob->next = job->next; + } + + if (j_list->head) + last_jobid = j_list->head->jobid; + else + last_jobid = 0; + + free(job); +} + +/* Checks to see if any background processes have exited -- if they + have, figure out why and see if a job has completed */ +static void checkjobs(struct jobset *j_list) +{ + struct job *job; + pid_t childpid; + int status; + int prognum = 0; + + while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { + for (job = j_list->head; job; job = job->next) { + prognum = 0; + while (prognum < job->num_progs && + job->progs[prognum].pid != childpid) prognum++; + if (prognum < job->num_progs) + break; + } + + /* This happens on backticked commands */ + if(job==NULL) + return; + + if (WIFEXITED(status) || WIFSIGNALED(status)) { + /* child exited */ + job->running_progs--; + job->progs[prognum].pid = 0; + + if (!job->running_progs) { + printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text); + last_jobid=0; + remove_job(j_list, job); + } + } else { + /* child stopped */ + job->stopped_progs++; + job->progs[prognum].is_stopped = 1; + +#if 0 + /* Printing this stuff is a pain, since it tends to + * overwrite the prompt an inconveinient moments. So + * don't do that. */ + if (job->stopped_progs == job->num_progs) { + printf(JOB_STATUS_FORMAT, job->jobid, "Stopped", + job->text); + } +#endif + } + } + + if (childpid == -1 && errno != ECHILD) + bb_perror_msg("waitpid"); +} +#else +static void checkjobs(struct jobset *j_list) +{ +} +static void free_job(struct job *cmd) +{ +} +static void remove_job(struct jobset *j_list, struct job *job) +{ +} +#endif + +#ifdef CONFIG_LASH_PIPE_N_REDIRECTS +/* squirrel != NULL means we squirrel away copies of stdin, stdout, + * and stderr if they are redirected. */ +static int setup_redirects(struct child_prog *prog, int squirrel[]) +{ + int i; + int openfd; + int mode = O_RDONLY; + struct redir_struct *redir = prog->redirects; + + for (i = 0; i < prog->num_redirects; i++, redir++) { + switch (redir->type) { + case REDIRECT_INPUT: + mode = O_RDONLY; + break; + case REDIRECT_OVERWRITE: + mode = O_WRONLY | O_CREAT | O_TRUNC; + break; + case REDIRECT_APPEND: + mode = O_WRONLY | O_CREAT | O_APPEND; + break; + } + + openfd = open(redir->filename, mode, 0666); + if (openfd < 0) { + /* this could get lost if stderr has been redirected, but + bash and ash both lose it as well (though zsh doesn't!) */ + bb_perror_msg("error opening %s", redir->filename); + return 1; + } + + if (openfd != redir->fd) { + if (squirrel && redir->fd < 3) { + squirrel[redir->fd] = dup(redir->fd); + fcntl (squirrel[redir->fd], F_SETFD, FD_CLOEXEC); + } + dup2(openfd, redir->fd); + close(openfd); + } + } + + return 0; +} + +static void restore_redirects(int squirrel[]) +{ + int i, fd; + for (i=0; i<3; i++) { + fd = squirrel[i]; + if (fd != -1) { + /* No error checking. I sure wouldn't know what + * to do with an error if I found one! */ + dup2(fd, i); + close(fd); + } + } +} +#else +static inline int setup_redirects(struct child_prog *prog, int squirrel[]) +{ + return 0; +} +static inline void restore_redirects(int squirrel[]) +{ +} +#endif + +static inline void cmdedit_set_initial_prompt(void) +{ +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + PS1 = NULL; +#else + PS1 = getenv("PS1"); + if(PS1==0) + PS1 = "\\w \\$ "; +#endif +} + +static inline void setup_prompt_string(char **prompt_str) +{ +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + /* Set up the prompt */ + if (shell_context == 0) { + free(PS1); + PS1=xmalloc(strlen(cwd)+4); + sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# "); + *prompt_str = PS1; + } else { + *prompt_str = PS2; + } +#else + *prompt_str = (shell_context==0)? PS1 : PS2; +#endif +} + +static int get_command(FILE * source, char *command) +{ + char *prompt_str; + + if (source == NULL) { + if (local_pending_command) { + /* a command specified (-c option): return it & mark it done */ + strcpy(command, local_pending_command); + free(local_pending_command); + local_pending_command = NULL; + return 0; + } + return 1; + } + + if (source == stdin) { + setup_prompt_string(&prompt_str); + +#ifdef CONFIG_FEATURE_COMMAND_EDITING + /* + ** enable command line editing only while a command line + ** is actually being read; otherwise, we'll end up bequeathing + ** atexit() handlers and other unwanted stuff to our + ** child processes (rob@sysgo.de) + */ + cmdedit_read_input(prompt_str, command); + return 0; +#else + fputs(prompt_str, stdout); +#endif + } + + if (!fgets(command, BUFSIZ - 2, source)) { + if (source == stdin) + printf("\n"); + return 1; + } + + return 0; +} + +static char* itoa(register int i) +{ + static char a[7]; /* Max 7 ints */ + register char *b = a + sizeof(a) - 1; + int sign = (i < 0); + + if (sign) + i = -i; + *b = 0; + do + { + *--b = '0' + (i % 10); + i /= 10; + } + while (i); + if (sign) + *--b = '-'; + return b; +} + +char * strsep_space( char *string, int * ix) +{ + char *token, *begin; + + begin = string; + + /* Short circuit the trivial case */ + if ( !string || ! string[*ix]) + return NULL; + + /* Find the end of the token. */ + while( string && string[*ix] && !isspace(string[*ix]) ) { + (*ix)++; + } + + /* Find the end of any whitespace trailing behind + * the token and let that be part of the token */ + while( string && string[*ix] && isspace(string[*ix]) ) { + (*ix)++; + } + + if (! string && *ix==0) { + /* Nothing useful was found */ + return NULL; + } + + token = xmalloc(*ix+1); + token[*ix] = '\0'; + strncpy(token, string, *ix); + + return token; +} + +static int expand_arguments(char *command) +{ + int total_length=0, length, i, retval, ix = 0; + expand_t expand_result; + char *tmpcmd, *cmd, *cmd_copy; + char *src, *dst, *var; + const char *out_of_space = "out of space during expansion"; + int flags = GLOB_NOCHECK +#ifdef GLOB_BRACE + | GLOB_BRACE +#endif +#ifdef GLOB_TILDE + | GLOB_TILDE +#endif + ; + + /* get rid of the terminating \n */ + chomp(command); + + /* Fix up escape sequences to be the Real Thing(tm) */ + while( command && command[ix]) { + if (command[ix] == '\\') { + const char *tmp = command+ix+1; + command[ix] = bb_process_escape_sequence( &tmp ); + memmove(command+ix + 1, tmp, strlen(tmp)+1); + } + ix++; + } + /* Use glob and then fixup environment variables and such */ + + /* It turns out that glob is very stupid. We have to feed it one word at a + * time since it can't cope with a full string. Here we convert command + * (char*) into cmd (char**, one word per string) */ + + /* We need a clean copy, so strsep can mess up the copy while + * we write stuff into the original (in a minute) */ + cmd = cmd_copy = bb_xstrdup(command); + *command = '\0'; + for (ix = 0, tmpcmd = cmd; + (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) { + if (*tmpcmd == '\0') + break; + /* we need to trim() the result for glob! */ + trim(tmpcmd); + retval = glob(tmpcmd, flags, NULL, &expand_result); + free(tmpcmd); /* Free mem allocated by strsep_space */ + if (retval == GLOB_NOSPACE) { + /* Mem may have been allocated... */ + globfree (&expand_result); + bb_error_msg(out_of_space); + return FALSE; + } else if (retval != 0) { + /* Some other error. GLOB_NOMATCH shouldn't + * happen because of the GLOB_NOCHECK flag in + * the glob call. */ + bb_error_msg("syntax error"); + return FALSE; + } else { + /* Convert from char** (one word per string) to a simple char*, + * but don't overflow command which is BUFSIZ in length */ + for (i=0; i < expand_result.gl_pathc; i++) { + length=strlen(expand_result.gl_pathv[i]); + if (total_length+length+1 >= BUFSIZ) { + bb_error_msg(out_of_space); + return FALSE; + } + strcat(command+total_length, " "); + total_length+=1; + strcat(command+total_length, expand_result.gl_pathv[i]); + total_length+=length; + } + globfree (&expand_result); + } + } + free(cmd_copy); + trim(command); + + /* Now do the shell variable substitutions which + * wordexp can't do for us, namely $? and $! */ + src = command; + while((dst = strchr(src,'$')) != NULL){ + var = NULL; + switch(*(dst+1)) { + case '?': + var = itoa(last_return_code); + break; + case '!': + if (last_bg_pid==-1) + *(var)='\0'; + else + var = itoa(last_bg_pid); + break; + /* Everything else like $$, $#, $[0-9], etc. should all be + * expanded by wordexp(), so we can in theory skip that stuff + * here, but just to be on the safe side (i.e., since uClibc + * wordexp doesn't do this stuff yet), lets leave it in for + * now. */ + case '$': + var = itoa(getpid()); + break; + case '#': + var = itoa(argc-1); + break; + case '0':case '1':case '2':case '3':case '4': + case '5':case '6':case '7':case '8':case '9': + { + int ixx=*(dst+1)-48+1; + if (ixx >= argc) { + var='\0'; + } else { + var = argv[ixx]; + } + } + break; + + } + if (var) { + /* a single character construction was found, and + * already handled in the case statement */ + src=dst+2; + } else { + /* Looks like an environment variable */ + char delim_hold; + int num_skip_chars=0; + int dstlen = strlen(dst); + /* Is this a ${foo} type variable? */ + if (dstlen >=2 && *(dst+1) == '{') { + src=strchr(dst+1, '}'); + num_skip_chars=1; + } else { + src=dst+1; + while(isalnum(*src) || *src=='_') src++; + } + if (src == NULL) { + src = dst+dstlen; + } + delim_hold=*src; + *src='\0'; /* temporary */ + var = getenv(dst + 1 + num_skip_chars); + *src=delim_hold; + src += num_skip_chars; + } + if (var == NULL) { + /* Seems we got an un-expandable variable. So delete it. */ + var = ""; + } + { + int subst_len = strlen(var); + int trail_len = strlen(src); + if (dst+subst_len+trail_len >= command+BUFSIZ) { + bb_error_msg(out_of_space); + return FALSE; + } + /* Move stuff to the end of the string to accommodate + * filling the created gap with the new stuff */ + memmove(dst+subst_len, src, trail_len+1); + /* Now copy in the new stuff */ + memcpy(dst, var, subst_len); + src = dst+subst_len; + } + } + + return TRUE; +} + +/* Return cmd->num_progs as 0 if no command is present (e.g. an empty + line). If a valid command is found, command_ptr is set to point to + the beginning of the next command (if the original command had more + then one job associated with it) or NULL if no more commands are + present. */ +static int parse_command(char **command_ptr, struct job *job, int *inbg) +{ + char *command; + char *return_command = NULL; + char *src, *buf; + int argc_l = 0; + int done = 0; + int argv_alloced; + int saw_quote = 0; + char quote = '\0'; + int count; + struct child_prog *prog; +#ifdef CONFIG_LASH_PIPE_N_REDIRECTS + int i; + char *chptr; +#endif + + /* skip leading white space */ + while (**command_ptr && isspace(**command_ptr)) + (*command_ptr)++; + + /* this handles empty lines or leading '#' characters */ + if (!**command_ptr || (**command_ptr == '#')) { + job->num_progs=0; + return 0; + } + + *inbg = 0; + job->num_progs = 1; + job->progs = xmalloc(sizeof(*job->progs)); + + /* We set the argv elements to point inside of this string. The + memory is freed by free_job(). Allocate twice the original + length in case we need to quote every single character. + + Getting clean memory relieves us of the task of NULL + terminating things and makes the rest of this look a bit + cleaner (though it is, admittedly, a tad less efficient) */ + job->cmdbuf = command = xcalloc(2*strlen(*command_ptr) + 1, sizeof(char)); + job->text = NULL; + + prog = job->progs; + prog->num_redirects = 0; + prog->is_stopped = 0; + prog->family = job; +#ifdef CONFIG_LASH_PIPE_N_REDIRECTS + prog->redirects = NULL; +#endif + + argv_alloced = 5; + prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced); + prog->argv[0] = job->cmdbuf; + + buf = command; + src = *command_ptr; + while (*src && !done) { + if (quote == *src) { + quote = '\0'; + } else if (quote) { + if (*src == '\\') { + src++; + if (!*src) { + bb_error_msg("character expected after \\"); + free_job(job); + return 1; + } + + /* in shell, "\'" should yield \' */ + if (*src != quote) { + *buf++ = '\\'; + *buf++ = '\\'; + } + } else if (*src == '*' || *src == '?' || *src == '[' || + *src == ']') *buf++ = '\\'; + *buf++ = *src; + } else if (isspace(*src)) { + if (*prog->argv[argc_l] || saw_quote) { + buf++, argc_l++; + /* +1 here leaves room for the NULL which ends argv */ + if ((argc_l + 1) == argv_alloced) { + argv_alloced += 5; + prog->argv = xrealloc(prog->argv, + sizeof(*prog->argv) * + argv_alloced); + } + prog->argv[argc_l] = buf; + saw_quote = 0; + } + } else + switch (*src) { + case '"': + case '\'': + quote = *src; + saw_quote = 1; + break; + + case '#': /* comment */ + if (*(src-1)== '$') + *buf++ = *src; + else + done = 1; + break; + +#ifdef CONFIG_LASH_PIPE_N_REDIRECTS + case '>': /* redirects */ + case '<': + i = prog->num_redirects++; + prog->redirects = xrealloc(prog->redirects, + sizeof(*prog->redirects) * + (i + 1)); + + prog->redirects[i].fd = -1; + if (buf != prog->argv[argc_l]) { + /* the stuff before this character may be the file number + being redirected */ + prog->redirects[i].fd = + strtol(prog->argv[argc_l], &chptr, 10); + + if (*chptr && *prog->argv[argc_l]) { + buf++, argc_l++; + prog->argv[argc_l] = buf; + } + } + + if (prog->redirects[i].fd == -1) { + if (*src == '>') + prog->redirects[i].fd = 1; + else + prog->redirects[i].fd = 0; + } + + if (*src++ == '>') { + if (*src == '>') + prog->redirects[i].type = + REDIRECT_APPEND, src++; + else + prog->redirects[i].type = REDIRECT_OVERWRITE; + } else { + prog->redirects[i].type = REDIRECT_INPUT; + } + + /* This isn't POSIX sh compliant. Oh well. */ + chptr = src; + while (isspace(*chptr)) + chptr++; + + if (!*chptr) { + bb_error_msg("file name expected after %c", *(src-1)); + free_job(job); + job->num_progs=0; + return 1; + } + + prog->redirects[i].filename = buf; + while (*chptr && !isspace(*chptr)) + *buf++ = *chptr++; + + src = chptr - 1; /* we src++ later */ + prog->argv[argc_l] = ++buf; + break; + + case '|': /* pipe */ + /* finish this command */ + if (*prog->argv[argc_l] || saw_quote) + argc_l++; + if (!argc_l) { + bb_error_msg("empty command in pipe"); + free_job(job); + job->num_progs=0; + return 1; + } + prog->argv[argc_l] = NULL; + + /* and start the next */ + job->num_progs++; + job->progs = xrealloc(job->progs, + sizeof(*job->progs) * job->num_progs); + prog = job->progs + (job->num_progs - 1); + prog->num_redirects = 0; + prog->redirects = NULL; + prog->is_stopped = 0; + prog->family = job; + argc_l = 0; + + argv_alloced = 5; + prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced); + prog->argv[0] = ++buf; + + src++; + while (*src && isspace(*src)) + src++; + + if (!*src) { + bb_error_msg("empty command in pipe"); + free_job(job); + job->num_progs=0; + return 1; + } + src--; /* we'll ++ it at the end of the loop */ + + break; +#endif + +#ifdef CONFIG_LASH_JOB_CONTROL + case '&': /* background */ + *inbg = 1; +#endif + case ';': /* multiple commands */ + done = 1; + return_command = *command_ptr + (src - *command_ptr) + 1; + break; + + case '\\': + src++; + if (!*src) { + bb_error_msg("character expected after \\"); + free_job(job); + return 1; + } + if (*src == '*' || *src == '[' || *src == ']' + || *src == '?') *buf++ = '\\'; + /* fallthrough */ + default: + *buf++ = *src; + } + + src++; + } + + if (*prog->argv[argc_l] || saw_quote) { + argc_l++; + } + if (!argc_l) { + free_job(job); + return 0; + } + prog->argv[argc_l] = NULL; + + if (!return_command) { + job->text = xmalloc(strlen(*command_ptr) + 1); + strcpy(job->text, *command_ptr); + } else { + /* This leaves any trailing spaces, which is a bit sloppy */ + count = return_command - *command_ptr; + job->text = xmalloc(count + 1); + strncpy(job->text, *command_ptr, count); + job->text[count] = '\0'; + } + + *command_ptr = return_command; + + return 0; +} + +/* Run the child_prog, no matter what kind of command it uses. + */ +static int pseudo_exec(struct child_prog *child) +{ + struct built_in_command *x; +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + char *name; +#endif + + /* Check if the command matches any of the non-forking builtins. + * Depending on context, this might be redundant. But it's + * easier to waste a few CPU cycles than it is to figure out + * if this is one of those cases. + */ + for (x = bltins; x->cmd; x++) { + if (strcmp(child->argv[0], x->cmd) == 0 ) { + _exit(x->function(child)); + } + } + + /* Check if the command matches any of the forking builtins. */ + for (x = bltins_forking; x->cmd; x++) { + if (strcmp(child->argv[0], x->cmd) == 0) { + bb_applet_name=x->cmd; + _exit (x->function(child)); + } + } +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + /* Check if the command matches any busybox internal + * commands ("applets") here. Following discussions from + * November 2000 on busybox@busybox.net, don't use + * bb_get_last_path_component(). This way explicit (with + * slashes) filenames will never be interpreted as an + * applet, just like with builtins. This way the user can + * override an applet with an explicit filename reference. + * The only downside to this change is that an explicit + * /bin/foo invocation will fork and exec /bin/foo, even if + * /bin/foo is a symlink to busybox. + */ + name = child->argv[0]; + + { + char** argv_l=child->argv; + int argc_l; + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++); + optind = 1; + run_applet_by_name(name, argc_l, child->argv); + } +#endif + + execvp(child->argv[0], child->argv); + + /* Do not use bb_perror_msg_and_die() here, since we must not + * call exit() but should call _exit() instead */ + fprintf(stderr, "%s: %m\n", child->argv[0]); + _exit(EXIT_FAILURE); +} + +static void insert_job(struct job *newjob, int inbg) +{ + struct job *thejob; + struct jobset *j_list=newjob->job_list; + + /* find the ID for thejob to use */ + newjob->jobid = 1; + for (thejob = j_list->head; thejob; thejob = thejob->next) + if (thejob->jobid >= newjob->jobid) + newjob->jobid = thejob->jobid + 1; + + /* add thejob to the list of running jobs */ + if (!j_list->head) { + thejob = j_list->head = xmalloc(sizeof(*thejob)); + } else { + for (thejob = j_list->head; thejob->next; thejob = thejob->next) /* nothing */; + thejob->next = xmalloc(sizeof(*thejob)); + thejob = thejob->next; + } + + *thejob = *newjob; /* physically copy the struct job */ + thejob->next = NULL; + thejob->running_progs = thejob->num_progs; + thejob->stopped_progs = 0; + +#ifdef CONFIG_LASH_JOB_CONTROL + if (inbg) { + /* we don't wait for background thejobs to return -- append it + to the list of backgrounded thejobs and leave it alone */ + printf("[%d] %d\n", thejob->jobid, + newjob->progs[newjob->num_progs - 1].pid); + last_jobid = newjob->jobid; + last_bg_pid=newjob->progs[newjob->num_progs - 1].pid; + } else { + newjob->job_list->fg = thejob; + + /* move the new process group into the foreground */ + /* suppress messages when run from /linuxrc mag@sysgo.de */ + if (tcsetpgrp(shell_terminal, newjob->pgrp) && errno != ENOTTY) + bb_perror_msg("tcsetpgrp"); + } +#endif +} + +static int run_command(struct job *newjob, int inbg, int outpipe[2]) +{ + /* struct job *thejob; */ + int i; + int nextin, nextout; + int pipefds[2]; /* pipefd[0] is for reading */ + struct built_in_command *x; + struct child_prog *child; + + nextin = 0, nextout = 1; + for (i = 0; i < newjob->num_progs; i++) { + child = & (newjob->progs[i]); + + if ((i + 1) < newjob->num_progs) { + if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe"); + nextout = pipefds[1]; + } else { + if (outpipe[1]!=-1) { + nextout = outpipe[1]; + } else { + nextout = 1; + } + } + + + /* Check if the command matches any non-forking builtins, + * but only if this is a simple command. + * Non-forking builtins within pipes have to fork anyway, + * and are handled in pseudo_exec. "echo foo | read bar" + * is doomed to failure, and doesn't work on bash, either. + */ + if (newjob->num_progs == 1) { + for (x = bltins; x->cmd; x++) { + if (strcmp(child->argv[0], x->cmd) == 0 ) { + int rcode; + int squirrel[] = {-1, -1, -1}; + setup_redirects(child, squirrel); + rcode = x->function(child); + restore_redirects(squirrel); + return rcode; + } + } + } + +#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) + if (!(child->pid = fork())) +#else + if (!(child->pid = vfork())) +#endif + { + /* Set the handling for job control signals back to the default. */ + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + close_all(); + + if (outpipe[1]!=-1) { + close(outpipe[0]); + } + if (nextin != 0) { + dup2(nextin, 0); + close(nextin); + } + + if (nextout != 1) { + dup2(nextout, 1); + dup2(nextout, 2); /* Really? */ + close(nextout); + close(pipefds[0]); + } + + /* explicit redirects override pipes */ + setup_redirects(child,NULL); + + pseudo_exec(child); + } + if (outpipe[1]!=-1) { + close(outpipe[1]); + } + + /* put our child in the process group whose leader is the + first process in this pipe */ + setpgid(child->pid, newjob->progs[0].pid); + if (nextin != 0) + close(nextin); + if (nextout != 1) + close(nextout); + + /* If there isn't another process, nextin is garbage + but it doesn't matter */ + nextin = pipefds[0]; + } + + newjob->pgrp = newjob->progs[0].pid; + + insert_job(newjob, inbg); + + return 0; +} + +static int busy_loop(FILE * input) +{ + char *command; + char *next_command = NULL; + struct job newjob; + int i; + int inbg; + int status; +#ifdef CONFIG_LASH_JOB_CONTROL + pid_t parent_pgrp; + /* save current owner of TTY so we can restore it on exit */ + parent_pgrp = tcgetpgrp(shell_terminal); +#endif + newjob.job_list = &job_list; + newjob.job_context = DEFAULT_CONTEXT; + + command = (char *) xcalloc(BUFSIZ, sizeof(char)); + + while (1) { + if (!job_list.fg) { + /* no job is in the foreground */ + + /* see if any background processes have exited */ + checkjobs(&job_list); + + if (!next_command) { + if (get_command(input, command)) + break; + next_command = command; + } + + if (! expand_arguments(next_command)) { + free(command); + command = (char *) xcalloc(BUFSIZ, sizeof(char)); + next_command = NULL; + continue; + } + + if (!parse_command(&next_command, &newjob, &inbg) && + newjob.num_progs) { + int pipefds[2] = {-1,-1}; + debug_printf( "job=%p fed to run_command by busy_loop()'\n", + &newjob); + run_command(&newjob, inbg, pipefds); + } + else { + free(command); + command = (char *) xcalloc(BUFSIZ, sizeof(char)); + next_command = NULL; + } + } else { + /* a job is running in the foreground; wait for it */ + i = 0; + while (!job_list.fg->progs[i].pid || + job_list.fg->progs[i].is_stopped == 1) i++; + + if (waitpid(job_list.fg->progs[i].pid, &status, WUNTRACED)<0) { + if (errno != ECHILD) { + bb_perror_msg_and_die("waitpid(%d)",job_list.fg->progs[i].pid); + } + } + + if (WIFEXITED(status) || WIFSIGNALED(status)) { + /* the child exited */ + job_list.fg->running_progs--; + job_list.fg->progs[i].pid = 0; + + last_return_code=WEXITSTATUS(status); + + if (!job_list.fg->running_progs) { + /* child exited */ + remove_job(&job_list, job_list.fg); + job_list.fg = NULL; + } + } +#ifdef CONFIG_LASH_JOB_CONTROL + else { + /* the child was stopped */ + job_list.fg->stopped_progs++; + job_list.fg->progs[i].is_stopped = 1; + + if (job_list.fg->stopped_progs == job_list.fg->running_progs) { + printf("\n" JOB_STATUS_FORMAT, job_list.fg->jobid, + "Stopped", job_list.fg->text); + job_list.fg = NULL; + } + } + + if (!job_list.fg) { + /* move the shell to the foreground */ + /* suppress messages when run from /linuxrc mag@sysgo.de */ + if (tcsetpgrp(shell_terminal, getpgrp()) && errno != ENOTTY) + bb_perror_msg("tcsetpgrp"); + } +#endif + } + } + free(command); + +#ifdef CONFIG_LASH_JOB_CONTROL + /* return controlling TTY back to parent process group before exiting */ + if (tcsetpgrp(shell_terminal, parent_pgrp) && errno != ENOTTY) + bb_perror_msg("tcsetpgrp"); +#endif + + /* return exit status if called with "-c" */ + if (input == NULL && WIFEXITED(status)) + return WEXITSTATUS(status); + + return 0; +} + +#ifdef CONFIG_FEATURE_CLEAN_UP +void free_memory(void) +{ + if (cwd && cwd!=bb_msg_unknown) { + free((char*)cwd); + } + if (local_pending_command) + free(local_pending_command); + + if (job_list.fg && !job_list.fg->running_progs) { + remove_job(&job_list, job_list.fg); + } +} +#endif + +#ifdef CONFIG_LASH_JOB_CONTROL +/* Make sure we have a controlling tty. If we get started under a job + * aware app (like bash for example), make sure we are now in charge so + * we don't fight over who gets the foreground */ +static void setup_job_control(void) +{ + int status; + pid_t shell_pgrp; + + /* Loop until we are in the foreground. */ + while ((status = tcgetpgrp (shell_terminal)) >= 0) { + if (status == (shell_pgrp = getpgrp ())) { + break; + } + kill (- shell_pgrp, SIGTTIN); + } + + /* Ignore interactive and job-control signals. */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + + /* Put ourselves in our own process group. */ + setsid(); + shell_pgrp = getpid (); + setpgid (shell_pgrp, shell_pgrp); + + /* Grab control of the terminal. */ + tcsetpgrp(shell_terminal, shell_pgrp); +} +#else +static inline void setup_job_control(void) +{ +} +#endif + +int lash_main(int argc_l, char **argv_l) +{ + int opt, interactive=FALSE; + FILE *input = stdin; + argc = argc_l; + argv = argv_l; + + /* These variables need re-initializing when recursing */ + last_jobid = 0; + local_pending_command = NULL; + close_me_head = NULL; + job_list.head = NULL; + job_list.fg = NULL; + last_return_code=1; + + if (argv[0] && argv[0][0] == '-') { + FILE *prof_input; + prof_input = fopen("/etc/profile", "r"); + if (prof_input) { + int tmp_fd = fileno(prof_input); + mark_open(tmp_fd); + /* Now run the file */ + busy_loop(prof_input); + fclose(prof_input); + mark_closed(tmp_fd); + } + } + + while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) { + switch (opt) { + case 'c': + input = NULL; + if (local_pending_command != 0) + bb_error_msg_and_die("multiple -c arguments"); + local_pending_command = bb_xstrdup(argv[optind]); + optind++; + argv = argv+optind; + break; + case 'i': + interactive = TRUE; + break; + default: + bb_show_usage(); + } + } + /* A shell is interactive if the `-i' flag was given, or if all of + * the following conditions are met: + * no -c command + * no arguments remaining or the -s flag given + * standard input is a terminal + * standard output is a terminal + * Refer to Posix.2, the description of the `sh' utility. */ + if (argv[optind]==NULL && input==stdin && + isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { + interactive=TRUE; + } + setup_job_control(); + if (interactive==TRUE) { + //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]); + /* Looks like they want an interactive shell */ +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET + printf( "\n\n" BB_BANNER " Built-in shell (lash)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); +#endif + } else if (local_pending_command==NULL) { + //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]); + input = bb_xfopen(argv[optind], "r"); + mark_open(fileno(input)); /* be lazy, never mark this closed */ + } + + /* initialize the cwd -- this is never freed...*/ + cwd = xgetcwd(0); + if (!cwd) + cwd = bb_msg_unknown; + +#ifdef CONFIG_FEATURE_CLEAN_UP + atexit(free_memory); +#endif + +#ifdef CONFIG_FEATURE_COMMAND_EDITING + cmdedit_set_initial_prompt(); +#else + PS1 = NULL; +#endif + + return (busy_loop(input)); +} diff --git a/busybox/shell/msh.c b/busybox/shell/msh.c new file mode 100644 index 000000000..2fb0df739 --- /dev/null +++ b/busybox/shell/msh.c @@ -0,0 +1,5487 @@ +/* vi: set sw=4 ts=4: */ +/* + * Minix shell port for busybox + * + * This version of the Minix shell was adapted for use in busybox + * by Erik Andersen + * + * - backtick expansion did not work properly + * Jonas Holmberg + * Robert Schwebel + * 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" + + +/* Conditional use of "register" keyword */ +#define REGISTER register + + +/*#define MSHDEBUG 1*/ + +#ifdef MSHDEBUG +int mshdbg = 0; + +#define DBGPRINTF(x) if(mshdbg>0)printf x +#define DBGPRINTF0(x) if(mshdbg>0)printf x +#define DBGPRINTF1(x) if(mshdbg>1)printf x +#define DBGPRINTF2(x) if(mshdbg>2)printf x +#define DBGPRINTF3(x) if(mshdbg>3)printf x +#define DBGPRINTF4(x) if(mshdbg>4)printf x +#define DBGPRINTF5(x) if(mshdbg>5)printf x +#define DBGPRINTF6(x) if(mshdbg>6)printf x +#define DBGPRINTF7(x) if(mshdbg>7)printf x +#define DBGPRINTF8(x) if(mshdbg>8)printf x +#define DBGPRINTF9(x) if(mshdbg>9)printf x + +int mshdbg_rc = 0; + +#define RCPRINTF(x) if(mshdbg_rc)printf x + +#else + +#define DBGPRINTF(x) +#define DBGPRINTF0(x) +#define DBGPRINTF1(x) +#define DBGPRINTF2(x) +#define DBGPRINTF3(x) +#define DBGPRINTF4(x) +#define DBGPRINTF5(x) +#define DBGPRINTF6(x) +#define DBGPRINTF7(x) +#define DBGPRINTF8(x) +#define DBGPRINTF9(x) + +#define RCPRINTF(x) + +#endif /* MSHDEBUG */ + + +/* -------- sh.h -------- */ +/* + * shell + */ + +#define LINELIM 2100 +#define NPUSH 8 /* limit to input nesting */ + +#undef NOFILE +#define NOFILE 20 /* Number of open files */ +#define NUFILE 10 /* Number of user-accessible files */ +#define FDBASE 10 /* First file usable by Shell */ + +/* + * values returned by wait + */ +#define WAITSIG(s) ((s)&0177) +#define WAITVAL(s) (((s)>>8)&0377) +#define WAITCORE(s) (((s)&0200)!=0) + +/* + * library and system definitions + */ +typedef void xint; /* base type of jmp_buf, for not broken compilers */ + +/* + * shell components + */ + +#define QUOTE 0200 + +#define NOBLOCK ((struct op *)NULL) +#define NOWORD ((char *)NULL) +#define NOWORDS ((char **)NULL) +#define NOPIPE ((int *)NULL) + +/* + * Description of a command or an operation on commands. + * Might eventually use a union. + */ +struct op { + int type; /* operation type, see below */ + char **words; /* arguments to a command */ + struct ioword **ioact; /* IO actions (eg, < > >>) */ + struct op *left; + struct op *right; + char *str; /* identifier for case and for */ +}; + +#define TCOM 1 /* command */ +#define TPAREN 2 /* (c-list) */ +#define TPIPE 3 /* a | b */ +#define TLIST 4 /* a [&;] b */ +#define TOR 5 /* || */ +#define TAND 6 /* && */ +#define TFOR 7 +#define TDO 8 +#define TCASE 9 +#define TIF 10 +#define TWHILE 11 +#define TUNTIL 12 +#define TELIF 13 +#define TPAT 14 /* pattern in case */ +#define TBRACE 15 /* {c-list} */ +#define TASYNC 16 /* c & */ +/* Added to support "." file expansion */ +#define TDOT 17 + +/* Strings for names to make debug easier */ +char *T_CMD_NAMES[] = { + "PLACEHOLDER", + "TCOM", + "TPAREN", + "TPIPE", + "TLIST", + "TOR", + "TAND", + "TFOR", + "TDO", + "TCASE", + "TIF", + "TWHILE", + "TUNTIL", + "TELIF", + "TPAT", + "TBRACE", + "TASYNC", + "TDOT", +}; + + +/* + * actions determining the environment of a process + */ +#define BIT(i) (1<<(i)) +#define FEXEC BIT(0) /* execute without forking */ + +#if 0 /* Original value */ +#define AREASIZE (65000) +#else +#define AREASIZE (90000) +#endif + +/* + * flags to control evaluation of words + */ +#define DOSUB 1 /* interpret $, `, and quotes */ +#define DOBLANK 2 /* perform blank interpretation */ +#define DOGLOB 4 /* interpret [?* */ +#define DOKEY 8 /* move words with `=' to 2nd arg. list */ +#define DOTRIM 16 /* trim resulting string */ + +#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM) + + +/* PROTOTYPES */ +static int newfile(char *s); +static char *findeq(char *cp); +static char *cclass(char *p, int sub); +static void initarea(void); +extern int msh_main(int argc, char **argv); + + +struct brkcon { + jmp_buf brkpt; + struct brkcon *nextlev; +}; + + +/* + * redirection + */ +struct ioword { + short io_unit; /* unit affected */ + short io_flag; /* action (below) */ + char *io_name; /* file name */ +}; + +#define IOREAD 1 /* < */ +#define IOHERE 2 /* << (here file) */ +#define IOWRITE 4 /* > */ +#define IOCAT 8 /* >> */ +#define IOXHERE 16 /* ${}, ` in << */ +#define IODUP 32 /* >&digit */ +#define IOCLOSE 64 /* >&- */ + +#define IODEFAULT (-1) /* token for default IO unit */ + + + +/* + * parsing & execution environment + */ +static struct env { + char *linep; + struct io *iobase; + struct io *iop; + xint *errpt; /* void * */ + int iofd; + struct env *oenv; +} e; + +/* + * flags: + * -e: quit on error + * -k: look for name=value everywhere on command line + * -n: no execution + * -t: exit after reading and executing one command + * -v: echo as read + * -x: trace + * -u: unset variables net diagnostic + */ +static char *flag; + +static char *null; /* null value for variable */ +static int intr; /* interrupt pending */ + +static char *trap[_NSIG + 1]; +static char ourtrap[_NSIG + 1]; +static int trapset; /* trap pending */ + +static int heedint; /* heed interrupt signals */ + +static int yynerrs; /* yacc */ + +static char line[LINELIM]; +static char *elinep; + + +/* + * other functions + */ +static int (*inbuilt(char *s)) (struct op *); + +static char *rexecve(char *c, char **v, char **envp); +static char *space(int n); +static char *strsave(char *s, int a); +static char *evalstr(char *cp, int f); +static char *putn(int n); +static char *itoa(int n); +static char *unquote(char *as); +static struct var *lookup(char *n); +static int rlookup(char *n); +static struct wdblock *glob(char *cp, struct wdblock *wb); +static int my_getc(int ec); +static int subgetc(int ec, int quoted); +static char **makenv(int all, struct wdblock *wb); +static char **eval(char **ap, int f); +static int setstatus(int s); +static int waitfor(int lastpid, int canintr); + +static void onintr(int s); /* SIGINT handler */ + +static int newenv(int f); +static void quitenv(void); +static void err(char *s); +static int anys(char *s1, char *s2); +static int any(int c, char *s); +static void next(int f); +static void setdash(void); +static void onecommand(void); +static void runtrap(int i); +static int gmatch(char *s, char *p); + + +/* + * error handling + */ +static void leave(void); /* abort shell (or fail in subshell) */ +static void fail(void); /* fail but return to process next command */ +static void warn(char *s); +static void sig(int i); /* default signal handler */ + + + +/* -------- area stuff -------- */ + +#define REGSIZE sizeof(struct region) +#define GROWBY (256) +/* #define SHRINKBY (64) */ +#undef SHRINKBY +#define FREE (32767) +#define BUSY (0) +#define ALIGN (sizeof(int)-1) + + +struct region { + struct region *next; + int area; +}; + + + +/* -------- grammar stuff -------- */ +typedef union { + char *cp; + char **wp; + int i; + struct op *o; +} YYSTYPE; + +#define WORD 256 +#define LOGAND 257 +#define LOGOR 258 +#define BREAK 259 +#define IF 260 +#define THEN 261 +#define ELSE 262 +#define ELIF 263 +#define FI 264 +#define CASE 265 +#define ESAC 266 +#define FOR 267 +#define WHILE 268 +#define UNTIL 269 +#define DO 270 +#define DONE 271 +#define IN 272 +/* Added for "." file expansion */ +#define DOT 273 + +#define YYERRCODE 300 + +/* flags to yylex */ +#define CONTIN 01 /* skip new lines to complete command */ + +#define SYNTAXERR zzerr() + +static struct op *pipeline(int cf); +static struct op *andor(void); +static struct op *c_list(void); +static int synio(int cf); +static void musthave(int c, int cf); +static struct op *simple(void); +static struct op *nested(int type, int mark); +static struct op *command(int cf); +static struct op *dogroup(int onlydone); +static struct op *thenpart(void); +static struct op *elsepart(void); +static struct op *caselist(void); +static struct op *casepart(void); +static char **pattern(void); +static char **wordlist(void); +static struct op *list(struct op *t1, struct op *t2); +static struct op *block(int type, struct op *t1, struct op *t2, char **wp); +static struct op *newtp(void); +static struct op *namelist(struct op *t); +static char **copyw(void); +static void word(char *cp); +static struct ioword **copyio(void); +static struct ioword *io(int u, int f, char *cp); +static void zzerr(void); +static void yyerror(char *s); +static int yylex(int cf); +static int collect(int c, int c1); +static int dual(int c); +static void diag(int ec); +static char *tree(unsigned size); + +/* -------- var.h -------- */ + +struct var { + char *value; + char *name; + struct var *next; + char status; +}; + +#define COPYV 1 /* flag to setval, suggesting copy */ +#define RONLY 01 /* variable is read-only */ +#define EXPORT 02 /* variable is to be exported */ +#define GETCELL 04 /* name & value space was got with getcell */ + +static int yyparse(void); +static struct var *lookup(char *n); +static void setval(struct var *vp, char *val); +static void nameval(struct var *vp, char *val, char *name); +static void export(struct var *vp); +static void ronly(struct var *vp); +static int isassign(char *s); +static int checkname(char *cp); +static int assign(char *s, int cf); +static void putvlist(int f, int out); +static int eqname(char *n1, char *n2); + +static int execute(struct op *t, int *pin, int *pout, int act); + + +/* -------- io.h -------- */ +/* io buffer */ +struct iobuf { + unsigned id; /* buffer id */ + char buf[512]; /* buffer */ + char *bufp; /* pointer into buffer */ + char *ebufp; /* pointer to end of buffer */ +}; + +/* possible arguments to an IO function */ +struct ioarg { + char *aword; + char **awordlist; + int afile; /* file descriptor */ + unsigned afid; /* buffer id */ + long afpos; /* file position */ + struct iobuf *afbuf; /* buffer for this file */ +}; + +//static struct ioarg ioargstack[NPUSH]; +#define AFID_NOBUF (~0) +#define AFID_ID 0 + +/* an input generator's state */ +struct io { + int (*iofn) (struct ioarg *, struct io *); + struct ioarg *argp; + int peekc; + char prev; /* previous character read by readc() */ + char nlcount; /* for `'s */ + char xchar; /* for `'s */ + char task; /* reason for pushed IO */ +}; + +//static struct io iostack[NPUSH]; +#define XOTHER 0 /* none of the below */ +#define XDOLL 1 /* expanding ${} */ +#define XGRAVE 2 /* expanding `'s */ +#define XIO 3 /* file IO */ + +/* in substitution */ +#define INSUB() (e.iop->task == XGRAVE || e.iop->task == XDOLL) + + +/* + * input generators for IO structure + */ +static int nlchar(struct ioarg *ap); +static int strchar(struct ioarg *ap); +static int qstrchar(struct ioarg *ap); +static int filechar(struct ioarg *ap); +static int herechar(struct ioarg *ap); +static int linechar(struct ioarg *ap); +static int gravechar(struct ioarg *ap, struct io *iop); +static int qgravechar(struct ioarg *ap, struct io *iop); +static int dolchar(struct ioarg *ap); +static int wdchar(struct ioarg *ap); +static void scraphere(void); +static void freehere(int area); +static void gethere(void); +static void markhere(char *s, struct ioword *iop); +static int herein(char *hname, int xdoll); +static int run(struct ioarg *argp, int (*f) (struct ioarg *)); + + +/* + * IO functions + */ +static int eofc(void); +static int readc(void); +static void unget(int c); +static void ioecho(int c); +static void prs(char *s); +static void prn(unsigned u); +static void closef(int i); +static void closeall(void); + + +/* + * IO control + */ +static void pushio(struct ioarg *argp, int (*f) (struct ioarg *)); +static int remap(int fd); +static int openpipe(int *pv); +static void closepipe(int *pv); +static struct io *setbase(struct io *ip); + +#define PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(&temparg,(gen))) +#define RUN(what,arg,gen) ((temparg.what = (arg)), run(&temparg,(gen))) + +/* -------- word.h -------- */ + +#define NSTART 16 /* default number of words to allow for initially */ + +struct wdblock { + short w_bsize; + short w_nword; + /* bounds are arbitrary */ + char *w_words[1]; +}; + +static struct wdblock *addword(char *wd, struct wdblock *wb); +static struct wdblock *newword(int nw); +static char **getwords(struct wdblock *wb); + +/* -------- area.h -------- */ + +/* + * storage allocation + */ +static char *getcell(unsigned nbytes); +static void garbage(void); +static void setarea(char *cp, int a); +static int getarea(char *cp); +static void freearea(int a); +static void freecell(char *cp); +static int areanum; /* current allocation area */ + +#define NEW(type) (type *)getcell(sizeof(type)) +#define DELETE(obj) freecell((char *)obj) + + +/* -------- misc stuff -------- */ + +static int forkexec(struct op *t, int *pin, int *pout, int act, char **wp); +static int iosetup(struct ioword *iop, int pipein, int pipeout); +static void echo(char **wp); +static struct op **find1case(struct op *t, char *w); +static struct op *findcase(struct op *t, char *w); +static void brkset(struct brkcon *bc); +static int dolabel(struct op *t); +static int dohelp(struct op *t); +static int dochdir(struct op *t); +static int doshift(struct op *t); +static int dologin(struct op *t); +static int doumask(struct op *t); +static int doexec(struct op *t); +static int dodot(struct op *t); +static int dowait(struct op *t); +static int doread(struct op *t); +static int doeval(struct op *t); +static int dotrap(struct op *t); +static int getsig(char *s); +static void setsig(int n, sighandler_t f); +static int getn(char *as); +static int dobreak(struct op *t); +static int docontinue(struct op *t); +static int brkcontin(char *cp, int val); +static int doexit(struct op *t); +static int doexport(struct op *t); +static int doreadonly(struct op *t); +static void rdexp(char **wp, void (*f) (struct var *), int key); +static void badid(char *s); +static int doset(struct op *t); +static void varput(char *s, int out); +static int dotimes(struct op *t); +static int expand(char *cp, struct wdblock **wbp, int f); +static char *blank(int f); +static int dollar(int quoted); +static int grave(int quoted); +static void globname(char *we, char *pp); +static char *generate(char *start1, char *end1, char *middle, char *end); +static int anyspcl(struct wdblock *wb); +static int xstrcmp(char *p1, char *p2); +static void glob0(char *a0, unsigned int a1, int a2, + int (*a3) (char *, char *)); +static void glob1(char *base, char *lim); +static void glob2(char *i, char *j); +static void glob3(char *i, char *j, char *k); +static void readhere(char **name, char *s, int ec); +static void pushio(struct ioarg *argp, int (*f) (struct ioarg *)); +static int xxchar(struct ioarg *ap); + +struct here { + char *h_tag; + int h_dosub; + struct ioword *h_iop; + struct here *h_next; +}; + +static char *signame[] = { + "Signal 0", + "Hangup", + (char *) NULL, /* interrupt */ + "Quit", + "Illegal instruction", + "Trace/BPT trap", + "Abort", + "Bus error", + "Floating Point Exception", + "Killed", + "SIGUSR1", + "SIGSEGV", + "SIGUSR2", + (char *) NULL, /* broken pipe */ + "Alarm clock", + "Terminated", +}; + +#define NSIGNAL (sizeof(signame)/sizeof(signame[0])) + +struct res { + char *r_name; + int r_val; +}; +static struct res restab[] = { + {"for", FOR}, + {"case", CASE}, + {"esac", ESAC}, + {"while", WHILE}, + {"do", DO}, + {"done", DONE}, + {"if", IF}, + {"in", IN}, + {"then", THEN}, + {"else", ELSE}, + {"elif", ELIF}, + {"until", UNTIL}, + {"fi", FI}, + {";;", BREAK}, + {"||", LOGOR}, + {"&&", LOGAND}, + {"{", '{'}, + {"}", '}'}, + {".", DOT}, + {0, 0}, +}; + + +struct builtincmd { + const char *name; + int (*builtinfunc) (struct op * t); +}; +static const struct builtincmd builtincmds[] = { + {".", dodot}, + {":", dolabel}, + {"break", dobreak}, + {"cd", dochdir}, + {"continue", docontinue}, + {"eval", doeval}, + {"exec", doexec}, + {"exit", doexit}, + {"export", doexport}, + {"help", dohelp}, + {"login", dologin}, + {"newgrp", dologin}, + {"read", doread}, + {"readonly", doreadonly}, + {"set", doset}, + {"shift", doshift}, + {"times", dotimes}, + {"trap", dotrap}, + {"umask", doumask}, + {"wait", dowait}, + {0, 0} +}; + +struct op *scantree(struct op *); +static struct op *dowholefile(int, int); + +/* Globals */ +extern char **environ; /* environment pointer */ + +static char **dolv; +static int dolc; +static int exstat; +static char gflg; +static int interactive = 0; /* Is this an interactive shell */ +static int execflg; +static int multiline; /* \n changed to ; */ +static struct op *outtree; /* result from parser */ +static xint *failpt; +static xint *errpt; +static struct brkcon *brklist; +static int isbreak; +static struct wdblock *wdlist; +static struct wdblock *iolist; +static char *trap[_NSIG + 1]; +static char ourtrap[_NSIG + 1]; +static int trapset; /* trap pending */ +static int yynerrs; /* yacc */ +static char line[LINELIM]; + +#ifdef MSHDEBUG +static struct var *mshdbg_var; +#endif +static struct var *vlist; /* dictionary */ +static struct var *homedir; /* home directory */ +static struct var *prompt; /* main prompt */ +static struct var *cprompt; /* continuation prompt */ +static struct var *path; /* search path for commands */ +static struct var *shell; /* shell to interpret command files */ +static struct var *ifs; /* field separators */ + +static int areanum; /* current allocation area */ +static int intr; +static int inparse; +static char flags['z' - 'a' + 1]; +static char *flag = flags - 'a'; +static char *null = ""; +static int heedint = 1; +static void (*qflag) (int) = SIG_IGN; +static int startl; +static int peeksym; +static int nlseen; +static int iounit = IODEFAULT; +static YYSTYPE yylval; +static char *elinep = line + sizeof(line) - 5; + +static struct ioarg temparg = { 0, 0, 0, AFID_NOBUF, 0 }; /* temporary for PUSHIO */ +static struct ioarg ioargstack[NPUSH]; +static struct io iostack[NPUSH]; +static struct iobuf sharedbuf = { AFID_NOBUF }; +static struct iobuf mainbuf = { AFID_NOBUF }; +static unsigned bufid = AFID_ID; /* buffer id counter */ + +static struct here *inhere; /* list of hear docs while parsing */ +static struct here *acthere; /* list of active here documents */ +static struct region *areabot; /* bottom of area */ +static struct region *areatop; /* top of area */ +static struct region *areanxt; /* starting point of scan */ +static void *brktop; +static void *brkaddr; + +static struct env e = { + line, /* linep: char ptr */ + iostack, /* iobase: struct io ptr */ + iostack - 1, /* iop: struct io ptr */ + (xint *) NULL, /* errpt: void ptr for errors? */ + FDBASE, /* iofd: file desc */ + (struct env *) NULL /* oenv: struct env ptr */ +}; + +#ifdef MSHDEBUG +void print_t(struct op *t) +{ + DBGPRINTF(("T: t=0x%x, type %s, words=0x%x, IOword=0x%x\n", t, + T_CMD_NAMES[t->type], t->words, t->ioact)); + + if (t->words) { + DBGPRINTF(("T: W1: %s", t->words[0])); + } + + return; +} + +void print_tree(struct op *head) +{ + if (head == NULL) { + DBGPRINTF(("PRINT_TREE: no tree\n")); + return; + } + + DBGPRINTF(("NODE: 0x%x, left 0x%x, right 0x%x\n", head, head->left, + head->right)); + + if (head->left) + print_tree(head->left); + + if (head->right) + print_tree(head->right); + + return; +} +#endif /* MSHDEBUG */ + + +#ifdef CONFIG_FEATURE_COMMAND_EDITING +static char *current_prompt; +#endif + +/* -------- sh.c -------- */ +/* + * shell + */ + + +extern int msh_main(int argc, char **argv) +{ + REGISTER int f; + REGISTER char *s; + int cflag; + char *name, **ap; + int (*iof) (struct ioarg *); + + DBGPRINTF(("MSH_MAIN: argc %d, environ 0x%x\n", argc, environ)); + + initarea(); + if ((ap = environ) != NULL) { + while (*ap) + assign(*ap++, !COPYV); + for (ap = environ; *ap;) + export(lookup(*ap++)); + } + closeall(); + areanum = 1; + + shell = lookup("SHELL"); + if (shell->value == null) + setval(shell, (char *)DEFAULT_SHELL); + export(shell); + + homedir = lookup("HOME"); + if (homedir->value == null) + setval(homedir, "/"); + export(homedir); + + setval(lookup("$"), putn(getpid())); + + path = lookup("PATH"); + if (path->value == null) { + if (geteuid() == 0) + setval(path, "/sbin:/bin:/usr/sbin:/usr/bin"); + else + setval(path, "/bin:/usr/bin"); + } + export(path); + + ifs = lookup("IFS"); + if (ifs->value == null) + setval(ifs, " \t\n"); + +#ifdef MSHDEBUG + mshdbg_var = lookup("MSHDEBUG"); + if (mshdbg_var->value == null) + setval(mshdbg_var, "0"); +#endif + + + prompt = lookup("PS1"); +#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT + if (prompt->value == null) +#endif + setval(prompt, "$ "); + if (geteuid() == 0) { + setval(prompt, "# "); + prompt->status &= ~EXPORT; + } + cprompt = lookup("PS2"); +#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT + if (cprompt->value == null) +#endif + setval(cprompt, "> "); + + iof = filechar; + cflag = 0; + name = *argv++; + if (--argc >= 1) { + if (argv[0][0] == '-' && argv[0][1] != '\0') { + for (s = argv[0] + 1; *s; s++) + switch (*s) { + case 'c': + prompt->status &= ~EXPORT; + cprompt->status &= ~EXPORT; + setval(prompt, ""); + setval(cprompt, ""); + cflag = 1; + if (--argc > 0) + PUSHIO(aword, *++argv, iof = nlchar); + break; + + case 'q': + qflag = SIG_DFL; + break; + + case 's': + /* standard input */ + break; + + case 't': + prompt->status &= ~EXPORT; + setval(prompt, ""); + iof = linechar; + break; + + case 'i': + interactive++; + default: + if (*s >= 'a' && *s <= 'z') + flag[(int) *s]++; + } + } else { + argv--; + argc++; + } + + if (iof == filechar && --argc > 0) { + setval(prompt, ""); + setval(cprompt, ""); + prompt->status &= ~EXPORT; + cprompt->status &= ~EXPORT; + +/* Shell is non-interactive, activate printf-based debug */ +#ifdef MSHDEBUG + mshdbg = (int) (((char) (mshdbg_var->value[0])) - '0'); + if (mshdbg < 0) + mshdbg = 0; +#endif + DBGPRINTF(("MSH_MAIN: calling newfile()\n")); + + if (newfile(name = *++argv)) + exit(1); /* Exit on error */ + } + } + + setdash(); + + /* This won't be true if PUSHIO has been called, say from newfile() above */ + if (e.iop < iostack) { + PUSHIO(afile, 0, iof); + if (isatty(0) && isatty(1) && !cflag) { + interactive++; +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET +#ifdef MSHDEBUG + printf("\n\n" BB_BANNER " Built-in shell (msh with debug)\n"); +#else + printf("\n\n" BB_BANNER " Built-in shell (msh)\n"); +#endif + printf("Enter 'help' for a list of built-in commands.\n\n"); +#endif + } + } + + signal(SIGQUIT, qflag); + if (name && name[0] == '-') { + interactive++; + if ((f = open(".profile", 0)) >= 0) + next(remap(f)); + if ((f = open("/etc/profile", 0)) >= 0) + next(remap(f)); + } + if (interactive) + signal(SIGTERM, sig); + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); + dolv = argv; + dolc = argc; + dolv[0] = name; + if (dolc > 1) { + for (ap = ++argv; --argc > 0;) { + if (assign(*ap = *argv++, !COPYV)) { + dolc--; /* keyword */ + } else { + ap++; + } + } + } + setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc)); + + DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, e.iop 0x%x, iostack 0x%x\n", interactive, e.iop, iostack)); + + for (;;) { + if (interactive && e.iop <= iostack) { +#ifdef CONFIG_FEATURE_COMMAND_EDITING + current_prompt = prompt->value; +#else + prs(prompt->value); +#endif + } + onecommand(); + /* Ensure that getenv("PATH") stays current */ + setenv("PATH", path->value, 1); + } + + DBGPRINTF(("MSH_MAIN: returning.\n")); +} + +static void setdash() +{ + REGISTER char *cp; + REGISTER int c; + char m['z' - 'a' + 1]; + + cp = m; + for (c = 'a'; c <= 'z'; c++) + if (flag[(int) c]) + *cp++ = c; + *cp = 0; + setval(lookup("-"), m); +} + +static int newfile(s) +REGISTER char *s; +{ + REGISTER int f; + + DBGPRINTF7(("NEWFILE: opening %s\n", s)); + + if (strcmp(s, "-") != 0) { + DBGPRINTF(("NEWFILE: s is %s\n", s)); + f = open(s, 0); + if (f < 0) { + prs(s); + err(": cannot open"); + return (1); + } + } else + f = 0; + + next(remap(f)); + return (0); +} + + +struct op *scantree(head) +struct op *head; +{ + struct op *dotnode; + + if (head == NULL) + return (NULL); + + if (head->left != NULL) { + dotnode = scantree(head->left); + if (dotnode) + return (dotnode); + } + + if (head->right != NULL) { + dotnode = scantree(head->right); + if (dotnode) + return (dotnode); + } + + if (head->words == NULL) + return (NULL); + + DBGPRINTF5(("SCANTREE: checking node 0x%x\n", head)); + + if ((head->type != TDOT) && (strcmp(".", head->words[0]) == 0)) { + DBGPRINTF5(("SCANTREE: dot found in node 0x%x\n", head)); + return (head); + } + + return (NULL); +} + + +static void onecommand() +{ + REGISTER int i; + jmp_buf m1; + + DBGPRINTF(("ONECOMMAND: enter, outtree=0x%x\n", outtree)); + + while (e.oenv) + quitenv(); + + areanum = 1; + freehere(areanum); + freearea(areanum); + garbage(); + wdlist = 0; + iolist = 0; + e.errpt = 0; + e.linep = line; + yynerrs = 0; + multiline = 0; + inparse = 1; + intr = 0; + execflg = 0; + + setjmp(failpt = m1); /* Bruce Evans' fix */ + if (setjmp(failpt = m1) || yyparse() || intr) { + + DBGPRINTF(("ONECOMMAND: this is not good.\n")); + + while (e.oenv) + quitenv(); + scraphere(); + if (!interactive && intr) + leave(); + inparse = 0; + intr = 0; + return; + } + + inparse = 0; + brklist = 0; + intr = 0; + execflg = 0; + + if (!flag['n']) { + DBGPRINTF(("ONECOMMAND: calling execute, t=outtree=0x%x\n", + outtree)); + execute(outtree, NOPIPE, NOPIPE, 0); + } + + if (!interactive && intr) { + execflg = 0; + leave(); + } + + if ((i = trapset) != 0) { + trapset = 0; + runtrap(i); + } +} + +static void fail() +{ + longjmp(failpt, 1); + /* NOTREACHED */ +} + +static void leave() +{ + DBGPRINTF(("LEAVE: leave called!\n")); + + if (execflg) + fail(); + scraphere(); + freehere(1); + runtrap(0); + _exit(exstat); + /* NOTREACHED */ +} + +static void warn(s) +REGISTER char *s; +{ + if (*s) { + prs(s); + exstat = -1; + } + prs("\n"); + if (flag['e']) + leave(); +} + +static void err(s) +char *s; +{ + warn(s); + if (flag['n']) + return; + if (!interactive) + leave(); + if (e.errpt) + longjmp(e.errpt, 1); + closeall(); + e.iop = e.iobase = iostack; +} + +static int newenv(f) +int f; +{ + REGISTER struct env *ep; + + DBGPRINTF(("NEWENV: f=%d (indicates quitenv and return)\n", f)); + + if (f) { + quitenv(); + return (1); + } + + ep = (struct env *) space(sizeof(*ep)); + if (ep == NULL) { + while (e.oenv) + quitenv(); + fail(); + } + *ep = e; + e.oenv = ep; + e.errpt = errpt; + + return (0); +} + +static void quitenv() +{ + REGISTER struct env *ep; + REGISTER int fd; + + DBGPRINTF(("QUITENV: e.oenv=0x%x\n", e.oenv)); + + if ((ep = e.oenv) != NULL) { + fd = e.iofd; + e = *ep; + /* should close `'d files */ + DELETE(ep); + while (--fd >= e.iofd) + close(fd); + } +} + +/* + * Is any character from s1 in s2? + */ +static int anys(s1, s2) +REGISTER char *s1, *s2; +{ + while (*s1) + if (any(*s1++, s2)) + return (1); + return (0); +} + +/* + * Is character c in s? + */ +static int any(c, s) +REGISTER int c; +REGISTER char *s; +{ + while (*s) + if (*s++ == c) + return (1); + return (0); +} + +static char *putn(n) +REGISTER int n; +{ + return (itoa(n)); +} + +static char *itoa(n) +REGISTER int n; +{ + static char s[20]; + + snprintf(s, sizeof(s), "%u", n); + return (s); +} + + +static void next(int f) +{ + PUSHIO(afile, f, filechar); +} + +static void onintr(s) +int s; /* ANSI C requires a parameter */ +{ + signal(SIGINT, onintr); + intr = 1; + if (interactive) { + if (inparse) { + prs("\n"); + fail(); + } + } else if (heedint) { + execflg = 0; + leave(); + } +} + +static char *space(n) +int n; +{ + REGISTER char *cp; + + if ((cp = getcell(n)) == 0) + err("out of string space"); + return (cp); +} + +static char *strsave(s, a) +REGISTER char *s; +int a; +{ + REGISTER char *cp, *xp; + + if ((cp = space(strlen(s) + 1)) != NULL) { + setarea((char *) cp, a); + for (xp = cp; (*xp++ = *s++) != '\0';); + return (cp); + } + return (""); +} + +/* + * trap handling + */ +static void sig(i) +REGISTER int i; +{ + trapset = i; + signal(i, sig); +} + +static void runtrap(i) +int i; +{ + char *trapstr; + + if ((trapstr = trap[i]) == NULL) + return; + + if (i == 0) + trap[i] = 0; + + RUN(aword, trapstr, nlchar); +} + +/* -------- var.c -------- */ + +/* + * Find the given name in the dictionary + * and return its value. If the name was + * not previously there, enter it now and + * return a null value. + */ +static struct var *lookup(n) +REGISTER char *n; +{ + REGISTER struct var *vp; + REGISTER char *cp; + REGISTER int c; + static struct var dummy; + + if (isdigit(*n)) { + dummy.name = n; + for (c = 0; isdigit(*n) && c < 1000; n++) + c = c * 10 + *n - '0'; + dummy.status = RONLY; + dummy.value = c <= dolc ? dolv[c] : null; + return (&dummy); + } + for (vp = vlist; vp; vp = vp->next) + if (eqname(vp->name, n)) + return (vp); + cp = findeq(n); + vp = (struct var *) space(sizeof(*vp)); + if (vp == 0 || (vp->name = space((int) (cp - n) + 2)) == 0) { + dummy.name = dummy.value = ""; + return (&dummy); + } + for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++); + if (*cp == 0) + *cp = '='; + *++cp = 0; + setarea((char *) vp, 0); + setarea((char *) vp->name, 0); + vp->value = null; + vp->next = vlist; + vp->status = GETCELL; + vlist = vp; + return (vp); +} + +/* + * give variable at `vp' the value `val'. + */ +static void setval(vp, val) +struct var *vp; +char *val; +{ + nameval(vp, val, (char *) NULL); +} + +/* + * if name is not NULL, it must be + * a prefix of the space `val', + * and end with `='. + * this is all so that exporting + * values is reasonably painless. + */ +static void nameval(vp, val, name) +REGISTER struct var *vp; +char *val, *name; +{ + REGISTER char *cp, *xp; + char *nv; + int fl; + + if (vp->status & RONLY) { + for (xp = vp->name; *xp && *xp != '=';) + putc(*xp++, stderr); + err(" is read-only"); + return; + } + fl = 0; + if (name == NULL) { + xp = space(strlen(vp->name) + strlen(val) + 2); + if (xp == 0) + return; + /* make string: name=value */ + setarea((char *) xp, 0); + name = xp; + for (cp = vp->name; (*xp = *cp++) && *xp != '='; xp++); + if (*xp++ == 0) + xp[-1] = '='; + nv = xp; + for (cp = val; (*xp++ = *cp++) != '\0';); + val = nv; + fl = GETCELL; + } + if (vp->status & GETCELL) + freecell(vp->name); /* form new string `name=value' */ + vp->name = name; + vp->value = val; + vp->status |= fl; +} + +static void export(vp) +struct var *vp; +{ + vp->status |= EXPORT; +} + +static void ronly(vp) +struct var *vp; +{ + if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */ + vp->status |= RONLY; +} + +static int isassign(s) +REGISTER char *s; +{ + DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s)); + + if (!isalpha((int) *s) && *s != '_') + return (0); + for (; *s != '='; s++) + if (*s == 0 || (!isalnum(*s) && *s != '_')) + return (0); + + return (1); +} + +static int assign(s, cf) +REGISTER char *s; +int cf; +{ + REGISTER char *cp; + struct var *vp; + + DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf)); + + if (!isalpha(*s) && *s != '_') + return (0); + for (cp = s; *cp != '='; cp++) + if (*cp == 0 || (!isalnum(*cp) && *cp != '_')) + return (0); + vp = lookup(s); + nameval(vp, ++cp, cf == COPYV ? (char *) NULL : s); + if (cf != COPYV) + vp->status &= ~GETCELL; + return (1); +} + +static int checkname(cp) +REGISTER char *cp; +{ + DBGPRINTF7(("CHECKNAME: enter, cp=%s\n", cp)); + + if (!isalpha(*cp++) && *(cp - 1) != '_') + return (0); + while (*cp) + if (!isalnum(*cp++) && *(cp - 1) != '_') + return (0); + return (1); +} + +static void putvlist(f, out) +REGISTER int f, out; +{ + REGISTER struct var *vp; + + for (vp = vlist; vp; vp = vp->next) + if (vp->status & f && (isalpha(*vp->name) || *vp->name == '_')) { + if (vp->status & EXPORT) + write(out, "export ", 7); + if (vp->status & RONLY) + write(out, "readonly ", 9); + write(out, vp->name, (int) (findeq(vp->name) - vp->name)); + write(out, "\n", 1); + } +} + +static int eqname(n1, n2) +REGISTER char *n1, *n2; +{ + for (; *n1 != '=' && *n1 != 0; n1++) + if (*n2++ != *n1) + return (0); + return (*n2 == 0 || *n2 == '='); +} + +static char *findeq(cp) +REGISTER char *cp; +{ + while (*cp != '\0' && *cp != '=') + cp++; + return (cp); +} + +/* -------- gmatch.c -------- */ +/* + * int gmatch(string, pattern) + * char *string, *pattern; + * + * Match a pattern as in sh(1). + */ + +#define CMASK 0377 +#define QUOTE 0200 +#define QMASK (CMASK&~QUOTE) +#define NOT '!' /* might use ^ */ + +static int gmatch(s, p) +REGISTER char *s, *p; +{ + REGISTER int sc, pc; + + if (s == NULL || p == NULL) + return (0); + while ((pc = *p++ & CMASK) != '\0') { + sc = *s++ & QMASK; + switch (pc) { + case '[': + if ((p = cclass(p, sc)) == NULL) + return (0); + break; + + case '?': + if (sc == 0) + return (0); + break; + + case '*': + s--; + do { + if (*p == '\0' || gmatch(s, p)) + return (1); + } while (*s++ != '\0'); + return (0); + + default: + if (sc != (pc & ~QUOTE)) + return (0); + } + } + return (*s == 0); +} + +static char *cclass(p, sub) +REGISTER char *p; +REGISTER int sub; +{ + REGISTER int c, d, not, found; + + if ((not = *p == NOT) != 0) + p++; + found = not; + do { + if (*p == '\0') + return ((char *) NULL); + c = *p & CMASK; + if (p[1] == '-' && p[2] != ']') { + d = p[2] & CMASK; + p++; + } else + d = c; + if (c == sub || (c <= sub && sub <= d)) + found = !not; + } while (*++p != ']'); + return (found ? p + 1 : (char *) NULL); +} + + +/* -------- area.c -------- */ + +/* + * All memory between (char *)areabot and (char *)(areatop+1) is + * exclusively administered by the area management routines. + * It is assumed that sbrk() and brk() manipulate the high end. + */ + +#define sbrk(X) ({ void * __q = (void *)-1; if (brkaddr + (int)(X) < brktop) { __q = brkaddr; brkaddr+=(int)(X); } __q;}) + +static void initarea() +{ + brkaddr = malloc(AREASIZE); + brktop = brkaddr + AREASIZE; + + while ((int) sbrk(0) & ALIGN) + sbrk(1); + areabot = (struct region *) sbrk(REGSIZE); + + areabot->next = areabot; + areabot->area = BUSY; + areatop = areabot; + areanxt = areabot; +} + +char *getcell(nbytes) +unsigned nbytes; +{ + REGISTER int nregio; + REGISTER struct region *p, *q; + REGISTER int i; + + if (nbytes == 0) { + puts("getcell(0)"); + abort(); + } + /* silly and defeats the algorithm */ + /* + * round upwards and add administration area + */ + nregio = (nbytes + (REGSIZE - 1)) / REGSIZE + 1; + for (p = areanxt;;) { + if (p->area > areanum) { + /* + * merge free cells + */ + while ((q = p->next)->area > areanum && q != areanxt) + p->next = q->next; + /* + * exit loop if cell big enough + */ + if (q >= p + nregio) + goto found; + } + p = p->next; + if (p == areanxt) + break; + } + i = nregio >= GROWBY ? nregio : GROWBY; + p = (struct region *) sbrk(i * REGSIZE); + if (p == (struct region *) -1) + return ((char *) NULL); + p--; + if (p != areatop) { + puts("not contig"); + abort(); /* allocated areas are contiguous */ + } + q = p + i; + p->next = q; + p->area = FREE; + q->next = areabot; + q->area = BUSY; + areatop = q; + found: + /* + * we found a FREE area big enough, pointed to by 'p', and up to 'q' + */ + areanxt = p + nregio; + if (areanxt < q) { + /* + * split into requested area and rest + */ + if (areanxt + 1 > q) { + puts("OOM"); + abort(); /* insufficient space left for admin */ + } + areanxt->next = q; + areanxt->area = FREE; + p->next = areanxt; + } + p->area = areanum; + return ((char *) (p + 1)); +} + +static void freecell(cp) +char *cp; +{ + REGISTER struct region *p; + + if ((p = (struct region *) cp) != NULL) { + p--; + if (p < areanxt) + areanxt = p; + p->area = FREE; + } +} + +static void freearea(a) +REGISTER int a; +{ + REGISTER struct region *p, *top; + + top = areatop; + for (p = areabot; p != top; p = p->next) + if (p->area >= a) + p->area = FREE; +} + +static void setarea(cp, a) +char *cp; +int a; +{ + REGISTER struct region *p; + + if ((p = (struct region *) cp) != NULL) + (p - 1)->area = a; +} + +int getarea(cp) +char *cp; +{ + return ((struct region *) cp - 1)->area; +} + +static void garbage() +{ + REGISTER struct region *p, *q, *top; + + top = areatop; + for (p = areabot; p != top; p = p->next) { + if (p->area > areanum) { + while ((q = p->next)->area > areanum) + p->next = q->next; + areanxt = p; + } + } +#ifdef SHRINKBY + if (areatop >= q + SHRINKBY && q->area > areanum) { + brk((char *) (q + 1)); + q->next = areabot; + q->area = BUSY; + areatop = q; + } +#endif +} + +/* -------- csyn.c -------- */ +/* + * shell: syntax (C version) + */ + +int yyparse() +{ + DBGPRINTF7(("YYPARSE: enter...\n")); + + startl = 1; + peeksym = 0; + yynerrs = 0; + outtree = c_list(); + musthave('\n', 0); + return (yynerrs != 0); +} + +static struct op *pipeline(cf) +int cf; +{ + REGISTER struct op *t, *p; + REGISTER int c; + + DBGPRINTF7(("PIPELINE: enter, cf=%d\n", cf)); + + t = command(cf); + + DBGPRINTF9(("PIPELINE: t=0x%x\n", t)); + + if (t != NULL) { + while ((c = yylex(0)) == '|') { + if ((p = command(CONTIN)) == NULL) { + DBGPRINTF8(("PIPELINE: error!\n")); + SYNTAXERR; + } + + if (t->type != TPAREN && t->type != TCOM) { + /* shell statement */ + t = block(TPAREN, t, NOBLOCK, NOWORDS); + } + + t = block(TPIPE, t, p, NOWORDS); + } + peeksym = c; + } + + DBGPRINTF7(("PIPELINE: returning t=0x%x\n", t)); + return (t); +} + +static struct op *andor() +{ + REGISTER struct op *t, *p; + REGISTER int c; + + DBGPRINTF7(("ANDOR: enter...\n")); + + t = pipeline(0); + + DBGPRINTF9(("ANDOR: t=0x%x\n", t)); + + if (t != NULL) { + while ((c = yylex(0)) == LOGAND || c == LOGOR) { + if ((p = pipeline(CONTIN)) == NULL) { + DBGPRINTF8(("ANDOR: error!\n")); + SYNTAXERR; + } + + t = block(c == LOGAND ? TAND : TOR, t, p, NOWORDS); + } /* WHILE */ + + peeksym = c; + } + + DBGPRINTF7(("ANDOR: returning t=0x%x\n", t)); + return (t); +} + +static struct op *c_list() +{ + REGISTER struct op *t, *p; + REGISTER int c; + + DBGPRINTF7(("C_LIST: enter...\n")); + + t = andor(); + + if (t != NULL) { + if ((peeksym = yylex(0)) == '&') + t = block(TASYNC, t, NOBLOCK, NOWORDS); + + while ((c = yylex(0)) == ';' || c == '&' + || (multiline && c == '\n')) { + + if ((p = andor()) == NULL) + return (t); + + if ((peeksym = yylex(0)) == '&') + p = block(TASYNC, p, NOBLOCK, NOWORDS); + + t = list(t, p); + } /* WHILE */ + + peeksym = c; + } + /* IF */ + DBGPRINTF7(("C_LIST: returning t=0x%x\n", t)); + return (t); +} + +static int synio(cf) +int cf; +{ + REGISTER struct ioword *iop; + REGISTER int i; + REGISTER int c; + + DBGPRINTF7(("SYNIO: enter, cf=%d\n", cf)); + + if ((c = yylex(cf)) != '<' && c != '>') { + peeksym = c; + return (0); + } + + i = yylval.i; + musthave(WORD, 0); + iop = io(iounit, i, yylval.cp); + iounit = IODEFAULT; + + if (i & IOHERE) + markhere(yylval.cp, iop); + + DBGPRINTF7(("SYNIO: returning 1\n")); + return (1); +} + +static void musthave(c, cf) +int c, cf; +{ + if ((peeksym = yylex(cf)) != c) { + DBGPRINTF7(("MUSTHAVE: error!\n")); + SYNTAXERR; + } + + peeksym = 0; +} + +static struct op *simple() +{ + REGISTER struct op *t; + + t = NULL; + for (;;) { + switch (peeksym = yylex(0)) { + case '<': + case '>': + (void) synio(0); + break; + + case WORD: + if (t == NULL) { + t = newtp(); + t->type = TCOM; + } + peeksym = 0; + word(yylval.cp); + break; + + default: + return (t); + } + } +} + +static struct op *nested(type, mark) +int type, mark; +{ + REGISTER struct op *t; + + DBGPRINTF3(("NESTED: enter, type=%d, mark=%d\n", type, mark)); + + multiline++; + t = c_list(); + musthave(mark, 0); + multiline--; + return (block(type, t, NOBLOCK, NOWORDS)); +} + +static struct op *command(cf) +int cf; +{ + REGISTER struct op *t; + struct wdblock *iosave; + REGISTER int c; + + DBGPRINTF(("COMMAND: enter, cf=%d\n", cf)); + + iosave = iolist; + iolist = NULL; + + if (multiline) + cf |= CONTIN; + + while (synio(cf)) + cf = 0; + + c = yylex(cf); + + switch (c) { + default: + peeksym = c; + if ((t = simple()) == NULL) { + if (iolist == NULL) + return ((struct op *) NULL); + t = newtp(); + t->type = TCOM; + } + break; + + case '(': + t = nested(TPAREN, ')'); + break; + + case '{': + t = nested(TBRACE, '}'); + break; + + case FOR: + t = newtp(); + t->type = TFOR; + musthave(WORD, 0); + startl = 1; + t->str = yylval.cp; + multiline++; + t->words = wordlist(); + if ((c = yylex(0)) != '\n' && c != ';') + peeksym = c; + t->left = dogroup(0); + multiline--; + break; + + case WHILE: + case UNTIL: + multiline++; + t = newtp(); + t->type = c == WHILE ? TWHILE : TUNTIL; + t->left = c_list(); + t->right = dogroup(1); + t->words = NULL; + multiline--; + break; + + case CASE: + t = newtp(); + t->type = TCASE; + musthave(WORD, 0); + t->str = yylval.cp; + startl++; + multiline++; + musthave(IN, CONTIN); + startl++; + + t->left = caselist(); + + musthave(ESAC, 0); + multiline--; + break; + + case IF: + multiline++; + t = newtp(); + t->type = TIF; + t->left = c_list(); + t->right = thenpart(); + musthave(FI, 0); + multiline--; + break; + + case DOT: + t = newtp(); + t->type = TDOT; + + musthave(WORD, 0); /* gets name of file */ + DBGPRINTF7(("COMMAND: DOT clause, yylval.cp is %s\n", yylval.cp)); + + word(yylval.cp); /* add word to wdlist */ + word(NOWORD); /* terminate wdlist */ + t->words = copyw(); /* dup wdlist */ + break; + + } + + while (synio(0)); + + t = namelist(t); + iolist = iosave; + + DBGPRINTF(("COMMAND: returning 0x%x\n", t)); + + return (t); +} + +static struct op *dowholefile(type, mark) +int type; +int mark; +{ + REGISTER struct op *t; + + DBGPRINTF(("DOWHOLEFILE: enter, type=%d, mark=%d\n", type, mark)); + + multiline++; + t = c_list(); + multiline--; + t = block(type, t, NOBLOCK, NOWORDS); + DBGPRINTF(("DOWHOLEFILE: return t=0x%x\n", t)); + return (t); +} + +static struct op *dogroup(onlydone) +int onlydone; +{ + REGISTER int c; + REGISTER struct op *mylist; + + c = yylex(CONTIN); + if (c == DONE && onlydone) + return ((struct op *) NULL); + if (c != DO) + SYNTAXERR; + mylist = c_list(); + musthave(DONE, 0); + return (mylist); +} + +static struct op *thenpart() +{ + REGISTER int c; + REGISTER struct op *t; + + if ((c = yylex(0)) != THEN) { + peeksym = c; + return ((struct op *) NULL); + } + t = newtp(); + t->type = 0; + t->left = c_list(); + if (t->left == NULL) + SYNTAXERR; + t->right = elsepart(); + return (t); +} + +static struct op *elsepart() +{ + REGISTER int c; + REGISTER struct op *t; + + switch (c = yylex(0)) { + case ELSE: + if ((t = c_list()) == NULL) + SYNTAXERR; + return (t); + + case ELIF: + t = newtp(); + t->type = TELIF; + t->left = c_list(); + t->right = thenpart(); + return (t); + + default: + peeksym = c; + return ((struct op *) NULL); + } +} + +static struct op *caselist() +{ + REGISTER struct op *t; + + t = NULL; + while ((peeksym = yylex(CONTIN)) != ESAC) { + DBGPRINTF(("CASELIST, doing yylex, peeksym=%d\n", peeksym)); + t = list(t, casepart()); + } + + DBGPRINTF(("CASELIST, returning t=0x%x\n", t)); + return (t); +} + +static struct op *casepart() +{ + REGISTER struct op *t; + + DBGPRINTF7(("CASEPART: enter...\n")); + + t = newtp(); + t->type = TPAT; + t->words = pattern(); + musthave(')', 0); + t->left = c_list(); + if ((peeksym = yylex(CONTIN)) != ESAC) + musthave(BREAK, CONTIN); + + DBGPRINTF7(("CASEPART: made newtp(TPAT, t=0x%x)\n", t)); + + return (t); +} + +static char **pattern() +{ + REGISTER int c, cf; + + cf = CONTIN; + do { + musthave(WORD, cf); + word(yylval.cp); + cf = 0; + } while ((c = yylex(0)) == '|'); + peeksym = c; + word(NOWORD); + + return (copyw()); +} + +static char **wordlist() +{ + REGISTER int c; + + if ((c = yylex(0)) != IN) { + peeksym = c; + return ((char **) NULL); + } + startl = 0; + while ((c = yylex(0)) == WORD) + word(yylval.cp); + word(NOWORD); + peeksym = c; + return (copyw()); +} + +/* + * supporting functions + */ +static struct op *list(t1, t2) +REGISTER struct op *t1, *t2; +{ + DBGPRINTF7(("LIST: enter, t1=0x%x, t2=0x%x\n", t1, t2)); + + if (t1 == NULL) + return (t2); + if (t2 == NULL) + return (t1); + + return (block(TLIST, t1, t2, NOWORDS)); +} + +static struct op *block(type, t1, t2, wp) +int type; +struct op *t1, *t2; +char **wp; +{ + REGISTER struct op *t; + + DBGPRINTF7(("BLOCK: enter, type=%d (%s)\n", type, T_CMD_NAMES[type])); + + t = newtp(); + t->type = type; + t->left = t1; + t->right = t2; + t->words = wp; + + DBGPRINTF7(("BLOCK: inserted 0x%x between 0x%x and 0x%x\n", t, t1, + t2)); + + return (t); +} + +/* See if given string is a shell multiline (FOR, IF, etc) */ +static int rlookup(n) +REGISTER char *n; +{ + REGISTER struct res *rp; + + DBGPRINTF7(("RLOOKUP: enter, n is %s\n", n)); + + for (rp = restab; rp->r_name; rp++) + if (strcmp(rp->r_name, n) == 0) { + DBGPRINTF7(("RLOOKUP: match, returning %d\n", rp->r_val)); + return (rp->r_val); /* Return numeric code for shell multiline */ + } + + DBGPRINTF7(("RLOOKUP: NO match, returning 0\n")); + return (0); /* Not a shell multiline */ +} + +static struct op *newtp() +{ + REGISTER struct op *t; + + t = (struct op *) tree(sizeof(*t)); + t->type = 0; + t->words = NULL; + t->ioact = NULL; + t->left = NULL; + t->right = NULL; + t->str = NULL; + + DBGPRINTF3(("NEWTP: allocated 0x%x\n", t)); + + return (t); +} + +static struct op *namelist(t) +REGISTER struct op *t; +{ + + DBGPRINTF7(("NAMELIST: enter, t=0x%x, type %s, iolist=0x%x\n", t, + T_CMD_NAMES[t->type], iolist)); + + if (iolist) { + iolist = addword((char *) NULL, iolist); + t->ioact = copyio(); + } else + t->ioact = NULL; + + if (t->type != TCOM) { + if (t->type != TPAREN && t->ioact != NULL) { + t = block(TPAREN, t, NOBLOCK, NOWORDS); + t->ioact = t->left->ioact; + t->left->ioact = NULL; + } + return (t); + } + + word(NOWORD); + t->words = copyw(); + + + return (t); +} + +static char **copyw() +{ + REGISTER char **wd; + + wd = getwords(wdlist); + wdlist = 0; + return (wd); +} + +static void word(cp) +char *cp; +{ + wdlist = addword(cp, wdlist); +} + +static struct ioword **copyio() +{ + REGISTER struct ioword **iop; + + iop = (struct ioword **) getwords(iolist); + iolist = 0; + return (iop); +} + +static struct ioword *io(u, f, cp) +int u; +int f; +char *cp; +{ + REGISTER struct ioword *iop; + + iop = (struct ioword *) tree(sizeof(*iop)); + iop->io_unit = u; + iop->io_flag = f; + iop->io_name = cp; + iolist = addword((char *) iop, iolist); + return (iop); +} + +static void zzerr() +{ + yyerror("syntax error"); +} + +static void yyerror(s) +char *s; +{ + yynerrs++; + if (interactive && e.iop <= iostack) { + multiline = 0; + while (eofc() == 0 && yylex(0) != '\n'); + } + err(s); + fail(); +} + +static int yylex(cf) +int cf; +{ + REGISTER int c, c1; + int atstart; + + if ((c = peeksym) > 0) { + peeksym = 0; + if (c == '\n') + startl = 1; + return (c); + } + + + nlseen = 0; + atstart = startl; + startl = 0; + yylval.i = 0; + e.linep = line; + +/* MALAMO */ + line[LINELIM - 1] = '\0'; + + loop: + while ((c = my_getc(0)) == ' ' || c == '\t') /* Skip whitespace */ + ; + + switch (c) { + default: + if (any(c, "0123456789")) { + unget(c1 = my_getc(0)); + if (c1 == '<' || c1 == '>') { + iounit = c - '0'; + goto loop; + } + *e.linep++ = c; + c = c1; + } + break; + + case '#': /* Comment, skip to next newline or End-of-string */ + while ((c = my_getc(0)) != 0 && c != '\n'); + unget(c); + goto loop; + + case 0: + DBGPRINTF5(("YYLEX: return 0, c=%d\n", c)); + return (c); + + case '$': + DBGPRINTF9(("YYLEX: found $\n")); + *e.linep++ = c; + if ((c = my_getc(0)) == '{') { + if ((c = collect(c, '}')) != '\0') + return (c); + goto pack; + } + break; + + case '`': + case '\'': + case '"': + if ((c = collect(c, c)) != '\0') + return (c); + goto pack; + + case '|': + case '&': + case ';': + startl = 1; + /* If more chars process them, else return NULL char */ + if ((c1 = dual(c)) != '\0') + return (c1); + else + return (c); + + case '^': + startl = 1; + return ('|'); + case '>': + case '<': + diag(c); + return (c); + + case '\n': + nlseen++; + gethere(); + startl = 1; + if (multiline || cf & CONTIN) { + if (interactive && e.iop <= iostack) { +#ifdef CONFIG_FEATURE_COMMAND_EDITING + current_prompt = cprompt->value; +#else + prs(cprompt->value); +#endif + } + if (cf & CONTIN) + goto loop; + } + return (c); + + case '(': + case ')': + startl = 1; + return (c); + } + + unget(c); + + pack: + while ((c = my_getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n")) { + if (e.linep >= elinep) + err("word too long"); + else + *e.linep++ = c; + }; + + unget(c); + + if (any(c, "\"'`$")) + goto loop; + + *e.linep++ = '\0'; + + if (atstart && (c = rlookup(line)) != 0) { + startl = 1; + return (c); + } + + yylval.cp = strsave(line, areanum); + return (WORD); +} + + +static int collect(c, c1) +REGISTER int c, c1; +{ + char s[2]; + + DBGPRINTF8(("COLLECT: enter, c=%d, c1=%d\n", c, c1)); + + *e.linep++ = c; + while ((c = my_getc(c1)) != c1) { + if (c == 0) { + unget(c); + s[0] = c1; + s[1] = 0; + prs("no closing "); + yyerror(s); + return (YYERRCODE); + } + if (interactive && c == '\n' && e.iop <= iostack) { +#ifdef CONFIG_FEATURE_COMMAND_EDITING + current_prompt = cprompt->value; +#else + prs(cprompt->value); +#endif + } + *e.linep++ = c; + } + + *e.linep++ = c; + + DBGPRINTF8(("COLLECT: return 0, line is %s\n", line)); + + return (0); +} + +/* "multiline commands" helper func */ +/* see if next 2 chars form a shell multiline */ +static int dual(c) +REGISTER int c; +{ + char s[3]; + REGISTER char *cp = s; + + DBGPRINTF8(("DUAL: enter, c=%d\n", c)); + + *cp++ = c; /* c is the given "peek" char */ + *cp++ = my_getc(0); /* get next char of input */ + *cp = 0; /* add EOS marker */ + + c = rlookup(s); /* see if 2 chars form a shell multiline */ + if (c == 0) + unget(*--cp); /* String is not a shell multiline, put peek char back */ + + return (c); /* String is multiline, return numeric multiline (restab) code */ +} + +static void diag(ec) +REGISTER int ec; +{ + REGISTER int c; + + DBGPRINTF8(("DIAG: enter, ec=%d\n", ec)); + + c = my_getc(0); + if (c == '>' || c == '<') { + if (c != ec) + zzerr(); + yylval.i = ec == '>' ? IOWRITE | IOCAT : IOHERE; + c = my_getc(0); + } else + yylval.i = ec == '>' ? IOWRITE : IOREAD; + if (c != '&' || yylval.i == IOHERE) + unget(c); + else + yylval.i |= IODUP; +} + +static char *tree(size) +unsigned size; +{ + REGISTER char *t; + + if ((t = getcell(size)) == NULL) { + DBGPRINTF2(("TREE: getcell(%d) failed!\n", size)); + prs("command line too complicated\n"); + fail(); + /* NOTREACHED */ + } + return (t); +} + +/* VARARGS1 */ +/* ARGSUSED */ + +/* -------- exec.c -------- */ + +/* + * execute tree + */ + + +static int execute(t, pin, pout, act) +REGISTER struct op *t; +int *pin, *pout; +int act; +{ + REGISTER struct op *t1; + volatile int i, rv, a; + char *cp, **wp, **wp2; + struct var *vp; + struct op *outtree_save; + struct brkcon bc; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) ℘ +#endif + + if (t == NULL) { + DBGPRINTF4(("EXECUTE: enter, t==null, returning.\n")); + return (0); + } + + DBGPRINTF(("EXECUTE: t=0x%x, t->type=%d (%s), t->words is %s\n", t, + t->type, T_CMD_NAMES[t->type], + ((t->words == NULL) ? "NULL" : t->words[0]))); + + rv = 0; + a = areanum++; + wp = (wp2 = t->words) != NULL + ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY) + : NULL; + +/* Hard to know how many words there are, be careful of garbage pointer values */ +/* They are likely to cause "PCI bus fault" errors */ +#if 0 + DBGPRINTF(("EXECUTE: t->left=0x%x, t->right=0x%x, t->words[1] is %s\n", + t->left, t->right, + ((t->words[1] == NULL) ? "NULL" : t->words[1]))); + DBGPRINTF7(("EXECUTE: t->words[2] is %s, t->words[3] is %s\n", + ((t->words[2] == NULL) ? "NULL" : t->words[2]), + ((t->words[3] == NULL) ? "NULL" : t->words[3]))); +#endif + + + switch (t->type) { + case TDOT: + DBGPRINTF3(("EXECUTE: TDOT\n")); + + outtree_save = outtree; + + newfile(evalstr(t->words[0], DOALL)); + + t->left = dowholefile(TLIST, 0); + t->right = NULL; + + outtree = outtree_save; + + if (t->left) + rv = execute(t->left, pin, pout, 0); + if (t->right) + rv = execute(t->right, pin, pout, 0); + break; + + case TPAREN: + rv = execute(t->left, pin, pout, 0); + break; + + case TCOM: + { + rv = forkexec(t, pin, pout, act, wp); + } + break; + + case TPIPE: + { + int pv[2]; + + if ((rv = openpipe(pv)) < 0) + break; + pv[0] = remap(pv[0]); + pv[1] = remap(pv[1]); + (void) execute(t->left, pin, pv, 0); + rv = execute(t->right, pv, pout, 0); + } + break; + + case TLIST: + (void) execute(t->left, pin, pout, 0); + rv = execute(t->right, pin, pout, 0); + break; + + case TASYNC: + { + int hinteractive = interactive; + + DBGPRINTF7(("EXECUTE: TASYNC clause, calling vfork()...\n")); + + i = vfork(); + if (i != 0) { + interactive = hinteractive; + if (i != -1) { + setval(lookup("!"), putn(i)); + if (pin != NULL) + closepipe(pin); + if (interactive) { + prs(putn(i)); + prs("\n"); + } + } else + rv = -1; + setstatus(rv); + } else { + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + if (interactive) + signal(SIGTERM, SIG_DFL); + interactive = 0; + if (pin == NULL) { + close(0); + open("/dev/null", 0); + } + _exit(execute(t->left, pin, pout, FEXEC)); + } + } + break; + + case TOR: + case TAND: + rv = execute(t->left, pin, pout, 0); + if ((t1 = t->right) != NULL && (rv == 0) == (t->type == TAND)) + rv = execute(t1, pin, pout, 0); + break; + + case TFOR: + if (wp == NULL) { + wp = dolv + 1; + if ((i = dolc) < 0) + i = 0; + } else { + i = -1; + while (*wp++ != NULL); + } + vp = lookup(t->str); + while (setjmp(bc.brkpt)) + if (isbreak) + goto broken; + brkset(&bc); + for (t1 = t->left; i-- && *wp != NULL;) { + setval(vp, *wp++); + rv = execute(t1, pin, pout, 0); + } + brklist = brklist->nextlev; + break; + + case TWHILE: + case TUNTIL: + while (setjmp(bc.brkpt)) + if (isbreak) + goto broken; + brkset(&bc); + t1 = t->left; + while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE)) + rv = execute(t->right, pin, pout, 0); + brklist = brklist->nextlev; + break; + + case TIF: + case TELIF: + if (t->right != NULL) { + rv = !execute(t->left, pin, pout, 0) ? + execute(t->right->left, pin, pout, 0) : + execute(t->right->right, pin, pout, 0); + } + break; + + case TCASE: + if ((cp = evalstr(t->str, DOSUB | DOTRIM)) == 0) + cp = ""; + + DBGPRINTF7(("EXECUTE: TCASE, t->str is %s, cp is %s\n", + ((t->str == NULL) ? "NULL" : t->str), + ((cp == NULL) ? "NULL" : cp))); + + if ((t1 = findcase(t->left, cp)) != NULL) { + DBGPRINTF7(("EXECUTE: TCASE, calling execute(t=0x%x, t1=0x%x)...\n", t, t1)); + rv = execute(t1, pin, pout, 0); + DBGPRINTF7(("EXECUTE: TCASE, back from execute(t=0x%x, t1=0x%x)...\n", t, t1)); + } + break; + + case TBRACE: +/* + if (iopp = t->ioact) + while (*iopp) + if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) { + rv = -1; + break; + } +*/ + if (rv >= 0 && (t1 = t->left)) + rv = execute(t1, pin, pout, 0); + break; + + }; + + broken: + t->words = wp2; + isbreak = 0; + freehere(areanum); + freearea(areanum); + areanum = a; + if (interactive && intr) { + closeall(); + fail(); + } + + if ((i = trapset) != 0) { + trapset = 0; + runtrap(i); + } + + DBGPRINTF(("EXECUTE: returning from t=0x%x, rv=%d\n", t, rv)); + return (rv); +} + +static int +forkexec(REGISTER struct op *t, int *pin, int *pout, int act, char **wp) +{ + pid_t newpid; + int i, rv; + int (*shcom) (struct op *) = NULL; + REGISTER int f; + char *cp = NULL; + struct ioword **iopp; + int resetsig; + char **owp; + int forked = 0; + + int *hpin = pin; + int *hpout = pout; + char *hwp; + int hinteractive; + int hintr; + struct brkcon *hbrklist; + int hexecflg; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &pin; + (void) &pout; + (void) ℘ + (void) &shcom; + (void) &cp; + (void) &resetsig; + (void) &owp; +#endif + + DBGPRINTF(("FORKEXEC: t=0x%x, pin 0x%x, pout 0x%x, act %d\n", t, pin, + pout, act)); + DBGPRINTF7(("FORKEXEC: t->words is %s\n", + ((t->words == NULL) ? "NULL" : t->words[0]))); + +/* Hard to know how many words there are, be careful of garbage pointer values */ +/* They are likely to cause "PCI bus fault" errors */ +#if 0 + DBGPRINTF7(("FORKEXEC: t->words is %s, t->words[1] is %s\n", + ((t->words == NULL) ? "NULL" : t->words[0]), + ((t->words == NULL) ? "NULL" : t->words[1]))); + DBGPRINTF7(("FORKEXEC: wp is %s, wp[1] is %s\n", + ((wp == NULL) ? "NULL" : wp[0]), + ((wp[1] == NULL) ? "NULL" : wp[1]))); + DBGPRINTF7(("FORKEXEC: wp2 is %s, wp[3] is %s\n", + ((wp[2] == NULL) ? "NULL" : wp[2]), + ((wp[3] == NULL) ? "NULL" : wp[3]))); +#endif + + + owp = wp; + resetsig = 0; + rv = -1; /* system-detected error */ + if (t->type == TCOM) { + while ((cp = *wp++) != NULL); + cp = *wp; + + /* strip all initial assignments */ + /* not correct wrt PATH=yyy command etc */ + if (flag['x']) { + DBGPRINTF9(("FORKEXEC: echo'ing, cp=0x%x, wp=0x%x, owp=0x%x\n", + cp, wp, owp)); + echo(cp ? wp : owp); + } +#if 0 + DBGPRINTF9(("FORKEXEC: t->words is %s, t->words[1] is %s\n", + ((t->words == NULL) ? "NULL" : t->words[0]), + ((t->words == NULL) ? "NULL" : t->words[1]))); + DBGPRINTF9(("FORKEXEC: wp is %s, wp[1] is %s\n", + ((wp == NULL) ? "NULL" : wp[0]), + ((wp == NULL) ? "NULL" : wp[1]))); +#endif + + if (cp == NULL && t->ioact == NULL) { + while ((cp = *owp++) != NULL && assign(cp, COPYV)); + DBGPRINTF(("FORKEXEC: returning setstatus()\n")); + return (setstatus(0)); + } else if (cp != NULL) { + shcom = inbuilt(cp); + } + } + + t->words = wp; + f = act; + +#if 0 + DBGPRINTF3(("FORKEXEC: t->words is %s, t->words[1] is %s\n", + ((t->words == NULL) ? "NULL" : t->words[0]), + ((t->words == NULL) ? "NULL" : t->words[1]))); +#endif + DBGPRINTF(("FORKEXEC: shcom 0x%x, f&FEXEC 0x%x, owp 0x%x\n", shcom, + f & FEXEC, owp)); + + if (shcom == NULL && (f & FEXEC) == 0) { + /* Save values in case the child process alters them */ + hpin = pin; + hpout = pout; + hwp = *wp; + hinteractive = interactive; + hintr = intr; + hbrklist = brklist; + hexecflg = execflg; + + DBGPRINTF3(("FORKEXEC: calling vfork()...\n")); + + newpid = vfork(); + + if (newpid == -1) { + DBGPRINTF(("FORKEXEC: ERROR, unable to vfork()!\n")); + return (-1); + } + + + if (newpid > 0) { /* Parent */ + + /* Restore values */ + pin = hpin; + pout = hpout; + *wp = hwp; + interactive = hinteractive; + intr = hintr; + brklist = hbrklist; + execflg = hexecflg; + +/* moved up + if (i == -1) + return(rv); +*/ + + if (pin != NULL) + closepipe(pin); + + return (pout == NULL ? setstatus(waitfor(newpid, 0)) : 0); + } + + /* Must be the child process, pid should be 0 */ + DBGPRINTF(("FORKEXEC: child process, shcom=0x%x\n", shcom)); + + if (interactive) { + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + resetsig = 1; + } + interactive = 0; + intr = 0; + forked = 1; + brklist = 0; + execflg = 0; + } + + + if (owp != NULL) + while ((cp = *owp++) != NULL && assign(cp, COPYV)) + if (shcom == NULL) + export(lookup(cp)); + +#ifdef COMPIPE + if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) { + err("piping to/from shell builtins not yet done"); + if (forked) + _exit(-1); + return (-1); + } +#endif + + if (pin != NULL) { + dup2(pin[0], 0); + closepipe(pin); + } + if (pout != NULL) { + dup2(pout[1], 1); + closepipe(pout); + } + + if ((iopp = t->ioact) != NULL) { + if (shcom != NULL && shcom != doexec) { + prs(cp); + err(": cannot redirect shell command"); + if (forked) + _exit(-1); + return (-1); + } + while (*iopp) + if (iosetup(*iopp++, pin != NULL, pout != NULL)) { + if (forked) + _exit(rv); + return (rv); + } + } + + if (shcom) { + i = setstatus((*shcom) (t)); + if (forked) + _exit(i); + DBGPRINTF(("FORKEXEC: returning i=%d\n", i)); + return (i); + } + + /* should use FIOCEXCL */ + for (i = FDBASE; i < NOFILE; i++) + close(i); + if (resetsig) { + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + } + + if (t->type == TPAREN) + _exit(execute(t->left, NOPIPE, NOPIPE, FEXEC)); + if (wp[0] == NULL) + _exit(0); + + cp = rexecve(wp[0], wp, makenv(0, NULL)); + prs(wp[0]); + prs(": "); + err(cp); + if (!execflg) + trap[0] = NULL; + + DBGPRINTF(("FORKEXEC: calling leave(), pid=%d\n", newpid)); + + leave(); + /* NOTREACHED */ + _exit(1); +} + +/* + * 0< 1> are ignored as required + * within pipelines. + */ +static int iosetup(iop, pipein, pipeout) +REGISTER struct ioword *iop; +int pipein, pipeout; +{ + REGISTER int u = -1; + char *cp = NULL, *msg; + + DBGPRINTF(("IOSETUP: iop 0x%x, pipein 0x%x, pipeout 0x%x\n", iop, + pipein, pipeout)); + + if (iop->io_unit == IODEFAULT) /* take default */ + iop->io_unit = iop->io_flag & (IOREAD | IOHERE) ? 0 : 1; + + if (pipein && iop->io_unit == 0) + return (0); + + if (pipeout && iop->io_unit == 1) + return (0); + + msg = iop->io_flag & (IOREAD | IOHERE) ? "open" : "create"; + if ((iop->io_flag & IOHERE) == 0) { + cp = iop->io_name; + if ((cp = evalstr(cp, DOSUB | DOTRIM)) == NULL) + return (1); + } + + if (iop->io_flag & IODUP) { + if (cp[1] || (!isdigit(*cp) && *cp != '-')) { + prs(cp); + err(": illegal >& argument"); + return (1); + } + if (*cp == '-') + iop->io_flag = IOCLOSE; + iop->io_flag &= ~(IOREAD | IOWRITE); + } + switch (iop->io_flag) { + case IOREAD: + u = open(cp, 0); + break; + + case IOHERE: + case IOHERE | IOXHERE: + u = herein(iop->io_name, iop->io_flag & IOXHERE); + cp = "here file"; + break; + + case IOWRITE | IOCAT: + if ((u = open(cp, 1)) >= 0) { + lseek(u, (long) 0, 2); + break; + } + case IOWRITE: + u = creat(cp, 0666); + break; + + case IODUP: + u = dup2(*cp - '0', iop->io_unit); + break; + + case IOCLOSE: + close(iop->io_unit); + return (0); + } + if (u < 0) { + prs(cp); + prs(": cannot "); + warn(msg); + return (1); + } else { + if (u != iop->io_unit) { + dup2(u, iop->io_unit); + close(u); + } + } + return (0); +} + +static void echo(wp) +REGISTER char **wp; +{ + REGISTER int i; + + prs("+"); + for (i = 0; wp[i]; i++) { + if (i) + prs(" "); + prs(wp[i]); + } + prs("\n"); +} + +static struct op **find1case(t, w) +struct op *t; +char *w; +{ + REGISTER struct op *t1; + struct op **tp; + REGISTER char **wp, *cp; + + + if (t == NULL) { + DBGPRINTF3(("FIND1CASE: enter, t==NULL, returning.\n")); + return ((struct op **) NULL); + } + + DBGPRINTF3(("FIND1CASE: enter, t->type=%d (%s)\n", t->type, + T_CMD_NAMES[t->type])); + + if (t->type == TLIST) { + if ((tp = find1case(t->left, w)) != NULL) { + DBGPRINTF3(("FIND1CASE: found one to the left, returning tp=0x%x\n", tp)); + return (tp); + } + t1 = t->right; /* TPAT */ + } else + t1 = t; + + for (wp = t1->words; *wp;) + if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) { + DBGPRINTF3(("FIND1CASE: returning &t1->left= 0x%x.\n", + &t1->left)); + return (&t1->left); + } + + DBGPRINTF(("FIND1CASE: returning NULL\n")); + return ((struct op **) NULL); +} + +static struct op *findcase(t, w) +struct op *t; +char *w; +{ + REGISTER struct op **tp; + + return ((tp = find1case(t, w)) != NULL ? *tp : (struct op *) NULL); +} + +/* + * Enter a new loop level (marked for break/continue). + */ +static void brkset(bc) +struct brkcon *bc; +{ + bc->nextlev = brklist; + brklist = bc; +} + +/* + * Wait for the last process created. + * Print a message for each process found + * that was killed by a signal. + * Ignore interrupt signals while waiting + * unless `canintr' is true. + */ +static int waitfor(lastpid, canintr) +REGISTER int lastpid; +int canintr; +{ + REGISTER int pid, rv; + int s; + int oheedint = heedint; + + heedint = 0; + rv = 0; + do { + pid = wait(&s); + if (pid == -1) { + if (errno != EINTR || canintr) + break; + } else { + if ((rv = WAITSIG(s)) != 0) { + if (rv < NSIGNAL) { + if (signame[rv] != NULL) { + if (pid != lastpid) { + prn(pid); + prs(": "); + } + prs(signame[rv]); + } + } else { + if (pid != lastpid) { + prn(pid); + prs(": "); + } + prs("Signal "); + prn(rv); + prs(" "); + } + if (WAITCORE(s)) + prs(" - core dumped"); + if (rv >= NSIGNAL || signame[rv]) + prs("\n"); + rv = -1; + } else + rv = WAITVAL(s); + } + } while (pid != lastpid); + heedint = oheedint; + if (intr) { + if (interactive) { + if (canintr) + intr = 0; + } else { + if (exstat == 0) + exstat = rv; + onintr(0); + } + } + return (rv); +} + +static int setstatus(s) +REGISTER int s; +{ + exstat = s; + setval(lookup("?"), putn(s)); + return (s); +} + +/* + * PATH-searching interface to execve. + * If getenv("PATH") were kept up-to-date, + * execvp might be used. + */ +static char *rexecve(c, v, envp) +char *c, **v, **envp; +{ + REGISTER int i; + REGISTER char *sp, *tp; + int eacces = 0, asis = 0; + +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + char *name = c; + + optind = 1; + if (find_applet_by_name(name)) { + /* We have to exec here since we vforked. Running + * run_applet_by_name() won't work and bad things + * will happen. */ + execve("/proc/self/exe", v, envp); + execve("busybox", v, envp); + } +#endif + + DBGPRINTF(("REXECVE: c=0x%x, v=0x%x, envp=0x%x\n", c, v, envp)); + + sp = any('/', c) ? "" : path->value; + asis = *sp == '\0'; + while (asis || *sp != '\0') { + asis = 0; + tp = e.linep; + for (; *sp != '\0'; tp++) + if ((*tp = *sp++) == ':') { + asis = *sp == '\0'; + break; + } + if (tp != e.linep) + *tp++ = '/'; + for (i = 0; (*tp++ = c[i++]) != '\0';); + + DBGPRINTF3(("REXECVE: e.linep is %s\n", e.linep)); + + execve(e.linep, v, envp); + + switch (errno) { + case ENOEXEC: + *v = e.linep; + tp = *--v; + *v = e.linep; + execve(DEFAULT_SHELL, v, envp); + *v = tp; + return ("no Shell"); + + case ENOMEM: + return ((char *) bb_msg_memory_exhausted); + + case E2BIG: + return ("argument list too long"); + + case EACCES: + eacces++; + break; + } + } + return (errno == ENOENT ? "not found" : "cannot execute"); +} + +/* + * Run the command produced by generator `f' + * applied to stream `arg'. + */ +static int run(struct ioarg *argp, int (*f) (struct ioarg *)) +{ + struct op *otree; + struct wdblock *swdlist; + struct wdblock *siolist; + jmp_buf ev, rt; + xint *ofail; + int rv; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &rv; +#endif + + DBGPRINTF(("RUN: enter, areanum %d, outtree 0x%x, failpt 0x%x\n", + areanum, outtree, failpt)); + + areanum++; + swdlist = wdlist; + siolist = iolist; + otree = outtree; + ofail = failpt; + rv = -1; + + if (newenv(setjmp(errpt = ev)) == 0) { + wdlist = 0; + iolist = 0; + pushio(argp, f); + e.iobase = e.iop; + yynerrs = 0; + if (setjmp(failpt = rt) == 0 && yyparse() == 0) + rv = execute(outtree, NOPIPE, NOPIPE, 0); + quitenv(); + } else { + DBGPRINTF(("RUN: error from newenv()!\n")); + } + + wdlist = swdlist; + iolist = siolist; + failpt = ofail; + outtree = otree; + freearea(areanum--); + + return (rv); +} + +/* -------- do.c -------- */ + +/* + * built-in commands: doX + */ + +static int dohelp(struct op *t) +{ + int col; + const struct builtincmd *x; + + printf("\nBuilt-in commands:\n"); + printf("-------------------\n"); + + for (col = 0, x = builtincmds; x->builtinfunc != NULL; x++) { + if (!x->name) + continue; + col += printf("%s%s", ((col == 0) ? "\t" : " "), x->name); + if (col > 60) { + printf("\n"); + col = 0; + } + } +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + { + int i; + const struct BB_applet *applet; + extern const struct BB_applet applets[]; + extern const size_t NUM_APPLETS; + + for (i = 0, applet = applets; i < NUM_APPLETS; applet++, i++) { + if (!applet->name) + continue; + + col += printf("%s%s", ((col == 0) ? "\t" : " "), applet->name); + if (col > 60) { + printf("\n"); + col = 0; + } + } + } +#endif + printf("\n\n"); + return EXIT_SUCCESS; +} + + + +static int dolabel(struct op *t) +{ + return (0); +} + +static int dochdir(t) +REGISTER struct op *t; +{ + REGISTER char *cp, *er; + + if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL) + er = ": no home directory"; + else if (chdir(cp) < 0) + er = ": bad directory"; + else + return (0); + prs(cp != NULL ? cp : "cd"); + err(er); + return (1); +} + +static int doshift(t) +REGISTER struct op *t; +{ + REGISTER int n; + + n = t->words[1] ? getn(t->words[1]) : 1; + if (dolc < n) { + err("nothing to shift"); + return (1); + } + dolv[n] = dolv[0]; + dolv += n; + dolc -= n; + setval(lookup("#"), putn(dolc)); + return (0); +} + +/* + * execute login and newgrp directly + */ +static int dologin(t) +struct op *t; +{ + REGISTER char *cp; + + if (interactive) { + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + } + cp = rexecve(t->words[0], t->words, makenv(0, NULL)); + prs(t->words[0]); + prs(": "); + err(cp); + return (1); +} + +static int doumask(t) +REGISTER struct op *t; +{ + REGISTER int i, n; + REGISTER char *cp; + + if ((cp = t->words[1]) == NULL) { + i = umask(0); + umask(i); + for (n = 3 * 4; (n -= 3) >= 0;) + putc('0' + ((i >> n) & 07), stderr); + putc('\n', stderr); + } else { + for (n = 0; *cp >= '0' && *cp <= '9'; cp++) + n = n * 8 + (*cp - '0'); + umask(n); + } + return (0); +} + +static int doexec(t) +REGISTER struct op *t; +{ + REGISTER int i; + jmp_buf ex; + xint *ofail; + + t->ioact = NULL; + for (i = 0; (t->words[i] = t->words[i + 1]) != NULL; i++); + if (i == 0) + return (1); + execflg = 1; + ofail = failpt; + if (setjmp(failpt = ex) == 0) + execute(t, NOPIPE, NOPIPE, FEXEC); + failpt = ofail; + execflg = 0; + return (1); +} + +static int dodot(t) +struct op *t; +{ + REGISTER int i; + REGISTER char *sp, *tp; + char *cp; + int maltmp; + + DBGPRINTF(("DODOT: enter, t=0x%x, tleft 0x%x, tright 0x%x, e.linep is %s\n", t, t->left, t->right, ((e.linep == NULL) ? "NULL" : e.linep))); + + if ((cp = t->words[1]) == NULL) { + DBGPRINTF(("DODOT: bad args, ret 0\n")); + return (0); + } else { + DBGPRINTF(("DODOT: cp is %s\n", cp)); + } + + sp = any('/', cp) ? ":" : path->value; + + DBGPRINTF(("DODOT: sp is %s, e.linep is %s\n", + ((sp == NULL) ? "NULL" : sp), + ((e.linep == NULL) ? "NULL" : e.linep))); + + while (*sp) { + tp = e.linep; + while (*sp && (*tp = *sp++) != ':') + tp++; + if (tp != e.linep) + *tp++ = '/'; + + for (i = 0; (*tp++ = cp[i++]) != '\0';); + + /* Original code */ + if ((i = open(e.linep, 0)) >= 0) { + exstat = 0; + maltmp = remap(i); + DBGPRINTF(("DODOT: remap=%d, exstat=%d, e.iofd %d, i %d, e.linep is %s\n", maltmp, exstat, e.iofd, i, e.linep)); + + next(maltmp); /* Basically a PUSHIO */ + + DBGPRINTF(("DODOT: returning exstat=%d\n", exstat)); + + return (exstat); + } + + } /* While */ + + prs(cp); + err(": not found"); + + return (-1); +} + +static int dowait(t) +struct op *t; +{ + REGISTER int i; + REGISTER char *cp; + + if ((cp = t->words[1]) != NULL) { + i = getn(cp); + if (i == 0) + return (0); + } else + i = -1; + setstatus(waitfor(i, 1)); + return (0); +} + +static int doread(t) +struct op *t; +{ + REGISTER char *cp, **wp; + REGISTER int nb = 0; + REGISTER int nl = 0; + + if (t->words[1] == NULL) { + err("Usage: read name ..."); + return (1); + } + for (wp = t->words + 1; *wp; wp++) { + for (cp = e.linep; !nl && cp < elinep - 1; cp++) + if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) || + (nl = (*cp == '\n')) || (wp[1] && any(*cp, ifs->value))) + break; + *cp = 0; + if (nb <= 0) + break; + setval(lookup(*wp), e.linep); + } + return (nb <= 0); +} + +static int doeval(t) +REGISTER struct op *t; +{ + return (RUN(awordlist, t->words + 1, wdchar)); +} + +static int dotrap(t) +REGISTER struct op *t; +{ + REGISTER int n, i; + REGISTER int resetsig; + + if (t->words[1] == NULL) { + for (i = 0; i <= _NSIG; i++) + if (trap[i]) { + prn(i); + prs(": "); + prs(trap[i]); + prs("\n"); + } + return (0); + } + resetsig = isdigit(*t->words[1]); + for (i = resetsig ? 1 : 2; t->words[i] != NULL; ++i) { + n = getsig(t->words[i]); + freecell(trap[n]); + trap[n] = 0; + if (!resetsig) { + if (*t->words[1] != '\0') { + trap[n] = strsave(t->words[1], 0); + setsig(n, sig); + } else + setsig(n, SIG_IGN); + } else { + if (interactive) + if (n == SIGINT) + setsig(n, onintr); + else + setsig(n, n == SIGQUIT ? SIG_IGN : SIG_DFL); + else + setsig(n, SIG_DFL); + } + } + return (0); +} + +static int getsig(s) +char *s; +{ + REGISTER int n; + + if ((n = getn(s)) < 0 || n > _NSIG) { + err("trap: bad signal number"); + n = 0; + } + return (n); +} + +static void setsig(REGISTER int n, sighandler_t f) +{ + if (n == 0) + return; + if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) { + ourtrap[n] = 1; + signal(n, f); + } +} + +static int getn(as) +char *as; +{ + REGISTER char *s; + REGISTER int n, m; + + s = as; + m = 1; + if (*s == '-') { + m = -1; + s++; + } + for (n = 0; isdigit(*s); s++) + n = (n * 10) + (*s - '0'); + if (*s) { + prs(as); + err(": bad number"); + } + return (n * m); +} + +static int dobreak(t) +struct op *t; +{ + return (brkcontin(t->words[1], 1)); +} + +static int docontinue(t) +struct op *t; +{ + return (brkcontin(t->words[1], 0)); +} + +static int brkcontin(cp, val) +REGISTER char *cp; +int val; +{ + REGISTER struct brkcon *bc; + REGISTER int nl; + + nl = cp == NULL ? 1 : getn(cp); + if (nl <= 0) + nl = 999; + do { + if ((bc = brklist) == NULL) + break; + brklist = bc->nextlev; + } while (--nl); + if (nl) { + err("bad break/continue level"); + return (1); + } + isbreak = val; + longjmp(bc->brkpt, 1); + /* NOTREACHED */ +} + +static int doexit(t) +struct op *t; +{ + REGISTER char *cp; + + execflg = 0; + if ((cp = t->words[1]) != NULL) + setstatus(getn(cp)); + + DBGPRINTF(("DOEXIT: calling leave(), t=0x%x\n", t)); + + leave(); + /* NOTREACHED */ + return (0); +} + +static int doexport(t) +struct op *t; +{ + rdexp(t->words + 1, export, EXPORT); + return (0); +} + +static int doreadonly(t) +struct op *t; +{ + rdexp(t->words + 1, ronly, RONLY); + return (0); +} + +static void rdexp(char **wp, void (*f) (struct var *), int key) +{ + DBGPRINTF6(("RDEXP: enter, wp=0x%x, func=0x%x, key=%d\n", wp, f, key)); + DBGPRINTF6(("RDEXP: *wp=%s\n", *wp)); + + if (*wp != NULL) { + for (; *wp != NULL; wp++) { + if (isassign(*wp)) { + char *cp; + + assign(*wp, COPYV); + for (cp = *wp; *cp != '='; cp++); + *cp = '\0'; + } + if (checkname(*wp)) + (*f) (lookup(*wp)); + else + badid(*wp); + } + } else + putvlist(key, 1); +} + +static void badid(s) +REGISTER char *s; +{ + prs(s); + err(": bad identifier"); +} + +static int doset(t) +REGISTER struct op *t; +{ + REGISTER struct var *vp; + REGISTER char *cp; + REGISTER int n; + + if ((cp = t->words[1]) == NULL) { + for (vp = vlist; vp; vp = vp->next) + varput(vp->name, 1); + return (0); + } + if (*cp == '-') { + /* bad: t->words++; */ + for (n = 0; (t->words[n] = t->words[n + 1]) != NULL; n++); + if (*++cp == 0) + flag['x'] = flag['v'] = 0; + else + for (; *cp; cp++) + switch (*cp) { + case 'e': + if (!interactive) + flag['e']++; + break; + + default: + if (*cp >= 'a' && *cp <= 'z') + flag[(int) *cp]++; + break; + } + setdash(); + } + if (t->words[1]) { + t->words[0] = dolv[0]; + for (n = 1; t->words[n]; n++) + setarea((char *) t->words[n], 0); + dolc = n - 1; + dolv = t->words; + setval(lookup("#"), putn(dolc)); + setarea((char *) (dolv - 1), 0); + } + return (0); +} + +static void varput(s, out) +REGISTER char *s; +int out; +{ + if (isalnum(*s) || *s == '_') { + write(out, s, strlen(s)); + write(out, "\n", 1); + } +} + + +/* + * Copyright (c) 1999 Herbert Xu + * This file contains code for the times builtin. + */ +static int dotimes(struct op *t) +{ + struct tms buf; + long int clk_tck = sysconf(_SC_CLK_TCK); + + times(&buf); + printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n", + (int) (buf.tms_utime / clk_tck / 60), + ((double) buf.tms_utime) / clk_tck, + (int) (buf.tms_stime / clk_tck / 60), + ((double) buf.tms_stime) / clk_tck, + (int) (buf.tms_cutime / clk_tck / 60), + ((double) buf.tms_cutime) / clk_tck, + (int) (buf.tms_cstime / clk_tck / 60), + ((double) buf.tms_cstime) / clk_tck); + return 0; +} + + +static int (*inbuilt(char *s)) (struct op *) { + const struct builtincmd *bp; + + for (bp = builtincmds; bp->name != NULL; bp++) + if (strcmp(bp->name, s) == 0) + return (bp->builtinfunc); + + return (NULL); +} + +/* -------- eval.c -------- */ + +/* + * ${} + * `command` + * blank interpretation + * quoting + * glob + */ + +static char **eval(char **ap, int f) +{ + struct wdblock *wb; + char **wp; + char **wf; + jmp_buf ev; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) ℘ + (void) ≈ +#endif + + DBGPRINTF4(("EVAL: enter, f=%d\n", f)); + + wp = NULL; + wb = NULL; + wf = NULL; + if (newenv(setjmp(errpt = ev)) == 0) { + while (*ap && isassign(*ap)) + expand(*ap++, &wb, f & ~DOGLOB); + if (flag['k']) { + for (wf = ap; *wf; wf++) { + if (isassign(*wf)) + expand(*wf, &wb, f & ~DOGLOB); + } + } + for (wb = addword((char *) 0, wb); *ap; ap++) { + if (!flag['k'] || !isassign(*ap)) + expand(*ap, &wb, f & ~DOKEY); + } + wb = addword((char *) 0, wb); + wp = getwords(wb); + quitenv(); + } else + gflg = 1; + + return (gflg ? (char **) NULL : wp); +} + +/* + * Make the exported environment from the exported + * names in the dictionary. Keyword assignments + * will already have been done. + */ +static char **makenv(int all, struct wdblock *wb) +{ + REGISTER struct var *vp; + + DBGPRINTF5(("MAKENV: enter, all=%d\n", all)); + + for (vp = vlist; vp; vp = vp->next) + if (all || vp->status & EXPORT) + wb = addword(vp->name, wb); + wb = addword((char *) 0, wb); + return (getwords(wb)); +} + +static char *evalstr(cp, f) +REGISTER char *cp; +int f; +{ + struct wdblock *wb; + + DBGPRINTF6(("EVALSTR: enter, cp=0x%x, f=%d\n", cp, f)); + + wb = NULL; + if (expand(cp, &wb, f)) { + if (wb == NULL || wb->w_nword == 0 + || (cp = wb->w_words[0]) == NULL) + cp = ""; + DELETE(wb); + } else + cp = NULL; + return (cp); +} + +static int expand(char *cp, REGISTER struct wdblock **wbp, int f) +{ + jmp_buf ev; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &cp; +#endif + + DBGPRINTF3(("EXPAND: enter, f=%d\n", f)); + + gflg = 0; + + if (cp == NULL) + return (0); + + if (!anys("$`'\"", cp) && + !anys(ifs->value, cp) && ((f & DOGLOB) == 0 || !anys("[*?", cp))) { + cp = strsave(cp, areanum); + if (f & DOTRIM) + unquote(cp); + *wbp = addword(cp, *wbp); + return (1); + } + if (newenv(setjmp(errpt = ev)) == 0) { + PUSHIO(aword, cp, strchar); + e.iobase = e.iop; + while ((cp = blank(f)) && gflg == 0) { + e.linep = cp; + cp = strsave(cp, areanum); + if ((f & DOGLOB) == 0) { + if (f & DOTRIM) + unquote(cp); + *wbp = addword(cp, *wbp); + } else + *wbp = glob(cp, *wbp); + } + quitenv(); + } else + gflg = 1; + return (gflg == 0); +} + +/* + * Blank interpretation and quoting + */ +static char *blank(f) +int f; +{ + REGISTER int c, c1; + REGISTER char *sp; + int scanequals, foundequals; + + DBGPRINTF3(("BLANK: enter, f=%d\n", f)); + + sp = e.linep; + scanequals = f & DOKEY; + foundequals = 0; + + loop: + switch (c = subgetc('"', foundequals)) { + case 0: + if (sp == e.linep) + return (0); + *e.linep++ = 0; + return (sp); + + default: + if (f & DOBLANK && any(c, ifs->value)) + goto loop; + break; + + case '"': + case '\'': + scanequals = 0; + if (INSUB()) + break; + for (c1 = c; (c = subgetc(c1, 1)) != c1;) { + if (c == 0) + break; + if (c == '\'' || !any(c, "$`\"")) + c |= QUOTE; + *e.linep++ = c; + } + c = 0; + } + unget(c); + if (!isalpha(c) && c != '_') + scanequals = 0; + for (;;) { + c = subgetc('"', foundequals); + if (c == 0 || + f & (DOBLANK && any(c, ifs->value)) || + (!INSUB() && any(c, "\"'"))) { + scanequals = 0; + unget(c); + if (any(c, "\"'")) + goto loop; + break; + } + if (scanequals) { + if (c == '=') { + foundequals = 1; + scanequals = 0; + } else if (!isalnum(c) && c != '_') + scanequals = 0; + } + *e.linep++ = c; + } + *e.linep++ = 0; + return (sp); +} + +/* + * Get characters, substituting for ` and $ + */ +static int subgetc(ec, quoted) +REGISTER char ec; +int quoted; +{ + REGISTER char c; + + DBGPRINTF3(("SUBGETC: enter, quoted=%d\n", quoted)); + + again: + c = my_getc(ec); + if (!INSUB() && ec != '\'') { + if (c == '`') { + if (grave(quoted) == 0) + return (0); + e.iop->task = XGRAVE; + goto again; + } + if (c == '$' && (c = dollar(quoted)) == 0) { + e.iop->task = XDOLL; + goto again; + } + } + return (c); +} + +/* + * Prepare to generate the string returned by ${} substitution. + */ +static int dollar(quoted) +int quoted; +{ + int otask; + struct io *oiop; + char *dolp; + REGISTER char *s, c, *cp = NULL; + struct var *vp; + + DBGPRINTF3(("DOLLAR: enter, quoted=%d\n", quoted)); + + c = readc(); + s = e.linep; + if (c != '{') { + *e.linep++ = c; + if (isalpha(c) || c == '_') { + while ((c = readc()) != 0 && (isalnum(c) || c == '_')) + if (e.linep < elinep) + *e.linep++ = c; + unget(c); + } + c = 0; + } else { + oiop = e.iop; + otask = e.iop->task; + + e.iop->task = XOTHER; + while ((c = subgetc('"', 0)) != 0 && c != '}' && c != '\n') + if (e.linep < elinep) + *e.linep++ = c; + if (oiop == e.iop) + e.iop->task = otask; + if (c != '}') { + err("unclosed ${"); + gflg++; + return (c); + } + } + if (e.linep >= elinep) { + err("string in ${} too long"); + gflg++; + e.linep -= 10; + } + *e.linep = 0; + if (*s) + for (cp = s + 1; *cp; cp++) + if (any(*cp, "=-+?")) { + c = *cp; + *cp++ = 0; + break; + } + if (s[1] == 0 && (*s == '*' || *s == '@')) { + if (dolc > 1) { + /* currently this does not distinguish $* and $@ */ + /* should check dollar */ + e.linep = s; + PUSHIO(awordlist, dolv + 1, dolchar); + return (0); + } else { /* trap the nasty ${=} */ + s[0] = '1'; + s[1] = 0; + } + } + vp = lookup(s); + if ((dolp = vp->value) == null) { + switch (c) { + case '=': + if (isdigit(*s)) { + err("cannot use ${...=...} with $n"); + gflg++; + break; + } + setval(vp, cp); + dolp = vp->value; + break; + + case '-': + dolp = strsave(cp, areanum); + break; + + case '?': + if (*cp == 0) { + prs("missing value for "); + err(s); + } else + err(cp); + gflg++; + break; + } + } else if (c == '+') + dolp = strsave(cp, areanum); + if (flag['u'] && dolp == null) { + prs("unset variable: "); + err(s); + gflg++; + } + e.linep = s; + PUSHIO(aword, dolp, quoted ? qstrchar : strchar); + return (0); +} + +/* + * Run the command in `...` and read its output. + */ + +static int grave(quoted) +int quoted; +{ + char *cp; + REGISTER int i; + int j; + int pf[2]; + static char child_cmd[LINELIM]; + char *src; + char *dest; + int count; + int ignore; + int ignore_once; + char *argument_list[4]; + struct wdblock *wb = NULL; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &cp; +#endif + + for (cp = e.iop->argp->aword; *cp != '`'; cp++) + if (*cp == 0) { + err("no closing `"); + return (0); + } + + /* string copy with dollar expansion */ + src = e.iop->argp->aword; + dest = child_cmd; + count = 0; + ignore = 0; + ignore_once = 0; + while ((*src != '`') && (count < LINELIM)) { + if (*src == '\'') + ignore = !ignore; + if (*src == '\\') + ignore_once = 1; + if (*src == '$' && !ignore && !ignore_once) { + struct var *vp; + char var_name[LINELIM]; + char alt_value[LINELIM]; + int var_index = 0; + int alt_index = 0; + char operator = 0; + int braces = 0; + char *value; + + src++; + if (*src == '{') { + braces = 1; + src++; + } + + var_name[var_index++] = *src++; + while (isalnum(*src)) + var_name[var_index++] = *src++; + var_name[var_index] = 0; + + if (braces) { + switch (*src) { + case '}': + break; + case '-': + case '=': + case '+': + case '?': + operator = * src; + break; + default: + err("unclosed ${\n"); + return (0); + } + if (operator) { + src++; + while (*src && (*src != '}')) { + alt_value[alt_index++] = *src++; + } + alt_value[alt_index] = 0; + if (*src != '}') { + err("unclosed ${\n"); + return (0); + } + } + src++; + } + + if (isalpha(*var_name)) { + /* let subshell handle it instead */ + + char *namep = var_name; + + *dest++ = '$'; + if (braces) + *dest++ = '{'; + while (*namep) + *dest++ = *namep++; + if (operator) { + char *altp = alt_value; + *dest++ = operator; + while (*altp) + *dest++ = *altp++; + } + if (braces) + *dest++ = '}'; + + wb = addword(lookup(var_name)->name, wb); + } else { + /* expand */ + + vp = lookup(var_name); + if (vp->value != null) + value = (operator == '+') ? + alt_value : vp->value; + else if (operator == '?') { + err(alt_value); + return (0); + } else if (alt_index && (operator != '+')) { + value = alt_value; + if (operator == '=') + setval(vp, value); + } else + continue; + + while (*value && (count < LINELIM)) { + *dest++ = *value++; + count++; + } + } + } else { + *dest++ = *src++; + count++; + ignore_once = 0; + } + } + *dest = '\0'; + + if (openpipe(pf) < 0) + return (0); + + while ((i = vfork()) == -1 && errno == EAGAIN); + + DBGPRINTF3(("GRAVE: i is %d\n", io)); + + if (i < 0) { + closepipe(pf); + err((char *) bb_msg_memory_exhausted); + return (0); + } + if (i != 0) { + waitpid(i, NULL, 0); + e.iop->argp->aword = ++cp; + close(pf[1]); + PUSHIO(afile, remap(pf[0]), + (int (*)(struct ioarg *)) ((quoted) ? qgravechar : + gravechar)); + return (1); + } + /* allow trapped signals */ + /* XXX - Maybe this signal stuff should go as well? */ + for (j = 0; j <= _NSIG; j++) + if (ourtrap[j] && signal(j, SIG_IGN) != SIG_IGN) + signal(j, SIG_DFL); + + dup2(pf[1], 1); + closepipe(pf); + + argument_list[0] = (char *) DEFAULT_SHELL; + argument_list[1] = "-c"; + argument_list[2] = child_cmd; + argument_list[3] = 0; + + cp = rexecve(argument_list[0], argument_list, makenv(1, wb)); + prs(argument_list[0]); + prs(": "); + err(cp); + _exit(1); +} + + +static char *unquote(as) +REGISTER char *as; +{ + REGISTER char *s; + + if ((s = as) != NULL) + while (*s) + *s++ &= ~QUOTE; + return (as); +} + +/* -------- glob.c -------- */ + +/* + * glob + */ + +#define scopy(x) strsave((x), areanum) +#define BLKSIZ 512 +#define NDENT ((BLKSIZ+sizeof(struct dirent)-1)/sizeof(struct dirent)) + +static struct wdblock *cl, *nl; +static char spcl[] = "[?*"; + +static struct wdblock *glob(cp, wb) +char *cp; +struct wdblock *wb; +{ + REGISTER int i; + REGISTER char *pp; + + if (cp == 0) + return (wb); + i = 0; + for (pp = cp; *pp; pp++) + if (any(*pp, spcl)) + i++; + else if (!any(*pp & ~QUOTE, spcl)) + *pp &= ~QUOTE; + if (i != 0) { + for (cl = addword(scopy(cp), (struct wdblock *) 0); anyspcl(cl); + cl = nl) { + nl = newword(cl->w_nword * 2); + for (i = 0; i < cl->w_nword; i++) { /* for each argument */ + for (pp = cl->w_words[i]; *pp; pp++) + if (any(*pp, spcl)) { + globname(cl->w_words[i], pp); + break; + } + if (*pp == '\0') + nl = addword(scopy(cl->w_words[i]), nl); + } + for (i = 0; i < cl->w_nword; i++) + DELETE(cl->w_words[i]); + DELETE(cl); + } + for (i = 0; i < cl->w_nword; i++) + unquote(cl->w_words[i]); + glob0((char *) cl->w_words, cl->w_nword, sizeof(char *), xstrcmp); + if (cl->w_nword) { + for (i = 0; i < cl->w_nword; i++) + wb = addword(cl->w_words[i], wb); + DELETE(cl); + return (wb); + } + } + wb = addword(unquote(cp), wb); + return (wb); +} + +static void globname(we, pp) +char *we; +REGISTER char *pp; +{ + REGISTER char *np, *cp; + char *name, *gp, *dp; + int k; + DIR *dirp; + struct dirent *de; + char dname[NAME_MAX + 1]; + struct stat dbuf; + + for (np = we; np != pp; pp--) + if (pp[-1] == '/') + break; + for (dp = cp = space((int) (pp - np) + 3); np < pp;) + *cp++ = *np++; + *cp++ = '.'; + *cp = '\0'; + for (gp = cp = space(strlen(pp) + 1); *np && *np != '/';) + *cp++ = *np++; + *cp = '\0'; + dirp = opendir(dp); + if (dirp == 0) { + DELETE(dp); + DELETE(gp); + return; + } + dname[NAME_MAX] = '\0'; + while ((de = readdir(dirp)) != NULL) { + /* XXX Hmmm... What this could be? (abial) */ + /* + if (ent[j].d_ino == 0) + continue; + */ + strncpy(dname, de->d_name, NAME_MAX); + if (dname[0] == '.') + if (*gp != '.') + continue; + for (k = 0; k < NAME_MAX; k++) + if (any(dname[k], spcl)) + dname[k] |= QUOTE; + if (gmatch(dname, gp)) { + name = generate(we, pp, dname, np); + if (*np && !anys(np, spcl)) { + if (stat(name, &dbuf)) { + DELETE(name); + continue; + } + } + nl = addword(name, nl); + } + } + closedir(dirp); + DELETE(dp); + DELETE(gp); +} + +/* + * generate a pathname as below. + * start..end1 / middle end + * the slashes come for free + */ +static char *generate(start1, end1, middle, end) +char *start1; +REGISTER char *end1; +char *middle, *end; +{ + char *p; + REGISTER char *op, *xp; + + p = op = + space((int) (end1 - start1) + strlen(middle) + strlen(end) + 2); + for (xp = start1; xp != end1;) + *op++ = *xp++; + for (xp = middle; (*op++ = *xp++) != '\0';); + op--; + for (xp = end; (*op++ = *xp++) != '\0';); + return (p); +} + +static int anyspcl(wb) +REGISTER struct wdblock *wb; +{ + REGISTER int i; + REGISTER char **wd; + + wd = wb->w_words; + for (i = 0; i < wb->w_nword; i++) + if (anys(spcl, *wd++)) + return (1); + return (0); +} + +static int xstrcmp(p1, p2) +char *p1, *p2; +{ + return (strcmp(*(char **) p1, *(char **) p2)); +} + +/* -------- word.c -------- */ + +static struct wdblock *newword(nw) +REGISTER int nw; +{ + REGISTER struct wdblock *wb; + + wb = (struct wdblock *) space(sizeof(*wb) + nw * sizeof(char *)); + wb->w_bsize = nw; + wb->w_nword = 0; + return (wb); +} + +static struct wdblock *addword(wd, wb) +char *wd; +REGISTER struct wdblock *wb; +{ + REGISTER struct wdblock *wb2; + REGISTER int nw; + + if (wb == NULL) + wb = newword(NSTART); + if ((nw = wb->w_nword) >= wb->w_bsize) { + wb2 = newword(nw * 2); + memcpy((char *) wb2->w_words, (char *) wb->w_words, + nw * sizeof(char *)); + wb2->w_nword = nw; + DELETE(wb); + wb = wb2; + } + wb->w_words[wb->w_nword++] = wd; + return (wb); +} + +static +char **getwords(wb) +REGISTER struct wdblock *wb; +{ + REGISTER char **wd; + REGISTER int nb; + + if (wb == NULL) + return ((char **) NULL); + if (wb->w_nword == 0) { + DELETE(wb); + return ((char **) NULL); + } + wd = (char **) space(nb = sizeof(*wd) * wb->w_nword); + memcpy((char *) wd, (char *) wb->w_words, nb); + DELETE(wb); /* perhaps should done by caller */ + return (wd); +} + +int (*func) (char *, char *); +int globv; + +static void glob0(a0, a1, a2, a3) +char *a0; +unsigned a1; +int a2; +int (*a3) (char *, char *); +{ + func = a3; + globv = a2; + glob1(a0, a0 + a1 * a2); +} + +static void glob1(base, lim) +char *base, *lim; +{ + REGISTER char *i, *j; + int v2; + char *lptr, *hptr; + int c; + unsigned n; + + + v2 = globv; + + top: + if ((n = (int) (lim - base)) <= v2) + return; + n = v2 * (n / (2 * v2)); + hptr = lptr = base + n; + i = base; + j = lim - v2; + for (;;) { + if (i < lptr) { + if ((c = (*func) (i, lptr)) == 0) { + glob2(i, lptr -= v2); + continue; + } + if (c < 0) { + i += v2; + continue; + } + } + + begin: + if (j > hptr) { + if ((c = (*func) (hptr, j)) == 0) { + glob2(hptr += v2, j); + goto begin; + } + if (c > 0) { + if (i == lptr) { + glob3(i, hptr += v2, j); + i = lptr += v2; + goto begin; + } + glob2(i, j); + j -= v2; + i += v2; + continue; + } + j -= v2; + goto begin; + } + + + if (i == lptr) { + if (lptr - base >= lim - hptr) { + glob1(hptr + v2, lim); + lim = lptr; + } else { + glob1(base, lptr); + base = hptr + v2; + } + goto top; + } + + + glob3(j, lptr -= v2, i); + j = hptr -= v2; + } +} + +static void glob2(i, j) +char *i, *j; +{ + REGISTER char *index1, *index2, c; + int m; + + m = globv; + index1 = i; + index2 = j; + do { + c = *index1; + *index1++ = *index2; + *index2++ = c; + } while (--m); +} + +static void glob3(i, j, k) +char *i, *j, *k; +{ + REGISTER char *index1, *index2, *index3; + int c; + int m; + + m = globv; + index1 = i; + index2 = j; + index3 = k; + do { + c = *index1; + *index1++ = *index3; + *index3++ = *index2; + *index2++ = c; + } while (--m); +} + +/* -------- io.c -------- */ + +/* + * shell IO + */ + +static int my_getc(int ec) +{ + REGISTER int c; + + if (e.linep > elinep) { + while ((c = readc()) != '\n' && c); + err("input line too long"); + gflg++; + return (c); + } + c = readc(); + if ((ec != '\'') && (ec != '`') && (e.iop->task != XGRAVE)) { + if (c == '\\') { + c = readc(); + if (c == '\n' && ec != '\"') + return (my_getc(ec)); + c |= QUOTE; + } + } + return (c); +} + +static void unget(c) +int c; +{ + if (e.iop >= e.iobase) + e.iop->peekc = c; +} + +static int eofc() +{ + return e.iop < e.iobase || (e.iop->peekc == 0 && e.iop->prev == 0); +} + +static int readc() +{ + REGISTER int c; + + RCPRINTF(("READC: e.iop 0x%x, e.iobase 0x%x\n", e.iop, e.iobase)); + + for (; e.iop >= e.iobase; e.iop--) { + RCPRINTF(("READC: e.iop 0x%x, peekc 0x%x\n", e.iop, e.iop->peekc)); + if ((c = e.iop->peekc) != '\0') { + e.iop->peekc = 0; + return (c); + } else { + if (e.iop->prev != 0) { + if ((c = (*e.iop->iofn) (e.iop->argp, e.iop)) != '\0') { + if (c == -1) { + e.iop++; + continue; + } + if (e.iop == iostack) + ioecho(c); + return (e.iop->prev = c); + } else if (e.iop->task == XIO && e.iop->prev != '\n') { + e.iop->prev = 0; + if (e.iop == iostack) + ioecho('\n'); + return '\n'; + } + } + if (e.iop->task == XIO) { + if (multiline) { + return e.iop->prev = 0; + } + if (interactive && e.iop == iostack + 1) { +#ifdef CONFIG_FEATURE_COMMAND_EDITING + current_prompt = prompt->value; +#else + prs(prompt->value); +#endif + } + } + } + + } /* FOR */ + + if (e.iop >= iostack) { + RCPRINTF(("READC: return 0, e.iop 0x%x\n", e.iop)); + return (0); + } + + DBGPRINTF(("READC: leave()...\n")); + leave(); + + /* NOTREACHED */ + return (0); +} + +static void ioecho(c) +char c; +{ + if (flag['v']) + write(2, &c, sizeof c); +} + + +static void pushio(struct ioarg *argp, int (*fn) (struct ioarg *)) +{ + DBGPRINTF(("PUSHIO: argp 0x%x, argp->afid 0x%x, e.iop 0x%x\n", argp, + argp->afid, e.iop)); + + /* Set env ptr for io source to next array spot and check for array overflow */ + if (++e.iop >= &iostack[NPUSH]) { + e.iop--; + err("Shell input nested too deeply"); + gflg++; + return; + } + + /* We did not overflow the NPUSH array spots so setup data structs */ + + e.iop->iofn = (int (*)(struct ioarg *, struct io *)) fn; /* Store data source func ptr */ + + if (argp->afid != AFID_NOBUF) + e.iop->argp = argp; + else { + + e.iop->argp = ioargstack + (e.iop - iostack); /* MAL - index into stack */ + *e.iop->argp = *argp; /* copy data from temp area into stack spot */ + + /* MAL - mainbuf is for 1st data source (command line?) and all nested use a single shared buffer? */ + + if (e.iop == &iostack[0]) + e.iop->argp->afbuf = &mainbuf; + else + e.iop->argp->afbuf = &sharedbuf; + + /* MAL - if not a termimal AND (commandline OR readable file) then give it a buffer id? */ + /* This line appears to be active when running scripts from command line */ + if ((isatty(e.iop->argp->afile) == 0) + && (e.iop == &iostack[0] + || lseek(e.iop->argp->afile, 0L, 1) != -1)) { + if (++bufid == AFID_NOBUF) /* counter rollover check, AFID_NOBUF = 11111111 */ + bufid = AFID_ID; /* AFID_ID = 0 */ + + e.iop->argp->afid = bufid; /* assign buffer id */ + } + + DBGPRINTF(("PUSHIO: iostack 0x%x, e.iop 0x%x, afbuf 0x%x\n", + iostack, e.iop, e.iop->argp->afbuf)); + DBGPRINTF(("PUSHIO: mbuf 0x%x, sbuf 0x%x, bid %d, e.iop 0x%x\n", + &mainbuf, &sharedbuf, bufid, e.iop)); + + } + + e.iop->prev = ~'\n'; + e.iop->peekc = 0; + e.iop->xchar = 0; + e.iop->nlcount = 0; + + if (fn == filechar || fn == linechar) + e.iop->task = XIO; + else if (fn == (int (*)(struct ioarg *)) gravechar + || fn == (int (*)(struct ioarg *)) qgravechar) + e.iop->task = XGRAVE; + else + e.iop->task = XOTHER; + + return; +} + +static struct io *setbase(ip) +struct io *ip; +{ + REGISTER struct io *xp; + + xp = e.iobase; + e.iobase = ip; + return (xp); +} + +/* + * Input generating functions + */ + +/* + * Produce the characters of a string, then a newline, then EOF. + */ +static int nlchar(ap) +REGISTER struct ioarg *ap; +{ + REGISTER int c; + + if (ap->aword == NULL) + return (0); + if ((c = *ap->aword++) == 0) { + ap->aword = NULL; + return ('\n'); + } + return (c); +} + +/* + * Given a list of words, produce the characters + * in them, with a space after each word. + */ +static int wdchar(ap) +REGISTER struct ioarg *ap; +{ + REGISTER char c; + REGISTER char **wl; + + if ((wl = ap->awordlist) == NULL) + return (0); + if (*wl != NULL) { + if ((c = *(*wl)++) != 0) + return (c & 0177); + ap->awordlist++; + return (' '); + } + ap->awordlist = NULL; + return ('\n'); +} + +/* + * Return the characters of a list of words, + * producing a space between them. + */ +static int dolchar(ap) +REGISTER struct ioarg *ap; +{ + REGISTER char *wp; + + if ((wp = *ap->awordlist++) != NULL) { + PUSHIO(aword, wp, *ap->awordlist == NULL ? strchar : xxchar); + return (-1); + } + return (0); +} + +static int xxchar(ap) +REGISTER struct ioarg *ap; +{ + REGISTER int c; + + if (ap->aword == NULL) + return (0); + if ((c = *ap->aword++) == '\0') { + ap->aword = NULL; + return (' '); + } + return (c); +} + +/* + * Produce the characters from a single word (string). + */ +static int strchar(ap) +REGISTER struct ioarg *ap; +{ + REGISTER int c; + + if (ap->aword == NULL || (c = *ap->aword++) == 0) + return (0); + return (c); +} + +/* + * Produce quoted characters from a single word (string). + */ +static int qstrchar(ap) +REGISTER struct ioarg *ap; +{ + REGISTER int c; + + if (ap->aword == NULL || (c = *ap->aword++) == 0) + return (0); + return (c | QUOTE); +} + +/* + * Return the characters from a file. + */ +static int filechar(ap) +REGISTER struct ioarg *ap; +{ + REGISTER int i; + char c; + struct iobuf *bp = ap->afbuf; + + if (ap->afid != AFID_NOBUF) { + if ((i = ap->afid != bp->id) || bp->bufp == bp->ebufp) { + + if (i) + lseek(ap->afile, ap->afpos, 0); + + i = safe_read(ap->afile, bp->buf, sizeof(bp->buf)); + + if (i <= 0) { + closef(ap->afile); + return 0; + } + + bp->id = ap->afid; + bp->ebufp = (bp->bufp = bp->buf) + i; + } + + ap->afpos++; + return *bp->bufp++ & 0177; + } +#ifdef CONFIG_FEATURE_COMMAND_EDITING + if (interactive && isatty(ap->afile)) { + static char mycommand[BUFSIZ]; + static int position = 0, size = 0; + + while (size == 0 || position >= size) { + cmdedit_read_input(current_prompt, mycommand); + size = strlen(mycommand); + position = 0; + } + c = mycommand[position]; + position++; + return (c); + } else +#endif + + { + i = safe_read(ap->afile, &c, sizeof(c)); + return (i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0)); + } +} + +/* + * Return the characters from a here temp file. + */ +static int herechar(ap) +REGISTER struct ioarg *ap; +{ + char c; + + + if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) { + close(ap->afile); + c = 0; + } + return (c); + +} + +/* + * Return the characters produced by a process (`...`). + * Quote them if required, and remove any trailing newline characters. + */ +static int gravechar(ap, iop) +struct ioarg *ap; +struct io *iop; +{ + REGISTER int c; + + if ((c = qgravechar(ap, iop) & ~QUOTE) == '\n') + c = ' '; + return (c); +} + +static int qgravechar(ap, iop) +REGISTER struct ioarg *ap; +struct io *iop; +{ + REGISTER int c; + + DBGPRINTF3(("QGRAVECHAR: enter, ap=0x%x, iop=0x%x\n", ap, iop)); + + if (iop->xchar) { + if (iop->nlcount) { + iop->nlcount--; + return ('\n' | QUOTE); + } + c = iop->xchar; + iop->xchar = 0; + } else if ((c = filechar(ap)) == '\n') { + iop->nlcount = 1; + while ((c = filechar(ap)) == '\n') + iop->nlcount++; + iop->xchar = c; + if (c == 0) + return (c); + iop->nlcount--; + c = '\n'; + } + return (c != 0 ? c | QUOTE : 0); +} + +/* + * Return a single command (usually the first line) from a file. + */ +static int linechar(ap) +REGISTER struct ioarg *ap; +{ + REGISTER int c; + + if ((c = filechar(ap)) == '\n') { + if (!multiline) { + closef(ap->afile); + ap->afile = -1; /* illegal value */ + } + } + return (c); +} + +static void prs(s) +REGISTER char *s; +{ + if (*s) + write(2, s, strlen(s)); +} + +static void prn(u) +unsigned u; +{ + prs(itoa(u)); +} + +static void closef(i) +REGISTER int i; +{ + if (i > 2) + close(i); +} + +static void closeall() +{ + REGISTER int u; + + for (u = NUFILE; u < NOFILE;) + close(u++); +} + + +/* + * remap fd into Shell's fd space + */ +static int remap(fd) +REGISTER int fd; +{ + REGISTER int i; + int map[NOFILE]; + int newfd; + + + DBGPRINTF(("REMAP: fd=%d, e.iofd=%d\n", fd, e.iofd)); + + if (fd < e.iofd) { + for (i = 0; i < NOFILE; i++) + map[i] = 0; + + do { + map[fd] = 1; + newfd = dup(fd); + fd = newfd; + } while (fd >= 0 && fd < e.iofd); + + for (i = 0; i < NOFILE; i++) + if (map[i]) + close(i); + + if (fd < 0) + err("too many files open in shell"); + } + + return (fd); +} + +static int openpipe(pv) +REGISTER int *pv; +{ + REGISTER int i; + + if ((i = pipe(pv)) < 0) + err("can't create pipe - try again"); + return (i); +} + +static void closepipe(pv) +REGISTER int *pv; +{ + if (pv != NULL) { + close(*pv++); + close(*pv); + } +} + +/* -------- here.c -------- */ + +/* + * here documents + */ + +static void markhere(s, iop) +REGISTER char *s; +struct ioword *iop; +{ + REGISTER struct here *h, *lh; + + DBGPRINTF7(("MARKHERE: enter, s=0x%x\n", s)); + + h = (struct here *) space(sizeof(struct here)); + if (h == 0) + return; + + h->h_tag = evalstr(s, DOSUB); + if (h->h_tag == 0) + return; + + h->h_iop = iop; + iop->io_name = 0; + h->h_next = NULL; + if (inhere == 0) + inhere = h; + else + for (lh = inhere; lh != NULL; lh = lh->h_next) + if (lh->h_next == 0) { + lh->h_next = h; + break; + } + iop->io_flag |= IOHERE | IOXHERE; + for (s = h->h_tag; *s; s++) + if (*s & QUOTE) { + iop->io_flag &= ~IOXHERE; + *s &= ~QUOTE; + } + h->h_dosub = iop->io_flag & IOXHERE; +} + +static void gethere() +{ + REGISTER struct here *h, *hp; + + DBGPRINTF7(("GETHERE: enter...\n")); + + /* Scan here files first leaving inhere list in place */ + for (hp = h = inhere; h != NULL; hp = h, h = h->h_next) + readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub ? 0 : '\''); + + /* Make inhere list active - keep list intact for scraphere */ + if (hp != NULL) { + hp->h_next = acthere; + acthere = inhere; + inhere = NULL; + } +} + +static void readhere(name, s, ec) +char **name; +REGISTER char *s; +int ec; +{ + int tf; + char tname[30] = ".msh_XXXXXX"; + REGISTER int c; + jmp_buf ev; + char myline[LINELIM + 1]; + char *thenext; + + DBGPRINTF7(("READHERE: enter, name=0x%x, s=0x%x\n", name, s)); + + tf = mkstemp(tname); + if (tf < 0) + return; + + *name = strsave(tname, areanum); + if (newenv(setjmp(errpt = ev)) != 0) + unlink(tname); + else { + pushio(e.iop->argp, (int (*)(struct ioarg *)) e.iop->iofn); + e.iobase = e.iop; + for (;;) { + if (interactive && e.iop <= iostack) { +#ifdef CONFIG_FEATURE_COMMAND_EDITING + current_prompt = cprompt->value; +#else + prs(cprompt->value); +#endif + } + thenext = myline; + while ((c = my_getc(ec)) != '\n' && c) { + if (ec == '\'') + c &= ~QUOTE; + if (thenext >= &myline[LINELIM]) { + c = 0; + break; + } + *thenext++ = c; + } + *thenext = 0; + if (strcmp(s, myline) == 0 || c == 0) + break; + *thenext++ = '\n'; + write(tf, myline, (int) (thenext - myline)); + } + if (c == 0) { + prs("here document `"); + prs(s); + err("' unclosed"); + } + quitenv(); + } + close(tf); +} + +/* + * open here temp file. + * if unquoted here, expand here temp file into second temp file. + */ +static int herein(hname, xdoll) +char *hname; +int xdoll; +{ + REGISTER int hf; + int tf; + +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &tf; +#endif + if (hname == NULL) + return (-1); + + DBGPRINTF7(("HEREIN: hname is %s, xdoll=%d\n", hname, xdoll)); + + hf = open(hname, 0); + if (hf < 0) + return (-1); + + if (xdoll) { + char c; + char tname[30] = ".msh_XXXXXX"; + jmp_buf ev; + + tf = mkstemp(tname); + if (tf < 0) + return (-1); + if (newenv(setjmp(errpt = ev)) == 0) { + PUSHIO(afile, hf, herechar); + setbase(e.iop); + while ((c = subgetc(0, 0)) != 0) { + c &= ~QUOTE; + write(tf, &c, sizeof c); + } + quitenv(); + } else + unlink(tname); + close(tf); + tf = open(tname, 0); + unlink(tname); + return (tf); + } else + return (hf); +} + +static void scraphere() +{ + REGISTER struct here *h; + + DBGPRINTF7(("SCRAPHERE: enter...\n")); + + for (h = inhere; h != NULL; h = h->h_next) { + if (h->h_iop && h->h_iop->io_name) + unlink(h->h_iop->io_name); + } + inhere = NULL; +} + +/* unlink here temp files before a freearea(area) */ +static void freehere(area) +int area; +{ + REGISTER struct here *h, *hl; + + DBGPRINTF6(("FREEHERE: enter, area=%d\n", area)); + + hl = NULL; + for (h = acthere; h != NULL; h = h->h_next) + if (getarea((char *) h) >= area) { + if (h->h_iop->io_name != NULL) + unlink(h->h_iop->io_name); + if (hl == NULL) + acthere = h->h_next; + else + hl->h_next = h->h_next; + } else + hl = h; +} + + + +/* + * Copyright (c) 1987,1997, Prentice Hall + * All rights reserved. + * + * Redistribution and use of the MINIX operating system in source and + * binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of Prentice Hall nor the names of the software + * authors or contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ diff --git a/busybox/sysdeps/linux/Config.in b/busybox/sysdeps/linux/Config.in new file mode 100644 index 000000000..744a84d18 --- /dev/null +++ b/busybox/sysdeps/linux/Config.in @@ -0,0 +1,294 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +mainmenu "BusyBox Configuration" + +config HAVE_DOT_CONFIG + bool + default y + +menu "General Configuration" + +choice + prompt "Buffer allocation policy" + default CONFIG_FEATURE_BUFFERS_USE_MALLOC + help + There are 3 ways BusyBox can handle buffer allocations: + - Use malloc. This costs code size for the call to xmalloc. + - Put them on stack. For some very small machines with limited stack + space, this can be deadly. For most folks, this works just fine. + - Put them in BSS. This works beautifully for computers with a real + MMU (and OS support), but wastes runtime RAM for uCLinux. This + behavior was the only one available for BusyBox versions 0.48 and + earlier. + +config CONFIG_FEATURE_BUFFERS_USE_MALLOC + bool "Allocate with Malloc" + +config CONFIG_FEATURE_BUFFERS_GO_ON_STACK + bool "Allocate on the Stack" + +config CONFIG_FEATURE_BUFFERS_GO_IN_BSS + bool "Allocate in the .bss section" + +endchoice + +config CONFIG_FEATURE_VERBOSE_USAGE + bool "Show verbose applet usage messages" + default n + help + All BusyBox applets will show more verbose help messages when + busybox is invoked with --help. This will add a lot of text to the + busybox binary. In the default configuration, this will add about + 13k, but it can add much more depending on your configuration. + +config CONFIG_FEATURE_INSTALLER + bool "Support --install [-s] to install applet links at runtime" + default n + help + Enable 'busybox --install [-s]' support. This will allow you to use + busybox at runtime to create hard links or symlinks for all the + applets that are compiled into busybox. This feature requires the + /proc filesystem. + +config CONFIG_LOCALE_SUPPORT + bool "Enable locale support (system needs locale for this to work)" + default n + help + Enable this if your system has locale support and you would like + busybox to support locale settings. + +config CONFIG_FEATURE_DEVFS + bool "Support for devfs" + default n + help + Enable if you want BusyBox to work with devfs. + +config CONFIG_FEATURE_DEVPTS + bool "Use the devpts filesystem for Unix98 PTYs" + default y if CONFIG_FEATURE_DEVFS + help + Enable if you want BusyBox to use Unix98 PTY support. If enabled, + busybox will use /dev/ptmx for the master side of the pseudoterminal + and /dev/pts/ for the slave side. Otherwise, BSD style + /dev/ttyp will be used. To use this option, you should have + devpts or devfs mounted. + +config CONFIG_FEATURE_CLEAN_UP + bool "Clean up all memory before exiting (usually not needed)" + default n + help + As a size optimization, busybox by default does not cleanup memory + that is dynamically allocated or close files before exiting. This + saves space and is usually not needed since the OS will clean up for + us. Don't enable this unless you have a really good reason to clean + things up manually. + +config CONFIG_FEATURE_SUID + bool "Support for SUID/SGID handling" + default n + help + Support SUID and SGID binaries. + +config CONFIG_FEATURE_SUID_CONFIG + bool "Runtime SUID/SGID configuration via /etc/busybox.conf" + default y if CONFIG_FEATURE_SUID + depends on CONFIG_FEATURE_SUID + help + Allow the SUID / SGID state of an applet to be determined runtime by + checking /etc/busybox.conf. The format of this file is as follows: + + = [Ssx-][Ssx-][x-] (|).(|) + + An example might help: + + [SUID] + su = ssx root.0 # applet su can be run by anyone and runs with euid=0/egid=0 + su = ssx # exactly the same + + mount = sx- root.disk # applet mount can be run by root and members of group disk + # and runs with euid=0 + + cp = --- # disable applet cp for everyone + + Robert 'sandman' Griebl has more information here: + . + +config CONFIG_FEATURE_SUID_CONFIG_QUIET + bool "Suppress warning message if /etc/busybox.conf is not readable" + default n + depends on CONFIG_FEATURE_SUID_CONFIG + help + /etc/busybox.conf should be readable by the user needing the SUID, check + this option to avoid users to be notified about missing permissions. + +config CONFIG_SELINUX + bool "Support NSA Security Enhanced Linux" + default n + help + Enable support for SE Linux in applets ls, ps, and id. Also provide + the option of compiling in SE Linux applets. + + If you do not have a complete SE Linux Full Userland installed, this + stuff will not compile. Go visit + http://www.nsa.gov/selinux/index.html + to download the necessary stuff to allow busybox to compile with this + option enabled. + + Most people will leave this set to 'N'. + +endmenu + +menu 'Build Options' + +config CONFIG_STATIC + bool "Build BusyBox as a static binary (no shared libs)" + default n + help + If you want to build a static BusyBox binary, which does not + use or require any shared libraries, then enable this option. + This can cause BusyBox to be considerably larger, so you should + leave this option false unless you have a good reason (i.e. + your target platform does not support shared libraries, or + you are building an initrd which doesn't need anything but + BusyBox, etc). + + Most people will leave this set to 'N'. + +config CONFIG_LFS + bool "Build with Large File Support (for accessing files > 2 GB)" + default n + select FDISK_SUPPORT_LARGE_DISKS + help + If you want to build BusyBox with large file support, then enable + this option. This will have no effect if your kernel or your C + library lacks large file support for large files. Some of the + programs that can benefit from large file support include dd, gzip, + cp, mount, tar, and many others. If you want to access files larger + than 2 Gigabytes, enable this option. Otherwise, leave it set to 'N'. + +config USING_CROSS_COMPILER + bool "Do you want to build BusyBox with a Cross Compiler?" + default n + help + Do you want to build BusyBox with a Cross Compiler? If so, + then enable this option. Otherwise leave it set to 'N'. + +config CROSS_COMPILER_PREFIX + string "Cross Compiler prefix" + default "/usr/i386-linux-uclibc/bin/i386-uclibc-" + depends on USING_CROSS_COMPILER + help + If you want to build BusyBox with a cross compiler, then you + will need to set this to the cross-compiler prefix. For example, + if my cross-compiler is /usr/i386-linux-uclibc/bin/i386-uclibc-gcc + then I would enter '/usr/i386-linux-uclibc/bin/i386-uclibc-' here, + which will ensure the correct compiler is used. + +config EXTRA_CFLAGS_OPTIONS + string "Any extra CFLAGS options for the compiler?" + default "" + help + Do you want to pass any extra CFLAGS options to the compiler as + you build BusyBox? If so, this is the option for you... For example, + if you want to add some simple compiler switches (like -march=i686), + or check for warnings using -Werror, just those options here. + +endmenu + +menu 'Installation Options' + +config CONFIG_INSTALL_NO_USR + bool "Don't use /usr" + default n + help + Disable use of /usr. Don't activate this option if you don't know + that you really want this behaviour. + +config PREFIX + string "BusyBox installation prefix" + default "./_install" + help + Define your directory to install BusyBox files/subdirs in. + + + +endmenu + +source archival/Config.in +source coreutils/Config.in +source console-tools/Config.in +source debianutils/Config.in +source editors/Config.in +source findutils/Config.in +source init/Config.in +source loginutils/Config.in +source miscutils/Config.in +source modutils/Config.in +source networking/Config.in +source procps/Config.in +source shell/Config.in +source sysklogd/Config.in +source util-linux/Config.in + +menu 'Debugging Options' + +config CONFIG_DEBUG + bool "Build BusyBox with Debugging symbols" + default n + help + Say Y here if you wish to compile BusyBox with debugging symbols. + This will allow you to use a debugger to examine BusyBox internals + while applets are running. This increases the size of the binary + considerably and should only be used when doing development. + If you are doing development and want to debug BusyBox, answer Y. + + Most people should answer N. + +choice + prompt "Additional debugging library" + default CONFIG_NO_DEBUG_LIB + depends on CONFIG_DEBUG + help + Using an additional debugging library will make BusyBox become + considerable larger and will cause it to run more slowly. You + should always leave this option disabled for production use. + + dmalloc support: + ---------------- + This enables compiling with dmalloc ( http://dmalloc.com/ ) + which is an excellent public domain mem leak and malloc problem + detector. To enable dmalloc, before running busybox you will + want to properly set your environment, for example: + export DMALLOC_OPTIONS=debug=0x34f47d83,inter=100,log=logfile + The 'debug=' value is generated using the following command + dmalloc -p log-stats -p log-non-free -p log-bad-space -p log-elapsed-time \ + -p check-fence -p check-heap -p check-lists -p check-blank \ + -p check-funcs -p realloc-copy -p allow-free-null + + Electric-fence support: + ----------------------- + This enables compiling with Electric-fence support. Electric + fence is another very useful malloc debugging library which uses + your computer's virtual memory hardware to detect illegal memory + accesses. This support will make BusyBox be considerable larger + and run slower, so you should leave this option disabled unless + you are hunting a hard to find memory problem. + + +config CONFIG_NO_DEBUG_LIB + bool "None" + +config CONFIG_DMALLOC + bool "Dmalloc" + +config CONFIG_EFENCE + bool "Electric-fence" + +endchoice + + +endmenu + diff --git a/busybox/sysdeps/linux/defconfig b/busybox/sysdeps/linux/defconfig new file mode 100644 index 000000000..fd2b118e3 --- /dev/null +++ b/busybox/sysdeps/linux/defconfig @@ -0,0 +1,421 @@ +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y + +# +# General Configuration +# +# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set +CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +CONFIG_FEATURE_VERBOSE_USAGE=y +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_FEATURE_DEVFS is not set +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_SUID is not set +# CONFIG_SELINUX is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_LFS is not set +# USING_CROSS_COMPILER is not set +EXTRA_CFLAGS_OPTIONS="" + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +PREFIX="./_install" + +# +# Archival Utilities +# +# CONFIG_AR is not set +CONFIG_BUNZIP2=y +# CONFIG_CPIO is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +CONFIG_GUNZIP=y +# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set +CONFIG_GZIP=y +# CONFIG_RPM2CPIO is not set +# CONFIG_RPM is not set +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_BZIP2=y +# CONFIG_FEATURE_TAR_FROM is not set +CONFIG_FEATURE_TAR_GZIP=y +# CONFIG_FEATURE_TAR_COMPRESS is not set +CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set +# CONFIG_UNCOMPRESS is not set +CONFIG_UNZIP=y + +# +# Common options for cpio and tar +# +# CONFIG_FEATURE_UNARCHIVE_TAPE is not set + +# +# Coreutils +# +CONFIG_BASENAME=y +# CONFIG_CAL is not set +CONFIG_CAT=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_CHROOT=y +CONFIG_CMP=y +CONFIG_CP=y +CONFIG_CUT=y +CONFIG_DATE=y +CONFIG_FEATURE_DATE_ISOFMT=y +CONFIG_DD=y +CONFIG_DF=y +CONFIG_DIRNAME=y +# CONFIG_DOS2UNIX is not set +CONFIG_DU=y +CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K=y +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_EXPR=y +CONFIG_FALSE=y +# CONFIG_FOLD is not set +CONFIG_HEAD=y +# CONFIG_FEATURE_FANCY_HEAD is not set +# CONFIG_HOSTID is not set +CONFIG_ID=y +CONFIG_INSTALL=y +# CONFIG_LENGTH is not set +CONFIG_LN=y +# CONFIG_LOGNAME is not set +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +CONFIG_FEATURE_LS_COLOR=y +# CONFIG_MD5SUM is not set +CONFIG_MKDIR=y +# CONFIG_MKFIFO is not set +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_OD is not set +# CONFIG_PRINTF is not set +CONFIG_PWD=y +# CONFIG_REALPATH is not set +CONFIG_RM=y +CONFIG_RMDIR=y +# CONFIG_SEQ is not set +# CONFIG_SHA1SUM is not set +CONFIG_SLEEP=y +# CONFIG_FEATURE_FANCY_SLEEP is not set +CONFIG_SORT=y +# CONFIG_STTY is not set +CONFIG_SYNC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TEST=y + +# +# test (forced enabled for use with shell) +# +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +CONFIG_UNIQ=y +CONFIG_USLEEP=y +# CONFIG_UUDECODE is not set +# CONFIG_UUENCODE is not set +# CONFIG_WATCH is not set +CONFIG_WC=y +# CONFIG_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls and more +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +# CONFIG_DUMPKMAP is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +CONFIG_OPENVT=y +CONFIG_RESET=y +# CONFIG_SETKEYCODES is not set + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +# CONFIG_PIPE_PROGRESS is not set +CONFIG_READLINK=y +# CONFIG_RUN_PARTS is not set +# CONFIG_START_STOP_DAEMON is not set +CONFIG_WHICH=y + +# +# Editors +# +# CONFIG_AWK is not set +# CONFIG_PATCH is not set +CONFIG_SED=y +CONFIG_VI=y +CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_YANKMARK=y +CONFIG_FEATURE_VI_SEARCH=y +CONFIG_FEATURE_VI_USE_SIGNALS=y +CONFIG_FEATURE_VI_DOT_CMD=y +CONFIG_FEATURE_VI_READONLY=y +CONFIG_FEATURE_VI_SETOPTS=y +CONFIG_FEATURE_VI_SET=y +CONFIG_FEATURE_VI_WIN_RESIZE=y +CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +# CONFIG_FEATURE_FIND_NEWER is not set +# CONFIG_FEATURE_FIND_INUM is not set +CONFIG_GREP=y +CONFIG_FEATURE_GREP_EGREP_ALIAS=y +CONFIG_FEATURE_GREP_FGREP_ALIAS=y +CONFIG_FEATURE_GREP_CONTEXT=y +CONFIG_XARGS=y +# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set +CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y +CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y +CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +CONFIG_FEATURE_INITRD=y +# CONFIG_FEATURE_INIT_COREDUMPS is not set +CONFIG_FEATURE_EXTRA_QUIET=y +CONFIG_HALT=y +CONFIG_POWEROFF=y +CONFIG_REBOOT=y +# CONFIG_MESG is not set + +# +# Login/Password Management Utilities +# +# CONFIG_USE_BB_PWD_GRP is not set +# CONFIG_ADDGROUP is not set +# CONFIG_DELGROUP is not set +# CONFIG_ADDUSER is not set +# CONFIG_DELUSER is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PASSWD is not set +# CONFIG_SU is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Miscellaneous Utilities +# +# CONFIG_ADJTIMEX is not set +# CONFIG_CROND is not set +# CONFIG_CRONTAB is not set +# CONFIG_DC is not set +# CONFIG_DEVFSD is not set +# CONFIG_LAST is not set +# CONFIG_HDPARM is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_MT is not set +# CONFIG_RX is not set +CONFIG_STRINGS=y +CONFIG_TIME=y +# CONFIG_WATCHDOG is not set + +# +# Linux Module Utilities +# +# CONFIG_INSMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_MODPROBE is not set +# CONFIG_RMMOD is not set + +# +# Networking Utilities +# +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_ARPING is not set +# CONFIG_FTPGET is not set +# CONFIG_FTPPUT is not set +CONFIG_HOSTNAME=y +# CONFIG_HTTPD is not set +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set +# CONFIG_FEATURE_IFCONFIG_HW is not set +# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set +# CONFIG_IFUPDOWN is not set +# CONFIG_INETD is not set +# CONFIG_IP is not set +# CONFIG_IPCALC is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_NAMEIF is not set +# CONFIG_NC is not set +# CONFIG_NETSTAT is not set +# CONFIG_NSLOOKUP is not set +CONFIG_PING=y +CONFIG_FEATURE_FANCY_PING=y +CONFIG_ROUTE=y +# CONFIG_TELNET is not set +# CONFIG_TELNETD is not set +# CONFIG_TFTP is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +# CONFIG_FEATURE_WGET_IP6_LITERAL is not set + +# +# udhcp Server/Client +# +# CONFIG_UDHCPD is not set +# CONFIG_UDHCPC is not set + +# +# Process Utilities +# +CONFIG_FREE=y +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_PIDOF=y +CONFIG_PS=y +# CONFIG_RENICE is not set +# CONFIG_TOP is not set +CONFIG_UPTIME=y +# CONFIG_SYSCTL is not set + +# +# Another Bourne-like Shell +# +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_LASH is not set +# CONFIG_FEATURE_SH_IS_MSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +CONFIG_ASH=y + +# +# Ash Shell Options +# +CONFIG_ASH_JOB_CONTROL=y +CONFIG_ASH_ALIAS=y +CONFIG_ASH_MATH_SUPPORT=y +CONFIG_ASH_MATH_SUPPORT_64=y +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +CONFIG_ASH_OPTIMIZE_FOR_SIZE=y +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_HUSH is not set +# CONFIG_LASH is not set +# CONFIG_MSH is not set + +# +# Bourne Shell Options +# +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set +CONFIG_FEATURE_COMMAND_EDITING=y +CONFIG_FEATURE_COMMAND_HISTORY=15 +CONFIG_FEATURE_COMMAND_SAVEHISTORY=y +CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y +# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set +CONFIG_FEATURE_SH_FANCY_PROMPT=y + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_KLOGD=y +CONFIG_LOGGER=y + +# +# Linux System Utilities +# +CONFIG_DMESG=y +# CONFIG_FBSET is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +# CONFIG_FREERAMDISK is not set +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_GETOPT is not set +CONFIG_HEXDUMP=y +# CONFIG_HWCLOCK is not set +# CONFIG_LOSETUP is not set +# CONFIG_MKSWAP is not set +CONFIG_MORE=y +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_PIVOT_ROOT=y +# CONFIG_RDATE is not set +CONFIG_SWAPONOFF=y +CONFIG_MOUNT=y +# CONFIG_NFSMOUNT is not set +CONFIG_UMOUNT=y +# CONFIG_FEATURE_MOUNT_FORCE is not set + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Debugging Options +# +# CONFIG_DEBUG is not set diff --git a/busybox/sysklogd/Config.in b/busybox/sysklogd/Config.in new file mode 100644 index 000000000..f77d79e8c --- /dev/null +++ b/busybox/sysklogd/Config.in @@ -0,0 +1,109 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "System Logging Utilities" + +config CONFIG_SYSLOGD + bool "syslogd" + default n + help + The syslogd utility is used to record logs of all the + significant events that occur on a system. Every + message that is logged records the date and time of the + event, and will generally also record the name of the + application that generated the message. When used in + conjunction with klogd, messages from the Linux kernel + can also be recorded. This is terribly useful, + especially for finding what happened when something goes + wrong. And something almost always will go wrong if + you wait long enough.... + +config CONFIG_FEATURE_ROTATE_LOGFILE + bool " Rotate message files" + default n + depends on CONFIG_SYSLOGD + help + This enables syslogd to rotate the message files + on his own. No need to use an external rotatescript. + +config CONFIG_FEATURE_REMOTE_LOG + bool " Remote Log support" + default n + depends on CONFIG_SYSLOGD + help + When you enable this feature, the syslogd utility can + be used to send system log messages to another system + connected via a network. This allows the remote + machine to log all the system messages, which can be + terribly useful for reducing the number of serial + cables you use. It can also be a very good security + measure to prevent system logs from being tampered with + by an intruder. + +config CONFIG_FEATURE_IPC_SYSLOG + bool " Circular Buffer support" + default n + depends on CONFIG_SYSLOGD + help + When you enable this feature, the syslogd utility will + use a circular buffer to record system log messages. + When the buffer is filled it will continue to overwrite + the oldest messages. This can be very useful for + systems with little or no permanent storage, since + otherwise system logs can eventually fill up your + entire filesystem, which may cause your system to + break badly. + +config CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE + int " Circular buffer size in Kbytes (minimum 4KB)" + default 16 + depends on CONFIG_FEATURE_IPC_SYSLOG + help + This option sets the size of the circular buffer + used to record system log messages. + +config CONFIG_LOGREAD + bool " logread" + default y + depends on CONFIG_FEATURE_IPC_SYSLOG + help + If you enabled Circular Buffer support, you almost + certainly want to enable this feature as well. This + utility will allow you to read the messages that are + stored in the syslogd circular buffer. + +config CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING + bool " logread double buffering" + default n + depends on CONFIG_LOGREAD + help + 'logread' ouput to slow serial terminals can have + side effects on syslog because of the semaphore. + This option make logread to double buffer copy + from circular buffer, minimizing semaphore + contention at some minor memory expense. + +config CONFIG_KLOGD + bool "klogd" + default n + depends on CONFIG_SYSLOGD + help + klogd is a utility which intercepts and logs all + messages from the Linux kernel and sends the messages + out to the 'syslogd' utility so they can be logged. If + you wish to record the messages produced by the kernel, + you should enable this option. + +config CONFIG_LOGGER + bool "logger" + default n + help + The logger utility allows you to send arbitrary text + messages to the system log (i.e. the 'syslogd' utility) so + they can be logged. This is generally used to help locate + problems that occur within programs and scripts. + +endmenu + diff --git a/busybox/sysklogd/Makefile b/busybox/sysklogd/Makefile new file mode 100644 index 000000000..78b0c0090 --- /dev/null +++ b/busybox/sysklogd/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_buildddir=.. +srcdir=$(top_srcdir)/sysklogd +SYSKLOGD_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/sysklogd/Makefile.in b/busybox/sysklogd/Makefile.in new file mode 100644 index 000000000..99a5f823c --- /dev/null +++ b/busybox/sysklogd/Makefile.in @@ -0,0 +1,39 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +SYSKLOGD_AR:=sysklogd.a +ifndef $(SYSKLOGD_DIR) +SYSKLOGD_DIR:=$(top_builddir)/sysklogd/ +endif +srcdir=$(top_srcdir)/sysklogd + +SYSKLOGD-:= +SYSKLOGD-$(CONFIG_KLOGD) += klogd.o +SYSKLOGD-$(CONFIG_LOGGER) += logger.o +SYSKLOGD-$(CONFIG_LOGREAD) += logread.o +SYSKLOGD-$(CONFIG_SYSLOGD) += syslogd.o + +libraries-y+=$(SYSKLOGD_DIR)$(SYSKLOGD_AR) + +$(SYSKLOGD_DIR)$(SYSKLOGD_AR): $(patsubst %,$(SYSKLOGD_DIR)%, $(SYSKLOGD-y)) + $(AR) -ro $@ $(patsubst %,$(SYSKLOGD_DIR)%, $(SYSKLOGD-y)) + +$(SYSKLOGD_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + diff --git a/busybox/sysklogd/klogd.c b/busybox/sysklogd/klogd.c new file mode 100644 index 000000000..c908b593c --- /dev/null +++ b/busybox/sysklogd/klogd.c @@ -0,0 +1,165 @@ +/* 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-2004 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 +#include + +#include "busybox.h" + +static void klogd_signal(int sig) +{ + klogctl(7, NULL, 0); + klogctl(0, 0, 0); + /* logMessage(0, "Kernel log daemon exiting."); */ + syslog(LOG_NOTICE, "Kernel log daemon exiting."); + exit(EXIT_SUCCESS); +} + +static void doKlogd(const int console_log_level) __attribute__ ((noreturn)); +static void doKlogd(const int console_log_level) +{ + int priority = LOG_INFO; + char log_buffer[4096]; + int i, n, lastc; + char *start; + + openlog("kernel", 0, LOG_KERN); + + /* Set up sig handlers */ + signal(SIGINT, klogd_signal); + signal(SIGKILL, klogd_signal); + signal(SIGTERM, klogd_signal); + signal(SIGHUP, SIG_IGN); + + /* "Open the log. Currently a NOP." */ + klogctl(1, NULL, 0); + + /* Set level of kernel console messaging.. */ + if (console_log_level != -1) + klogctl(8, NULL, console_log_level); + + syslog(LOG_NOTICE, "klogd started: " BB_BANNER); + + while (1) { + /* Use kernel syscalls */ + memset(log_buffer, '\0', sizeof(log_buffer)); + n = klogctl(2, log_buffer, sizeof(log_buffer)); + if (n < 0) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "klogd: Error return from sys_sycall: %d - %m.\n", errno); + exit(EXIT_FAILURE); + } + + /* klogctl buffer parsing modelled after code in dmesg.c */ + start = &log_buffer[0]; + lastc = '\0'; + for (i = 0; i < n; i++) { + if (lastc == '\0' && log_buffer[i] == '<') { + priority = 0; + i++; + while (isdigit(log_buffer[i])) { + priority = priority * 10 + (log_buffer[i] - '0'); + i++; + } + if (log_buffer[i] == '>') + i++; + start = &log_buffer[i]; + } + if (log_buffer[i] == '\n') { + log_buffer[i] = '\0'; /* zero terminate this message */ + syslog(priority, "%s", start); + start = &log_buffer[i + 1]; + priority = LOG_INFO; + } + lastc = log_buffer[i]; + } + } +} + +extern int klogd_main(int argc, char **argv) +{ + /* no options, no getopt */ + int opt; + int doFork = TRUE; + unsigned char console_log_level = -1; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "c:n")) > 0) { + switch (opt) { + case 'c': + if ((optarg == NULL) || (optarg[1] != '\0')) { + bb_show_usage(); + } + /* Valid levels are between 1 and 8 */ + console_log_level = *optarg - '1'; + if (console_log_level > 7) { + bb_show_usage(); + } + console_log_level++; + + break; + case 'n': + doFork = FALSE; + break; + default: + bb_show_usage(); + } + } + + if (doFork) { +#if defined(__uClinux__) + vfork_daemon_rexec(0, 1, argc, argv, "-n"); +#else /* __uClinux__ */ + if (daemon(0, 1) < 0) + bb_perror_msg_and_die("daemon"); +#endif /* __uClinux__ */ + } + doKlogd(console_log_level); + + return EXIT_SUCCESS; +} + +/* +Local Variables +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/sysklogd/logger.c b/busybox/sysklogd/logger.c new file mode 100644 index 000000000..16155316f --- /dev/null +++ b/busybox/sysklogd/logger.c @@ -0,0 +1,204 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logger implementation for busybox + * + * Copyright (C) 1999-2004 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 CONFIG_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) + bb_error_msg_and_die("unknown facility name: %s", save); + *s++ = '.'; + } else { + s = save; + } + lev = decode(s, prioritynames); + if (lev < 0) + bb_error_msg_and_die("unknown priority name: %s", save); + return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK)); +} + + +extern int logger_main(int argc, char **argv) +{ + int pri = LOG_USER | LOG_NOTICE; + int option = 0; + int c, i, opt; + char buf[1024], name[128]; + + /* Fill out the name string early (may be overwritten later) */ + my_getpwuid(name, geteuid(), sizeof(name)); + + /* Parse any options */ + while ((opt = getopt(argc, argv, "p:st:")) > 0) { + switch (opt) { + case 's': + option |= LOG_PERROR; + break; + case 'p': + pri = pencode(optarg); + break; + case 't': + safe_strncpy(name, optarg, sizeof(name)); + break; + default: + bb_show_usage(); + } + } + + openlog(name, option, (pri | LOG_FACMASK)); + if (optind == argc) { + do { + /* read from stdin */ + i = 0; + while ((c = getc(stdin)) != EOF && c != '\n' && + i < (sizeof(buf)-1)) { + buf[i++] = c; + } + if (i > 0) { + buf[i++] = '\0'; + syslog(pri, "%s", buf); + } + } while (c != EOF); + } else { + char *message = NULL; + int len = argc - optind; /* for the space between the args + and '\0' */ + opt = len; + argv += optind; + for (i = 0; i < opt; i++) { + len += strlen(*argv); + message = xrealloc(message, len); + if(!i) + message[0] = 0; + else + strcat(message, " "); + strcat(message, *argv); + argv++; + } + syslog(pri, "%s", message); + } + + closelog(); + return EXIT_SUCCESS; +} + + +/*- + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * This is the original license statement for the decode and pencode functions. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * 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..70d1db631 --- /dev/null +++ b/busybox/sysklogd/logread.c @@ -0,0 +1,186 @@ +/* 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 +#include "busybox.h" + +static const long KEY_ID = 0x414e4547; /*"GENA"*/ + +static struct shbuf_ds { + int size; // size of data written + int head; // start of message list + int tail; // end of message list + char data[1]; // data/messages +} *buf = NULL; // shared memory pointer + + +// Semaphore operation structures +static struct sembuf SMrup[1] = {{0, -1, IPC_NOWAIT | SEM_UNDO}}; // set SMrup +static struct sembuf SMrdn[2] = {{1, 0}, {0, +1, SEM_UNDO}}; // set SMrdn + +static int log_shmid = -1; // ipc shared memory id +static int log_semid = -1; // ipc semaphore id +static jmp_buf jmp_env; + +static void error_exit(const char *str); +static void interrupted(int sig); + +/* + * sem_up - up()'s a semaphore. + */ +static inline void sem_up(int semid) +{ + if ( semop(semid, SMrup, 1) == -1 ) + error_exit("semop[SMrup]"); +} + +/* + * sem_down - down()'s a semaphore + */ +static inline void sem_down(int semid) +{ + if ( semop(semid, SMrdn, 2) == -1 ) + error_exit("semop[SMrdn]"); +} + +extern int logread_main(int argc, char **argv) +{ + int i; + int follow=0; + + if (argc == 2 && strcmp(argv[1],"-f")==0) { + follow = 1; + } else { + /* no options, no getopt */ + if (argc > 1) + bb_show_usage(); + } + + // handle intrrupt signal + if (setjmp(jmp_env)) goto output_end; + + // attempt to redefine ^C signal + signal(SIGINT, interrupted); + + if ( (log_shmid = shmget(KEY_ID, 0, 0)) == -1) + error_exit("Can't find circular buffer"); + + // Attach shared memory to our char* + if ( (buf = shmat(log_shmid, NULL, SHM_RDONLY)) == NULL) + error_exit("Can't get access to circular buffer from syslogd"); + + if ( (log_semid = semget(KEY_ID, 0, 0)) == -1) + error_exit("Can't get access to semaphone(s) for circular buffer from syslogd"); + + // Suppose atomic memory move + i = follow ? buf->tail : buf->head; + + do { +#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING + char *buf_data; + int log_len,j; +#endif + + sem_down(log_semid); + + //printf("head: %i tail: %i size: %i\n",buf->head,buf->tail,buf->size); + if (buf->head == buf->tail || i==buf->tail) { + if (follow) { + sem_up(log_semid); + sleep(1); /* TODO: replace me with a sleep_on */ + continue; + } else { + printf("\n"); + } + } + + // Read Memory +#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING + log_len = buf->tail - i; + if (log_len < 0) + log_len += buf->size; + buf_data = (char *)malloc(log_len); + if (!buf_data) + error_exit("malloc failed"); + + if (buf->tail < i) { + memcpy(buf_data, buf->data+i, buf->size-i); + memcpy(buf_data+buf->size-i, buf->data, buf->tail); + } else { + memcpy(buf_data, buf->data+i, buf->tail-i); + } + i = buf->tail; + +#else + while ( i != buf->tail) { + printf("%s", buf->data+i); + i+= strlen(buf->data+i) + 1; + if (i >= buf->size ) + i=0; + } +#endif + // release the lock on the log chain + sem_up(log_semid); + +#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING + for (j=0; j < log_len; j+=strlen(buf_data+j)+1) { + printf("%s", buf_data+j); + if (follow) + fflush(stdout); + } + free(buf_data); +#endif + } while (follow); + +output_end: + if (log_shmid != -1) + shmdt(buf); + + return EXIT_SUCCESS; +} + +static void interrupted(int sig){ + signal(SIGINT, SIG_IGN); + longjmp(jmp_env, 1); +} + +static void error_exit(const char *str){ + perror(str); + //release all acquired resources + if (log_shmid != -1) + shmdt(buf); + + exit(1); +} diff --git a/busybox/sysklogd/syslogd.c b/busybox/sysklogd/syslogd.c new file mode 100644 index 000000000..8c6c44ee0 --- /dev/null +++ b/busybox/sysklogd/syslogd.c @@ -0,0 +1,712 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini syslogd implementation for busybox + * + * Copyright (C) 1999-2004 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 +#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[MAXPATHLEN]; + +static const char *logFilePath = __LOG_FILE; + +#ifdef CONFIG_FEATURE_ROTATE_LOGFILE +/* max size of message file before being rotated */ +static int logFileSize = 200 * 1024; + +/* number of rotated message files */ +static int logFileRotate = 1; +#endif + +/* interval between marks in seconds */ +static int MarkInterval = 20 * 60; + +/* localhost's name */ +static char LocalHostName[64]; + +#ifdef CONFIG_FEATURE_REMOTE_LOG +#include +/* udp socket for logging to remote host */ +static int remotefd = -1; +static struct sockaddr_in remoteaddr; + +/* where do we log? */ +static char *RemoteHost; + +/* what port to log to? */ +static int RemotePort = 514; + +/* To remote log or not to remote log, that is the question. */ +static int doRemoteLog = FALSE; +static int local_logging = FALSE; +#endif + +/* Make loging output smaller. */ +static bool small = false; + + +#define MAXLINE 1024 /* maximum line length */ + + +/* circular buffer variables/structures */ +#ifdef CONFIG_FEATURE_IPC_SYSLOG + +#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4 +#error Sorry, you must set the syslogd buffer size to at least 4KB. +#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE +#endif + +#include +#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 +static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size +static int circular_logging = FALSE; + +/* + * sem_up - up()'s a semaphore. + */ +static inline void sem_up(int semid) +{ + if (semop(semid, SMwup, 1) == -1) { + bb_perror_msg_and_die("semop[SMwup]"); + } +} + +/* + * sem_down - down()'s a semaphore + */ +static inline void sem_down(int semid) +{ + if (semop(semid, SMwdn, 3) == -1) { + bb_perror_msg_and_die("semop[SMwdn]"); + } +} + + +void ipcsyslog_cleanup(void) +{ + printf("Exiting Syslogd!\n"); + if (shmid != -1) { + shmdt(buf); + } + + if (shmid != -1) { + shmctl(shmid, IPC_RMID, NULL); + } + if (s_semid != -1) { + semctl(s_semid, 0, IPC_RMID, 0); + } +} + +void ipcsyslog_init(void) +{ + if (buf == NULL) { + if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) { + bb_perror_msg_and_die("shmget"); + } + + if ((buf = shmat(shmid, NULL, 0)) == NULL) { + bb_perror_msg_and_die("shmat"); + } + + buf->size = shm_size - sizeof(*buf); + buf->head = buf->tail = 0; + + // we'll trust the OS to set initial semval to 0 (let's hope) + if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) { + if (errno == EEXIST) { + if ((s_semid = semget(KEY_ID, 2, 0)) == -1) { + bb_perror_msg_and_die("semget"); + } + } else { + bb_perror_msg_and_die("semget"); + } + } + } else { + printf("Buffer already allocated just grab the semaphore?"); + } +} + +/* write message to buffer */ +void circ_message(const char *msg) +{ + int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */ + + sem_down(s_semid); + + /* + * Circular Buffer Algorithm: + * -------------------------- + * + * Start-off w/ empty buffer of specific size SHM_SIZ + * Start filling it up w/ messages. I use '\0' as separator to break up messages. + * This is also very handy since we can do printf on message. + * + * Once the buffer is full we need to get rid of the first message in buffer and + * insert the new message. (Note: if the message being added is >1 message then + * we will need to "remove" >1 old message from the buffer). The way this is done + * is the following: + * When we reach the end of the buffer we set a mark and start from the beginning. + * Now what about the beginning and end of the buffer? Well we have the "head" + * index/pointer which is the starting point for the messages and we have "tail" + * index/pointer which is the ending point for the messages. When we "display" the + * messages we start from the beginning and continue until we reach "tail". If we + * reach end of buffer, then we just start from the beginning (offset 0). "head" and + * "tail" are actually offsets from the beginning of the buffer. + * + * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide + * a threasafe way of handling shared memory operations. + */ + if ((buf->tail + l) < buf->size) { + /* before we append the message we need to check the HEAD so that we won't + overwrite any of the message that we still need and adjust HEAD to point + to the next message! */ + if (buf->tail < buf->head) { + if ((buf->tail + l) >= buf->head) { + /* we need to move the HEAD to point to the next message + * Theoretically we have enough room to add the whole message to the + * buffer, because of the first outer IF statement, so we don't have + * to worry about overflows here! + */ + int k = buf->tail + l - buf->head; /* we need to know how many bytes + we are overwriting to make + enough room */ + char *c = + memchr(buf->data + buf->head + k, '\0', + buf->size - (buf->head + k)); + if (c != NULL) { /* do a sanity check just in case! */ + buf->head = c - buf->data + 1; /* we need to convert pointer to + offset + skip the '\0' since + we need to point to the beginning + of the next message */ + /* Note: HEAD is only used to "retrieve" messages, it's not used + when writing messages into our buffer */ + } else { /* show an error message to know we messed up? */ + printf("Weird! Can't find the terminator token??? \n"); + buf->head = 0; + } + } + } + + /* in other cases no overflows have been done yet, so we don't care! */ + /* we should be ok to append the message now */ + strncpy(buf->data + buf->tail, msg, l); /* append our message */ + buf->tail += l; /* count full message w/ '\0' terminating char */ + } else { + /* we need to break up the message and "circle" it around */ + char *c; + int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */ + + /* We need to move HEAD! This is always the case since we are going + * to "circle" the message. + */ + c = memchr(buf->data + k, '\0', buf->size - k); + + if (c != NULL) { /* if we don't have '\0'??? weird!!! */ + /* move head pointer */ + buf->head = c - buf->data + 1; + + /* now write the first part of the message */ + strncpy(buf->data + buf->tail, msg, l - k - 1); + + /* ALWAYS terminate end of buffer w/ '\0' */ + buf->data[buf->size - 1] = '\0'; + + /* now write out the rest of the string to the beginning of the buffer */ + strcpy(buf->data, &msg[l - k - 1]); + + /* we need to place the TAIL at the end of the message */ + buf->tail = k + 1; + } else { + printf + ("Weird! Can't find the terminator token from the beginning??? \n"); + buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */ + } + + } + sem_up(s_semid); +} +#endif /* CONFIG_FEATURE_IPC_SYSLOG */ + +/* Note: There is also a function called "message()" in init.c */ +/* Print a message to the log file. */ +static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2))); +static void message(char *fmt, ...) +{ + int fd; + struct flock fl; + va_list arguments; + + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 1; + +#ifdef CONFIG_FEATURE_IPC_SYSLOG + if ((circular_logging == TRUE) && (buf != NULL)) { + char b[1024]; + + va_start(arguments, fmt); + vsnprintf(b, sizeof(b) - 1, fmt, arguments); + va_end(arguments); + circ_message(b); + + } else +#endif + if ((fd = + device_open(logFilePath, + O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND | + O_NONBLOCK)) >= 0) { + fl.l_type = F_WRLCK; + fcntl(fd, F_SETLKW, &fl); +#ifdef CONFIG_FEATURE_ROTATE_LOGFILE + if ( logFileSize > 0 ) { + struct stat statf; + int r = fstat(fd, &statf); + if( !r && (statf.st_mode & S_IFREG) + && (lseek(fd,0,SEEK_END) > logFileSize) ) { + if(logFileRotate > 0) { + int i; + char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)]; + for(i=logFileRotate-1;i>0;i--) { + sprintf(oldFile, "%s.%d", logFilePath, i-1); + sprintf(newFile, "%s.%d", logFilePath, i); + rename(oldFile, newFile); + } + sprintf(newFile, "%s.%d", logFilePath, 0); + fl.l_type = F_UNLCK; + fcntl (fd, F_SETLKW, &fl); + close(fd); + rename(logFilePath, newFile); + fd = device_open (logFilePath, + O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND | + O_NONBLOCK); + fl.l_type = F_WRLCK; + fcntl (fd, F_SETLKW, &fl); + } else { + ftruncate( fd, 0 ); + } + } + } +#endif + va_start(arguments, fmt); + vdprintf(fd, fmt, arguments); + va_end(arguments); + fl.l_type = F_UNLCK; + fcntl(fd, F_SETLKW, &fl); + close(fd); + } else { + /* Always send console messages to /dev/console so people will see them. */ + if ((fd = + device_open(_PATH_CONSOLE, + O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) { + va_start(arguments, fmt); + vdprintf(fd, fmt, arguments); + va_end(arguments); + close(fd); + } else { + fprintf(stderr, "Bummer, can't print: "); + va_start(arguments, fmt); + vfprintf(stderr, fmt, arguments); + fflush(stderr); + va_end(arguments); + } + } +} + +#ifdef CONFIG_FEATURE_REMOTE_LOG +static void init_RemoteLog(void) +{ + memset(&remoteaddr, 0, sizeof(remoteaddr)); + remotefd = socket(AF_INET, SOCK_DGRAM, 0); + + if (remotefd < 0) { + bb_error_msg("cannot create socket"); + } + + remoteaddr.sin_family = AF_INET; + remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list; + remoteaddr.sin_port = htons(RemotePort); +} +#endif + +static void logMessage(int pri, char *msg) +{ + time_t now; + char *timestamp; + static char res[20] = ""; +#ifdef CONFIG_FEATURE_REMOTE_LOG + static char line[MAXLINE + 1]; +#endif + CODE *c_pri, *c_fac; + + if (pri != 0) { + for (c_fac = facilitynames; + c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++); + for (c_pri = prioritynames; + c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++); + if (c_fac->c_name == NULL || c_pri->c_name == NULL) { + snprintf(res, sizeof(res), "<%d>", pri); + } else { + snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name); + } + } + + if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' || + msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') { + time(&now); + timestamp = ctime(&now) + 4; + timestamp[15] = '\0'; + } else { + timestamp = msg; + timestamp[15] = '\0'; + msg += 16; + } + + /* todo: supress duplicates */ + +#ifdef CONFIG_FEATURE_REMOTE_LOG + if (doRemoteLog == TRUE) { + /* trying connect the socket */ + if (-1 == remotefd) { + init_RemoteLog(); + } + + /* if we have a valid socket, send the message */ + if (-1 != remotefd) { + now = 1; + snprintf(line, sizeof(line), "<%d> %s", pri, msg); + + retry: + /* send message to remote logger */ + if(( -1 == sendto(remotefd, line, strlen(line), 0, + (struct sockaddr *) &remoteaddr, + sizeof(remoteaddr))) && (errno == EINTR)) { + /* sleep now seconds and retry (with now * 2) */ + sleep(now); + now *= 2; + goto retry; + } + } + } + + if (local_logging == TRUE) +#endif + { + /* now spew out the message to wherever it is supposed to go */ + if (small) + message("%s %s\n", timestamp, msg); + else + message("%s %s %s %s\n", timestamp, LocalHostName, res, msg); + } +} + +static void quit_signal(int sig) +{ + logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting."); + unlink(lfile); +#ifdef CONFIG_FEATURE_IPC_SYSLOG + ipcsyslog_cleanup(); +#endif + + exit(TRUE); +} + +static void domark(int sig) +{ + if (MarkInterval > 0) { + logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --"); + alarm(MarkInterval); + } +} + +/* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are + * enabled, we otherwise get a "storage size isn't constant error. */ +static int serveConnection(char *tmpbuf, int n_read) +{ + char *p = tmpbuf; + + while (p < tmpbuf + n_read) { + + int pri = (LOG_USER | LOG_NOTICE); + int num_lt = 0; + char line[MAXLINE + 1]; + unsigned char c; + char *q = line; + + while ((c = *p) && q < &line[sizeof(line) - 1]) { + if (c == '<' && num_lt == 0) { + /* Parse the magic priority number. */ + num_lt++; + pri = 0; + while (isdigit(*(++p))) { + pri = 10 * pri + (*p - '0'); + } + if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) { + pri = (LOG_USER | LOG_NOTICE); + } + } else if (c == '\n') { + *q++ = ' '; + } else if (iscntrl(c) && (c < 0177)) { + *q++ = '^'; + *q++ = c ^ 0100; + } else { + *q++ = c; + } + p++; + } + *q = '\0'; + p++; + /* Now log it */ + logMessage(pri, line); + } + return n_read; +} + +static void doSyslogd(void) __attribute__ ((noreturn)); +static void doSyslogd(void) +{ + struct sockaddr_un sunx; + socklen_t addrLength; + + int sock_fd; + fd_set fds; + + /* Set up signal handlers. */ + signal(SIGINT, quit_signal); + signal(SIGTERM, quit_signal); + signal(SIGQUIT, quit_signal); + signal(SIGHUP, SIG_IGN); + signal(SIGCHLD, SIG_IGN); +#ifdef SIGCLD + signal(SIGCLD, SIG_IGN); +#endif + signal(SIGALRM, domark); + alarm(MarkInterval); + + /* Create the syslog file so realpath() can work. */ + if (realpath(_PATH_LOG, lfile) != NULL) { + unlink(lfile); + } + + memset(&sunx, 0, sizeof(sunx)); + sunx.sun_family = AF_UNIX; + strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path)); + if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { + bb_perror_msg_and_die("Couldn't get file descriptor for socket " + _PATH_LOG); + } + + addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path); + if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) { + bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG); + } + + if (chmod(lfile, 0666) < 0) { + bb_perror_msg_and_die("Could not set permission on " _PATH_LOG); + } +#ifdef CONFIG_FEATURE_IPC_SYSLOG + if (circular_logging == TRUE) { + ipcsyslog_init(); + } +#endif + +#ifdef CONFIG_FEATURE_REMOTE_LOG + if (doRemoteLog == TRUE) { + init_RemoteLog(); + } +#endif + + logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER); + + for (;;) { + + FD_ZERO(&fds); + FD_SET(sock_fd, &fds); + + if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) { + /* alarm may have happened. */ + continue; + } + bb_perror_msg_and_die("select error"); + } + + if (FD_ISSET(sock_fd, &fds)) { + int i; + + RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1); + + memset(tmpbuf, '\0', MAXLINE + 1); + if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) { + serveConnection(tmpbuf, i); + } else { + bb_perror_msg_and_die("UNIX socket error"); + } + RELEASE_CONFIG_BUFFER(tmpbuf); + } /* FD_ISSET() */ + } /* for main loop */ +} + +extern int syslogd_main(int argc, char **argv) +{ + int opt; + + int doFork = TRUE; + + char *p; + + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) { + switch (opt) { + case 'm': + MarkInterval = atoi(optarg) * 60; + break; + case 'n': + doFork = FALSE; + break; + case 'O': + logFilePath = optarg; + break; +#ifdef CONFIG_FEATURE_ROTATE_LOGFILE + case 's': + logFileSize = atoi(optarg) * 1024; + break; + case 'b': + logFileRotate = atoi(optarg); + if( logFileRotate > 99 ) logFileRotate = 99; + break; +#endif +#ifdef CONFIG_FEATURE_REMOTE_LOG + case 'R': + RemoteHost = bb_xstrdup(optarg); + if ((p = strchr(RemoteHost, ':'))) { + RemotePort = atoi(p + 1); + *p = '\0'; + } + doRemoteLog = TRUE; + break; + case 'L': + local_logging = TRUE; + break; +#endif +#ifdef CONFIG_FEATURE_IPC_SYSLOG + case 'C': + if (optarg) { + int buf_size = atoi(optarg); + if (buf_size >= 4) { + shm_size = buf_size * 1024; + } + } + circular_logging = TRUE; + break; +#endif + case 'S': + small = true; + break; + default: + bb_show_usage(); + } + } + +#ifdef CONFIG_FEATURE_REMOTE_LOG + /* If they have not specified remote logging, then log locally */ + if (doRemoteLog == FALSE) + local_logging = TRUE; +#endif + + + /* Store away localhost's name before the fork */ + gethostname(LocalHostName, sizeof(LocalHostName)); + if ((p = strchr(LocalHostName, '.'))) { + *p = '\0'; + } + + umask(0); + + if (doFork == TRUE) { +#if defined(__uClinux__) + vfork_daemon_rexec(0, 1, argc, argv, "-n"); +#else /* __uClinux__ */ + if(daemon(0, 1) < 0) + bb_perror_msg_and_die("daemon"); +#endif /* __uClinux__ */ + } + doSyslogd(); + + return EXIT_SUCCESS; +} + +/* +Local Variables +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/testsuite/README b/busybox/testsuite/README new file mode 100644 index 000000000..40439bfe8 --- /dev/null +++ b/busybox/testsuite/README @@ -0,0 +1,31 @@ +To run the test suite, change to this directory and run "./runtest". It will +run all of the test cases, and list those with unexpected outcomes. Adding the +-v option will cause it to show expected outcomes as well. To only run the test +cases for particular applets, specify them as parameters to runtest. + +The test cases for an applet reside in the subdirectory of the applet name. The +name of the test case should be the assertion that is tested. The test case +should be a shell fragment that returns successfully if the test case passes, +and unsuccessfully otherwise. + +If the test case relies on a certain feature, it should include the string +"FEATURE: " followed by the name of the feature in a comment. If it is always +expected to fail, it should include the string "XFAIL" in a comment. + +For the entire testsuite, the copyright is as follows: + +Copyright (C) 2001, 2002 Matt Kraai + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. diff --git a/busybox/testsuite/TODO b/busybox/testsuite/TODO new file mode 100644 index 000000000..ced571595 --- /dev/null +++ b/busybox/testsuite/TODO @@ -0,0 +1,18 @@ +This testsuite is quite obviously a work in progress. As such, +there are a number of good extensions. If you are looking for +something to do, feel free to tackle one or more of the following: + +Buildroot support + Erik has put together a handy package for constructing and + testing busybox called buildroot. Integrating this into + the testsuite would allow for greater test coverage (e.g., + init, mount, and other privileged applications). + +libbb unit testing + Being able to test the functions of libbb individually may + help to prevent regressions. + +Standardization + This testsuite is totally bastardized. It would be better + to use an existing test framework, such as dejagnu, greg, + or a XUnit clone (shunit?). diff --git a/busybox/testsuite/basename/basename-does-not-remove-identical-extension b/busybox/testsuite/basename/basename-does-not-remove-identical-extension new file mode 100644 index 000000000..4448fdec4 --- /dev/null +++ b/busybox/testsuite/basename/basename-does-not-remove-identical-extension @@ -0,0 +1 @@ +test xfoo = x`busybox basename foo foo` diff --git a/busybox/testsuite/basename/basename-works b/busybox/testsuite/basename/basename-works new file mode 100644 index 000000000..38907d4c1 --- /dev/null +++ b/busybox/testsuite/basename/basename-works @@ -0,0 +1,2 @@ +test x$(basename $(pwd)) = x$(busybox basename $(pwd)) + diff --git a/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input b/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input new file mode 100644 index 000000000..e212a1207 --- /dev/null +++ b/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input @@ -0,0 +1,2 @@ +echo foo | bzip2 | busybox bunzip2 > output +echo foo | cmp - output diff --git a/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file b/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file new file mode 100644 index 000000000..f1d15503e --- /dev/null +++ b/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file @@ -0,0 +1,3 @@ +echo foo | bzip2 >foo.bz2 +busybox bunzip2 foo.bz2 +test ! -f foo.bz2 diff --git a/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file b/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file new file mode 100644 index 000000000..7d4016ec5 --- /dev/null +++ b/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file @@ -0,0 +1,3 @@ +echo foo | bzip2 >foo.bz2 +busybox bzcat foo.bz2 +test -f foo.bz2 diff --git a/busybox/testsuite/cat/cat-prints-a-file b/busybox/testsuite/cat/cat-prints-a-file new file mode 100644 index 000000000..e3f35a86e --- /dev/null +++ b/busybox/testsuite/cat/cat-prints-a-file @@ -0,0 +1,3 @@ +echo I WANT > foo +busybox cat foo >bar +cmp foo bar diff --git a/busybox/testsuite/cat/cat-prints-a-file-and-standard-input b/busybox/testsuite/cat/cat-prints-a-file-and-standard-input new file mode 100644 index 000000000..bc9231882 --- /dev/null +++ b/busybox/testsuite/cat/cat-prints-a-file-and-standard-input @@ -0,0 +1,7 @@ +echo I WANT > foo +echo SOMETHING | busybox cat foo - >bar +cat >baz <foo +echo bar >bar +set +e +busybox cmp -s foo bar +if [ $? != 0 ] ; then + exit 0; +fi + +exit 1; diff --git a/busybox/testsuite/cp/cp-a-files-to-dir b/busybox/testsuite/cp/cp-a-files-to-dir new file mode 100644 index 000000000..39f8f8103 --- /dev/null +++ b/busybox/testsuite/cp/cp-a-files-to-dir @@ -0,0 +1,14 @@ +echo file number one > file1 +echo file number two > file2 +ln -s file2 link1 +mkdir dir1 +touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3 +mkdir there +busybox cp -a file1 file2 link1 dir1 there +test -f there/file1 +test -f there/file2 +test ! -s there/dir1/file3 +test -L there/link1 +test xfile2 = x`readlink there/link1` +test ! dir1/file3 -ot there/dir1/file3 +test ! dir1/file3 -nt there/dir1/file3 diff --git a/busybox/testsuite/cp/cp-a-preserves-links b/busybox/testsuite/cp/cp-a-preserves-links new file mode 100644 index 000000000..0c0cd9653 --- /dev/null +++ b/busybox/testsuite/cp/cp-a-preserves-links @@ -0,0 +1,5 @@ +touch foo +ln -s foo bar +busybox cp -a bar baz +test -L baz +test xfoo = x`readlink baz` diff --git a/busybox/testsuite/cp/cp-copies-empty-file b/busybox/testsuite/cp/cp-copies-empty-file new file mode 100644 index 000000000..ad25aa12e --- /dev/null +++ b/busybox/testsuite/cp/cp-copies-empty-file @@ -0,0 +1,3 @@ +touch foo +busybox cp foo bar +cmp foo bar diff --git a/busybox/testsuite/cp/cp-copies-large-file b/busybox/testsuite/cp/cp-copies-large-file new file mode 100644 index 000000000..c2225c6d8 --- /dev/null +++ b/busybox/testsuite/cp/cp-copies-large-file @@ -0,0 +1,3 @@ +dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null +busybox cp foo bar +cmp foo bar diff --git a/busybox/testsuite/cp/cp-copies-small-file b/busybox/testsuite/cp/cp-copies-small-file new file mode 100644 index 000000000..d52a887c0 --- /dev/null +++ b/busybox/testsuite/cp/cp-copies-small-file @@ -0,0 +1,3 @@ +echo I WANT > foo +busybox cp foo bar +cmp foo bar diff --git a/busybox/testsuite/cp/cp-d-files-to-dir b/busybox/testsuite/cp/cp-d-files-to-dir new file mode 100644 index 000000000..9571a567e --- /dev/null +++ b/busybox/testsuite/cp/cp-d-files-to-dir @@ -0,0 +1,11 @@ +echo file number one > file1 +echo file number two > file2 +touch file3 +ln -s file2 link1 +mkdir there +busybox cp -d file1 file2 file3 link1 there +test -f there/file1 +test -f there/file2 +test ! -s there/file3 +test -L there/link1 +test xfile2 = x`readlink there/link1` diff --git a/busybox/testsuite/cp/cp-dir-create-dir b/busybox/testsuite/cp/cp-dir-create-dir new file mode 100644 index 000000000..2c89af67e --- /dev/null +++ b/busybox/testsuite/cp/cp-dir-create-dir @@ -0,0 +1,4 @@ +mkdir bar +touch bar/baz +busybox cp -R bar foo +test -f foo/baz diff --git a/busybox/testsuite/cp/cp-dir-existing-dir b/busybox/testsuite/cp/cp-dir-existing-dir new file mode 100644 index 000000000..5ba3f8e33 --- /dev/null +++ b/busybox/testsuite/cp/cp-dir-existing-dir @@ -0,0 +1,5 @@ +mkdir bar +touch bar/baz +mkdir foo +busybox cp -R bar foo +test -f foo/bar/baz diff --git a/busybox/testsuite/cp/cp-does-not-copy-unreadable-file b/busybox/testsuite/cp/cp-does-not-copy-unreadable-file new file mode 100644 index 000000000..ce11bfab0 --- /dev/null +++ b/busybox/testsuite/cp/cp-does-not-copy-unreadable-file @@ -0,0 +1,6 @@ +touch foo +chmod a-r foo +set +e +busybox cp foo bar +set -e +test ! -f bar diff --git a/busybox/testsuite/cp/cp-files-to-dir b/busybox/testsuite/cp/cp-files-to-dir new file mode 100644 index 000000000..fdb81916f --- /dev/null +++ b/busybox/testsuite/cp/cp-files-to-dir @@ -0,0 +1,11 @@ +echo file number one > file1 +echo file number two > file2 +touch file3 +ln -s file2 link1 +mkdir there +busybox cp file1 file2 file3 link1 there +test -f there/file1 +test -f there/file2 +test ! -s there/file3 +test -f there/link1 +cmp there/file2 there/link1 diff --git a/busybox/testsuite/cp/cp-follows-links b/busybox/testsuite/cp/cp-follows-links new file mode 100644 index 000000000..2d9f05e9f --- /dev/null +++ b/busybox/testsuite/cp/cp-follows-links @@ -0,0 +1,4 @@ +touch foo +ln -s foo bar +busybox cp bar baz +test -f baz diff --git a/busybox/testsuite/cp/cp-preserves-hard-links b/busybox/testsuite/cp/cp-preserves-hard-links new file mode 100644 index 000000000..4de7b85db --- /dev/null +++ b/busybox/testsuite/cp/cp-preserves-hard-links @@ -0,0 +1,6 @@ +# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS +touch foo +ln foo bar +mkdir baz +busybox cp -d foo bar baz +test baz/foo -ef baz/bar diff --git a/busybox/testsuite/cp/cp-preserves-links b/busybox/testsuite/cp/cp-preserves-links new file mode 100644 index 000000000..301dc5fd8 --- /dev/null +++ b/busybox/testsuite/cp/cp-preserves-links @@ -0,0 +1,5 @@ +touch foo +ln -s foo bar +busybox cp -d bar baz +test -L baz +test xfoo = x`readlink baz` diff --git a/busybox/testsuite/cp/cp-preserves-source-file b/busybox/testsuite/cp/cp-preserves-source-file new file mode 100644 index 000000000..f0f5065f4 --- /dev/null +++ b/busybox/testsuite/cp/cp-preserves-source-file @@ -0,0 +1,3 @@ +touch foo +busybox cp foo bar +test -f foo diff --git a/busybox/testsuite/cut/cut-cuts-a-character b/busybox/testsuite/cut/cut-cuts-a-character new file mode 100644 index 000000000..d6c5efa3a --- /dev/null +++ b/busybox/testsuite/cut/cut-cuts-a-character @@ -0,0 +1 @@ +test $(echo abcd | busybox cut -c 3) = c diff --git a/busybox/testsuite/cut/cut-cuts-a-closed-range b/busybox/testsuite/cut/cut-cuts-a-closed-range new file mode 100644 index 000000000..9680b7650 --- /dev/null +++ b/busybox/testsuite/cut/cut-cuts-a-closed-range @@ -0,0 +1 @@ +test $(echo abcd | busybox cut -c 1-2) = ab diff --git a/busybox/testsuite/cut/cut-cuts-a-field b/busybox/testsuite/cut/cut-cuts-a-field new file mode 100644 index 000000000..4c7f44007 --- /dev/null +++ b/busybox/testsuite/cut/cut-cuts-a-field @@ -0,0 +1 @@ +test $(echo -e "f1\tf2\tf3" | busybox cut -f 2) = f2 diff --git a/busybox/testsuite/cut/cut-cuts-an-open-range b/busybox/testsuite/cut/cut-cuts-an-open-range new file mode 100644 index 000000000..1fbf27742 --- /dev/null +++ b/busybox/testsuite/cut/cut-cuts-an-open-range @@ -0,0 +1 @@ +test $(echo abcd | busybox cut -c -3) = abc diff --git a/busybox/testsuite/cut/cut-cuts-an-unclosed-range b/busybox/testsuite/cut/cut-cuts-an-unclosed-range new file mode 100644 index 000000000..a2b0cdb03 --- /dev/null +++ b/busybox/testsuite/cut/cut-cuts-an-unclosed-range @@ -0,0 +1 @@ +test $(echo abcd | busybox cut -c 3-) = cd diff --git a/busybox/testsuite/date/date-R-works b/busybox/testsuite/date/date-R-works new file mode 100644 index 000000000..ec3a06751 --- /dev/null +++ b/busybox/testsuite/date/date-R-works @@ -0,0 +1,2 @@ +test x"`date -R`" = x"`busybox date -R`" + diff --git a/busybox/testsuite/date/date-format-works b/busybox/testsuite/date/date-format-works new file mode 100644 index 000000000..f28d06cfc --- /dev/null +++ b/busybox/testsuite/date/date-format-works @@ -0,0 +1 @@ +test x"`date +%d/%m/%y`" = x"`busybox date +%d/%m/%y`" diff --git a/busybox/testsuite/date/date-u-works b/busybox/testsuite/date/date-u-works new file mode 100644 index 000000000..7d9902a3f --- /dev/null +++ b/busybox/testsuite/date/date-u-works @@ -0,0 +1,2 @@ +test x"`date -u`" = x"`busybox date -u`" + diff --git a/busybox/testsuite/date/date-works b/busybox/testsuite/date/date-works new file mode 100644 index 000000000..2f6dd1eca --- /dev/null +++ b/busybox/testsuite/date/date-works @@ -0,0 +1,2 @@ +test x"`date`" = x"`busybox date`" + diff --git a/busybox/testsuite/dd/dd-accepts-if b/busybox/testsuite/dd/dd-accepts-if new file mode 100644 index 000000000..03d1af853 --- /dev/null +++ b/busybox/testsuite/dd/dd-accepts-if @@ -0,0 +1,2 @@ +echo I WANT >foo +test "$(busybox dd if=foo 2>/dev/null)" = "I WANT" diff --git a/busybox/testsuite/dd/dd-accepts-of b/busybox/testsuite/dd/dd-accepts-of new file mode 100644 index 000000000..84405e622 --- /dev/null +++ b/busybox/testsuite/dd/dd-accepts-of @@ -0,0 +1,2 @@ +echo I WANT | busybox dd of=foo 2>/dev/null +echo I WANT | cmp foo - diff --git a/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output b/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output new file mode 100644 index 000000000..d890eb04c --- /dev/null +++ b/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output @@ -0,0 +1 @@ +test "$(echo I WANT | busybox dd 2>/dev/null)" = "I WANT" diff --git a/busybox/testsuite/dd/dd-prints-count-to-standard-error b/busybox/testsuite/dd/dd-prints-count-to-standard-error new file mode 100644 index 000000000..2187dc027 --- /dev/null +++ b/busybox/testsuite/dd/dd-prints-count-to-standard-error @@ -0,0 +1,2 @@ +echo I WANT | busybox dd of=foo >/dev/null 2>bar +grep -q records bar diff --git a/busybox/testsuite/dirname/dirname-handles-absolute-path b/busybox/testsuite/dirname/dirname-handles-absolute-path new file mode 100644 index 000000000..ca1a51b3a --- /dev/null +++ b/busybox/testsuite/dirname/dirname-handles-absolute-path @@ -0,0 +1 @@ +test $(busybox dirname /foo/bar/baz) = /foo/bar diff --git a/busybox/testsuite/dirname/dirname-handles-empty-path b/busybox/testsuite/dirname/dirname-handles-empty-path new file mode 100644 index 000000000..04134a58f --- /dev/null +++ b/busybox/testsuite/dirname/dirname-handles-empty-path @@ -0,0 +1 @@ +test $(busybox dirname '') = . diff --git a/busybox/testsuite/dirname/dirname-handles-multiple-slashes b/busybox/testsuite/dirname/dirname-handles-multiple-slashes new file mode 100644 index 000000000..286f25361 --- /dev/null +++ b/busybox/testsuite/dirname/dirname-handles-multiple-slashes @@ -0,0 +1 @@ +test $(busybox dirname foo/bar///baz) = foo/bar diff --git a/busybox/testsuite/dirname/dirname-handles-relative-path b/busybox/testsuite/dirname/dirname-handles-relative-path new file mode 100644 index 000000000..ffe4ab459 --- /dev/null +++ b/busybox/testsuite/dirname/dirname-handles-relative-path @@ -0,0 +1 @@ +test $(busybox dirname foo/bar/baz) = foo/bar diff --git a/busybox/testsuite/dirname/dirname-handles-root b/busybox/testsuite/dirname/dirname-handles-root new file mode 100644 index 000000000..6bd62b8a1 --- /dev/null +++ b/busybox/testsuite/dirname/dirname-handles-root @@ -0,0 +1 @@ +test $(busybox dirname /) = / diff --git a/busybox/testsuite/dirname/dirname-handles-single-component b/busybox/testsuite/dirname/dirname-handles-single-component new file mode 100644 index 000000000..24f9ae163 --- /dev/null +++ b/busybox/testsuite/dirname/dirname-handles-single-component @@ -0,0 +1 @@ +test $(busybox dirname foo) = . diff --git a/busybox/testsuite/dirname/dirname-works b/busybox/testsuite/dirname/dirname-works new file mode 100644 index 000000000..f339c8f73 --- /dev/null +++ b/busybox/testsuite/dirname/dirname-works @@ -0,0 +1,2 @@ +test x$(dirname $(pwd)) = x$(busybox dirname $(pwd)) + diff --git a/busybox/testsuite/du/du-h-works b/busybox/testsuite/du/du-h-works new file mode 100644 index 000000000..82041ab33 --- /dev/null +++ b/busybox/testsuite/du/du-h-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +du -h "$d" > logfile.gnu +busybox du -h "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/du/du-k-works b/busybox/testsuite/du/du-k-works new file mode 100644 index 000000000..177a1a2cd --- /dev/null +++ b/busybox/testsuite/du/du-k-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +du -k "$d" > logfile.gnu +busybox du -k "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/du/du-l-works b/busybox/testsuite/du/du-l-works new file mode 100644 index 000000000..61e91400c --- /dev/null +++ b/busybox/testsuite/du/du-l-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +du -l "$d" > logfile.gnu +busybox du -l "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/du/du-m-works b/busybox/testsuite/du/du-m-works new file mode 100644 index 000000000..bc9707350 --- /dev/null +++ b/busybox/testsuite/du/du-m-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +du -m "$d" > logfile.gnu +busybox du -m "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/du/du-s-works b/busybox/testsuite/du/du-s-works new file mode 100644 index 000000000..f0b3bf0ae --- /dev/null +++ b/busybox/testsuite/du/du-s-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +du -s "$d" > logfile.gnu +busybox du -s "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/du/du-works b/busybox/testsuite/du/du-works new file mode 100644 index 000000000..47949c694 --- /dev/null +++ b/busybox/testsuite/du/du-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +du "$d" > logfile.gnu +busybox du "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/echo/echo-does-not-print-newline b/busybox/testsuite/echo/echo-does-not-print-newline new file mode 100644 index 000000000..2ed03caf5 --- /dev/null +++ b/busybox/testsuite/echo/echo-does-not-print-newline @@ -0,0 +1 @@ +test `busybox echo -n word | wc -c` -eq 4 diff --git a/busybox/testsuite/echo/echo-prints-argument b/busybox/testsuite/echo/echo-prints-argument new file mode 100644 index 000000000..479dac89c --- /dev/null +++ b/busybox/testsuite/echo/echo-prints-argument @@ -0,0 +1 @@ +test xfubar = x`busybox echo fubar` diff --git a/busybox/testsuite/echo/echo-prints-arguments b/busybox/testsuite/echo/echo-prints-arguments new file mode 100644 index 000000000..4e4e3b434 --- /dev/null +++ b/busybox/testsuite/echo/echo-prints-arguments @@ -0,0 +1 @@ +test "`busybox echo foo bar`" = "foo bar" diff --git a/busybox/testsuite/echo/echo-prints-newline b/busybox/testsuite/echo/echo-prints-newline new file mode 100644 index 000000000..838671efe --- /dev/null +++ b/busybox/testsuite/echo/echo-prints-newline @@ -0,0 +1 @@ +test `busybox echo word | wc -c` -eq 5 diff --git a/busybox/testsuite/expr/expr-works b/busybox/testsuite/expr/expr-works new file mode 100644 index 000000000..af49ac4d5 --- /dev/null +++ b/busybox/testsuite/expr/expr-works @@ -0,0 +1,59 @@ +# busybox expr +busybox expr 1 \| 1 +busybox expr 1 \| 0 +busybox expr 0 \| 1 +busybox expr 1 \& 1 +busybox expr 0 \< 1 +busybox expr 1 \> 0 +busybox expr 0 \<= 1 +busybox expr 1 \<= 1 +busybox expr 1 \>= 0 +busybox expr 1 \>= 1 +busybox expr 1 + 2 +busybox expr 2 - 1 +busybox expr 2 \* 3 +busybox expr 12 / 2 +busybox expr 12 % 5 + + +set +e +busybox expr 0 \| 0 +if [ $? != 1 ] ; then + exit 1; +fi; + +busybox expr 1 \& 0 +if [ $? != 1 ] ; then + exit 1; +fi; + +busybox expr 0 \& 1 +if [ $? != 1 ] ; then + exit 1; +fi; + +busybox expr 0 \& 0 +if [ $? != 1 ] ; then + exit 1; +fi; + +busybox expr 1 \< 0 +if [ $? != 1 ] ; then + exit 1; +fi; + +busybox expr 0 \> 1 +if [ $? != 1 ] ; then + exit 1; +fi; + +busybox expr 1 \<= 0 +if [ $? != 1 ] ; then + exit 1; +fi; + +busybox expr 0 \>= 1 +if [ $? != 1 ] ; then + exit 1; +fi; + diff --git a/busybox/testsuite/false/false-is-silent b/busybox/testsuite/false/false-is-silent new file mode 100644 index 000000000..8a9aa0c7f --- /dev/null +++ b/busybox/testsuite/false/false-is-silent @@ -0,0 +1 @@ +busybox false 2>&1 | cmp - /dev/null diff --git a/busybox/testsuite/false/false-returns-failure b/busybox/testsuite/false/false-returns-failure new file mode 100644 index 000000000..1a061f286 --- /dev/null +++ b/busybox/testsuite/false/false-returns-failure @@ -0,0 +1 @@ +! busybox false diff --git a/busybox/testsuite/find/find-supports-minus-xdev b/busybox/testsuite/find/find-supports-minus-xdev new file mode 100644 index 000000000..4c559a1f4 --- /dev/null +++ b/busybox/testsuite/find/find-supports-minus-xdev @@ -0,0 +1 @@ +busybox find . -xdev >/dev/null 2>&1 diff --git a/busybox/testsuite/grep/egrep-is-not-case-insensitive b/busybox/testsuite/grep/egrep-is-not-case-insensitive new file mode 100644 index 000000000..881607393 --- /dev/null +++ b/busybox/testsuite/grep/egrep-is-not-case-insensitive @@ -0,0 +1,2 @@ +# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS +test x`echo foo | busybox egrep FOO` = x diff --git a/busybox/testsuite/grep/egrep-supports-extended-regexps b/busybox/testsuite/grep/egrep-supports-extended-regexps new file mode 100644 index 000000000..6ef8b9159 --- /dev/null +++ b/busybox/testsuite/grep/egrep-supports-extended-regexps @@ -0,0 +1,2 @@ +# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS +echo foo | busybox egrep fo+ diff --git a/busybox/testsuite/grep/grep-handles-binary-files b/busybox/testsuite/grep/grep-handles-binary-files new file mode 100644 index 000000000..edb2042e7 --- /dev/null +++ b/busybox/testsuite/grep/grep-handles-binary-files @@ -0,0 +1 @@ +echo -e '\0foo' | busybox grep foo diff --git a/busybox/testsuite/grep/grep-handles-multiple-regexps b/busybox/testsuite/grep/grep-handles-multiple-regexps new file mode 100644 index 000000000..5c1b8de1f --- /dev/null +++ b/busybox/testsuite/grep/grep-handles-multiple-regexps @@ -0,0 +1 @@ +echo foo | busybox grep -e foo -e bar diff --git a/busybox/testsuite/grep/grep-is-also-egrep b/busybox/testsuite/grep/grep-is-also-egrep new file mode 100644 index 000000000..2e6977c28 --- /dev/null +++ b/busybox/testsuite/grep/grep-is-also-egrep @@ -0,0 +1,2 @@ +# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS +echo foo | busybox egrep foo diff --git a/busybox/testsuite/grep/grep-matches-NUL b/busybox/testsuite/grep/grep-matches-NUL new file mode 100644 index 000000000..082bd8700 --- /dev/null +++ b/busybox/testsuite/grep/grep-matches-NUL @@ -0,0 +1,8 @@ +set +e +echo -e '\0' | busybox grep . +if [ $? != 0 ] ; then + exit 0; +fi + +exit 1; + diff --git a/busybox/testsuite/gunzip/gunzip-reads-from-standard-input b/busybox/testsuite/gunzip/gunzip-reads-from-standard-input new file mode 100644 index 000000000..7c498c0ce --- /dev/null +++ b/busybox/testsuite/gunzip/gunzip-reads-from-standard-input @@ -0,0 +1,2 @@ +echo foo | gzip | busybox gunzip > output +echo foo | cmp - output diff --git a/busybox/testsuite/gzip/gzip-accepts-multiple-files b/busybox/testsuite/gzip/gzip-accepts-multiple-files new file mode 100644 index 000000000..8f0d9c845 --- /dev/null +++ b/busybox/testsuite/gzip/gzip-accepts-multiple-files @@ -0,0 +1,3 @@ +touch foo bar +busybox gzip foo bar +test -f foo.gz -a -f bar.gz diff --git a/busybox/testsuite/gzip/gzip-accepts-single-minus b/busybox/testsuite/gzip/gzip-accepts-single-minus new file mode 100644 index 000000000..8b51fdfed --- /dev/null +++ b/busybox/testsuite/gzip/gzip-accepts-single-minus @@ -0,0 +1 @@ +echo foo | busybox gzip - >/dev/null diff --git a/busybox/testsuite/gzip/gzip-removes-original-file b/busybox/testsuite/gzip/gzip-removes-original-file new file mode 100644 index 000000000..b9cb995bc --- /dev/null +++ b/busybox/testsuite/gzip/gzip-removes-original-file @@ -0,0 +1,3 @@ +touch foo +busybox gzip foo +test ! -f foo diff --git a/busybox/testsuite/head/head-n-works b/busybox/testsuite/head/head-n-works new file mode 100644 index 000000000..db4325556 --- /dev/null +++ b/busybox/testsuite/head/head-n-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +head -n 2 "$d/README" > logfile.gnu +busybox head -n 2 "$d/README" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/head/head-works b/busybox/testsuite/head/head-works new file mode 100644 index 000000000..56ad3e36b --- /dev/null +++ b/busybox/testsuite/head/head-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +head "$d/README" > logfile.gnu +busybox head "$d/README" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/hostid/hostid-works b/busybox/testsuite/hostid/hostid-works new file mode 100644 index 000000000..e85698e66 --- /dev/null +++ b/busybox/testsuite/hostid/hostid-works @@ -0,0 +1,2 @@ +test x$(hostid) = x$(busybox hostid) + diff --git a/busybox/testsuite/hostname/hostname-d-works b/busybox/testsuite/hostname/hostname-d-works new file mode 100644 index 000000000..a9aeb92cb --- /dev/null +++ b/busybox/testsuite/hostname/hostname-d-works @@ -0,0 +1,2 @@ +test x$(hostname -d) = x$(busybox hostname -d) + diff --git a/busybox/testsuite/hostname/hostname-i-works b/busybox/testsuite/hostname/hostname-i-works new file mode 100644 index 000000000..68a3e6789 --- /dev/null +++ b/busybox/testsuite/hostname/hostname-i-works @@ -0,0 +1,2 @@ +test x$(hostname -i) = x$(busybox hostname -i) + diff --git a/busybox/testsuite/hostname/hostname-s-works b/busybox/testsuite/hostname/hostname-s-works new file mode 100644 index 000000000..172b94409 --- /dev/null +++ b/busybox/testsuite/hostname/hostname-s-works @@ -0,0 +1 @@ +test x$(hostname -s) = x$(busybox hostname -s) diff --git a/busybox/testsuite/hostname/hostname-works b/busybox/testsuite/hostname/hostname-works new file mode 100644 index 000000000..f51a406ea --- /dev/null +++ b/busybox/testsuite/hostname/hostname-works @@ -0,0 +1 @@ +test x$(hostname) = x$(busybox hostname) diff --git a/busybox/testsuite/id/id-g-works b/busybox/testsuite/id/id-g-works new file mode 100644 index 000000000..671fc5361 --- /dev/null +++ b/busybox/testsuite/id/id-g-works @@ -0,0 +1 @@ +test x$(id -g) = x$(busybox id -g) diff --git a/busybox/testsuite/id/id-u-works b/busybox/testsuite/id/id-u-works new file mode 100644 index 000000000..2358cb0d7 --- /dev/null +++ b/busybox/testsuite/id/id-u-works @@ -0,0 +1 @@ +test x$(id -u) = x$(busybox id -u) diff --git a/busybox/testsuite/id/id-un-works b/busybox/testsuite/id/id-un-works new file mode 100644 index 000000000..db390e733 --- /dev/null +++ b/busybox/testsuite/id/id-un-works @@ -0,0 +1 @@ +test x$(id -un) = x$(busybox id -un) diff --git a/busybox/testsuite/id/id-ur-works b/busybox/testsuite/id/id-ur-works new file mode 100644 index 000000000..6b0fcb038 --- /dev/null +++ b/busybox/testsuite/id/id-ur-works @@ -0,0 +1 @@ +test x$(id -ur) = x$(busybox id -ur) diff --git a/busybox/testsuite/ln/ln-creates-hard-links b/busybox/testsuite/ln/ln-creates-hard-links new file mode 100644 index 000000000..2f6e23f9a --- /dev/null +++ b/busybox/testsuite/ln/ln-creates-hard-links @@ -0,0 +1,4 @@ +echo file number one > file1 +busybox ln file1 link1 +test -f file1 +test -f link1 diff --git a/busybox/testsuite/ln/ln-creates-soft-links b/busybox/testsuite/ln/ln-creates-soft-links new file mode 100644 index 000000000..e875e4c8a --- /dev/null +++ b/busybox/testsuite/ln/ln-creates-soft-links @@ -0,0 +1,4 @@ +echo file number one > file1 +busybox ln -s file1 link1 +test -L link1 +test xfile1 = x`readlink link1` diff --git a/busybox/testsuite/ln/ln-force-creates-hard-links b/busybox/testsuite/ln/ln-force-creates-hard-links new file mode 100644 index 000000000..c96b7d6cf --- /dev/null +++ b/busybox/testsuite/ln/ln-force-creates-hard-links @@ -0,0 +1,5 @@ +echo file number one > file1 +echo file number two > link1 +busybox ln -f file1 link1 +test -f file1 +test -f link1 diff --git a/busybox/testsuite/ln/ln-force-creates-soft-links b/busybox/testsuite/ln/ln-force-creates-soft-links new file mode 100644 index 000000000..cab8d1db7 --- /dev/null +++ b/busybox/testsuite/ln/ln-force-creates-soft-links @@ -0,0 +1,5 @@ +echo file number one > file1 +echo file number two > link1 +busybox ln -f -s file1 link1 +test -L link1 +test xfile1 = x`readlink link1` diff --git a/busybox/testsuite/ln/ln-preserves-hard-links b/busybox/testsuite/ln/ln-preserves-hard-links new file mode 100644 index 000000000..47fb98961 --- /dev/null +++ b/busybox/testsuite/ln/ln-preserves-hard-links @@ -0,0 +1,8 @@ +echo file number one > file1 +echo file number two > link1 +set +e +busybox ln file1 link1 +if [ $? != 0 ] ; then + exit 0; +fi +exit 1; diff --git a/busybox/testsuite/ln/ln-preserves-soft-links b/busybox/testsuite/ln/ln-preserves-soft-links new file mode 100644 index 000000000..a8123ece3 --- /dev/null +++ b/busybox/testsuite/ln/ln-preserves-soft-links @@ -0,0 +1,9 @@ +echo file number one > file1 +echo file number two > link1 +set +e +busybox ln -s file1 link1 +if [ $? != 0 ] ; then + exit 0; +fi +exit 1; + diff --git a/busybox/testsuite/ls/ls-1-works b/busybox/testsuite/ls/ls-1-works new file mode 100644 index 000000000..8ad484fc3 --- /dev/null +++ b/busybox/testsuite/ls/ls-1-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +ls -1 "$d" > logfile.gnu +busybox ls -1 "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/ls/ls-h-works b/busybox/testsuite/ls/ls-h-works new file mode 100644 index 000000000..7331262c9 --- /dev/null +++ b/busybox/testsuite/ls/ls-h-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +ls -h "$d" > logfile.gnu +busybox ls -h "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/ls/ls-l-works b/busybox/testsuite/ls/ls-l-works new file mode 100644 index 000000000..ae5141d80 --- /dev/null +++ b/busybox/testsuite/ls/ls-l-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +ls -l "$d" > logfile.gnu +busybox ls -l "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/ls/ls-s-works b/busybox/testsuite/ls/ls-s-works new file mode 100644 index 000000000..d82f328b7 --- /dev/null +++ b/busybox/testsuite/ls/ls-s-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +ls -1s "$d" > logfile.gnu +busybox ls -1s "$d" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file b/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file new file mode 100644 index 000000000..8566a234d --- /dev/null +++ b/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file @@ -0,0 +1,3 @@ +touch foo +md5sum foo > bar +busybox md5sum -c bar diff --git a/busybox/testsuite/mkdir/mkdir-makes-a-directory b/busybox/testsuite/mkdir/mkdir-makes-a-directory new file mode 100644 index 000000000..6ca5c4d52 --- /dev/null +++ b/busybox/testsuite/mkdir/mkdir-makes-a-directory @@ -0,0 +1,2 @@ +busybox mkdir foo +test -d foo diff --git a/busybox/testsuite/mkdir/mkdir-makes-parent-directories b/busybox/testsuite/mkdir/mkdir-makes-parent-directories new file mode 100644 index 000000000..992facb46 --- /dev/null +++ b/busybox/testsuite/mkdir/mkdir-makes-parent-directories @@ -0,0 +1,2 @@ +busybox mkdir -p foo/bar +test -d foo -a -d foo/bar diff --git a/busybox/testsuite/msh/msh-supports-underscores-in-variable-names b/busybox/testsuite/msh/msh-supports-underscores-in-variable-names new file mode 100644 index 000000000..9c7834b37 --- /dev/null +++ b/busybox/testsuite/msh/msh-supports-underscores-in-variable-names @@ -0,0 +1 @@ +test "`busybox msh -c 'FOO_BAR=foo; echo $FOO_BAR'`" = foo diff --git a/busybox/testsuite/mv/mv-files-to-dir b/busybox/testsuite/mv/mv-files-to-dir new file mode 100644 index 000000000..c8eaba88e --- /dev/null +++ b/busybox/testsuite/mv/mv-files-to-dir @@ -0,0 +1,16 @@ +echo file number one > file1 +echo file number two > file2 +ln -s file2 link1 +mkdir dir1 +touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3 +mkdir there +busybox mv file1 file2 link1 dir1 there +test -f there/file1 +test -f there/file2 +test -f there/dir1/file3 +test -L there/link1 +test xfile2 = x`readlink there/link1` +test ! -e file1 +test ! -e file2 +test ! -e link1 +test ! -e dir1/file3 diff --git a/busybox/testsuite/mv/mv-follows-links b/busybox/testsuite/mv/mv-follows-links new file mode 100644 index 000000000..1fb355b81 --- /dev/null +++ b/busybox/testsuite/mv/mv-follows-links @@ -0,0 +1,4 @@ +touch foo +ln -s foo bar +busybox mv bar baz +test -f baz diff --git a/busybox/testsuite/mv/mv-moves-empty-file b/busybox/testsuite/mv/mv-moves-empty-file new file mode 100644 index 000000000..48afca4d5 --- /dev/null +++ b/busybox/testsuite/mv/mv-moves-empty-file @@ -0,0 +1,4 @@ +touch foo +busybox mv foo bar +test ! -e foo +test -f bar diff --git a/busybox/testsuite/mv/mv-moves-file b/busybox/testsuite/mv/mv-moves-file new file mode 100644 index 000000000..edb4c3751 --- /dev/null +++ b/busybox/testsuite/mv/mv-moves-file @@ -0,0 +1,3 @@ +touch foo +busybox mv foo bar +test ! -f foo -a -f bar diff --git a/busybox/testsuite/mv/mv-moves-hardlinks b/busybox/testsuite/mv/mv-moves-hardlinks new file mode 100644 index 000000000..eaa8215a4 --- /dev/null +++ b/busybox/testsuite/mv/mv-moves-hardlinks @@ -0,0 +1,4 @@ +touch foo +ln foo bar +busybox mv bar baz +test ! -f bar -a -f baz diff --git a/busybox/testsuite/mv/mv-moves-large-file b/busybox/testsuite/mv/mv-moves-large-file new file mode 100644 index 000000000..77d088ff1 --- /dev/null +++ b/busybox/testsuite/mv/mv-moves-large-file @@ -0,0 +1,4 @@ +dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null +busybox mv foo bar +test ! -e foo +test -f bar diff --git a/busybox/testsuite/mv/mv-moves-small-file b/busybox/testsuite/mv/mv-moves-small-file new file mode 100644 index 000000000..065c7f1e9 --- /dev/null +++ b/busybox/testsuite/mv/mv-moves-small-file @@ -0,0 +1,4 @@ +echo I WANT > foo +busybox mv foo bar +test ! -e foo +test -f bar diff --git a/busybox/testsuite/mv/mv-moves-symlinks b/busybox/testsuite/mv/mv-moves-symlinks new file mode 100644 index 000000000..c413af07c --- /dev/null +++ b/busybox/testsuite/mv/mv-moves-symlinks @@ -0,0 +1,6 @@ +touch foo +ln -s foo bar +busybox mv bar baz +test -f foo +test ! -e bar +test -L baz diff --git a/busybox/testsuite/mv/mv-moves-unreadable-files b/busybox/testsuite/mv/mv-moves-unreadable-files new file mode 100644 index 000000000..bc9c3133c --- /dev/null +++ b/busybox/testsuite/mv/mv-moves-unreadable-files @@ -0,0 +1,5 @@ +touch foo +chmod a-r foo +busybox mv foo bar +test ! -e foo +test -f bar diff --git a/busybox/testsuite/mv/mv-preserves-hard-links b/busybox/testsuite/mv/mv-preserves-hard-links new file mode 100644 index 000000000..b3ba3aa29 --- /dev/null +++ b/busybox/testsuite/mv/mv-preserves-hard-links @@ -0,0 +1,6 @@ +# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS +touch foo +ln foo bar +mkdir baz +busybox mv foo bar baz +test baz/foo -ef baz/bar diff --git a/busybox/testsuite/mv/mv-preserves-links b/busybox/testsuite/mv/mv-preserves-links new file mode 100644 index 000000000..ea565d2f1 --- /dev/null +++ b/busybox/testsuite/mv/mv-preserves-links @@ -0,0 +1,5 @@ +touch foo +ln -s foo bar +busybox mv bar baz +test -L baz +test xfoo = x`readlink baz` diff --git a/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir b/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir new file mode 100644 index 000000000..7c572c4f8 --- /dev/null +++ b/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir @@ -0,0 +1,23 @@ +echo file number one > file1 +echo file number two > file2 +ln -s file2 link1 +mkdir dir1 +touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3 +mkdir there +busybox mv file1 file2 link1 dir1 there +test -f there/file1 +test -f there/file2 +test -f there/dir1/file3 +test -L there/link1 +test xfile2 = x`readlink there/link1` +test ! -e file1 +test ! -e file2 +test ! -e link1 +test ! -e dir1/file3 +set +e +busybox mv there there/dir1 +if [ $? != 0 ] ; then + exit 0; +fi + +exit 1; diff --git a/busybox/testsuite/mv/mv-removes-source-file b/busybox/testsuite/mv/mv-removes-source-file new file mode 100644 index 000000000..48afca4d5 --- /dev/null +++ b/busybox/testsuite/mv/mv-removes-source-file @@ -0,0 +1,4 @@ +touch foo +busybox mv foo bar +test ! -e foo +test -f bar diff --git a/busybox/testsuite/pwd/pwd-prints-working-directory b/busybox/testsuite/pwd/pwd-prints-working-directory new file mode 100644 index 000000000..8575347d6 --- /dev/null +++ b/busybox/testsuite/pwd/pwd-prints-working-directory @@ -0,0 +1 @@ +test $(pwd) = $(busybox pwd) diff --git a/busybox/testsuite/rm/rm-removes-file b/busybox/testsuite/rm/rm-removes-file new file mode 100644 index 000000000..46571a98a --- /dev/null +++ b/busybox/testsuite/rm/rm-removes-file @@ -0,0 +1,3 @@ +touch foo +busybox rm foo +test ! -f foo diff --git a/busybox/testsuite/rmdir/rmdir-removes-parent-directories b/busybox/testsuite/rmdir/rmdir-removes-parent-directories new file mode 100644 index 000000000..222f5dec7 --- /dev/null +++ b/busybox/testsuite/rmdir/rmdir-removes-parent-directories @@ -0,0 +1,3 @@ +mkdir -p foo/bar +busybox rmdir -p foo/bar +test ! -d foo diff --git a/busybox/testsuite/runtest b/busybox/testsuite/runtest new file mode 100755 index 000000000..6ba334bce --- /dev/null +++ b/busybox/testsuite/runtest @@ -0,0 +1,102 @@ +#!/bin/sh + +[ -n "$srcdir" ] || srcdir=$(pwd) +[ -n "$bindir" ] || bindir=$(dirname $(pwd)) +PATH=$bindir:$PATH + +run_applet_testcase () +{ + local applet=$1 + local testcase=$2 + + local status=0 + local RES= + + local uc_applet=$(echo $applet | tr a-z A-Z) + local testname=$(basename $testcase) + + if grep -q "^# CONFIG_${uc_applet} is not set$" $bindir/.config; then + echo UNTESTED: $testname + return 0 + fi + + if grep -q "^# FEATURE: " $testcase; then + local feature=`sed -ne 's/^# FEATURE: //p' $testcase` + + if grep -q "^# ${feature} is not set$" $bindir/.config; then + echo UNTESTED: $testname + return 0 + fi + fi + + rm -rf tmp + mkdir -p tmp + pushd tmp >/dev/null + + d=$srcdir sh -x -e $testcase >.logfile.txt 2>&1 + + if [ $? != 0 ] ; then + echo FAIL: $testname + if [ "$verbose" = 1 ]; then + cat .logfile.txt + #exit 1; + fi; + status=$? + else + echo PASS: $testname + rm -f .logfile.txt + status=$? + fi + + popd >/dev/null + rm -rf tmp + + return $status +} + +run_applet_tests () +{ + local applet=$1 + + local status=0 + + for testcase in $srcdir/$applet/*; do + if [ "$testcase" = "$srcdir/$applet/CVS" ]; then + continue + fi + + if run_applet_testcase $applet $testcase; then + : + else + status=1 + fi + done + + return $status +} + + +status=0 + +if [ x"$1" = x"-v" ]; then + verbose=1 + shift +fi + +if [ $# -ne 0 ]; then + applets="$@" +else + applets=$(ls $srcdir) +fi + +for applet in $applets; do + if [ "$applet" != CVS -a -d "$srcdir/$applet" ]; then + if run_applet_tests $applet; then + : + else + status=1 + fi + fi +done + +exit $status diff --git a/busybox/testsuite/sed/sed-accepts-blanks-before-command b/busybox/testsuite/sed/sed-accepts-blanks-before-command new file mode 100644 index 000000000..9597c2f8b --- /dev/null +++ b/busybox/testsuite/sed/sed-accepts-blanks-before-command @@ -0,0 +1 @@ +busybox sed -e '1 d' input <output <output <output <output <output <foo +cmp foo /dev/null diff --git a/busybox/testsuite/sed/sed-handles-embedded-slashes b/busybox/testsuite/sed/sed-handles-embedded-slashes new file mode 100644 index 000000000..cc287613d --- /dev/null +++ b/busybox/testsuite/sed/sed-handles-embedded-slashes @@ -0,0 +1 @@ +test "$(echo fu/bar | busybox sed -e 's/[/]//')" = fubar diff --git a/busybox/testsuite/sed/sed-handles-empty-lines b/busybox/testsuite/sed/sed-handles-empty-lines new file mode 100644 index 000000000..2bb8f045a --- /dev/null +++ b/busybox/testsuite/sed/sed-handles-empty-lines @@ -0,0 +1 @@ +test `echo | busybox sed -e 's/$/@/'` = @ diff --git a/busybox/testsuite/sed/sed-handles-unsatisfied-backrefs b/busybox/testsuite/sed/sed-handles-unsatisfied-backrefs new file mode 100644 index 000000000..61bff8837 --- /dev/null +++ b/busybox/testsuite/sed/sed-handles-unsatisfied-backrefs @@ -0,0 +1,6 @@ +busybox sed -e 's/.*root=/\1/' >output <output <output <output < output +cmp output - <output <output < logfile.gnu +busybox sort -n "$d/README" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/sort/sort-r-works b/busybox/testsuite/sort/sort-r-works new file mode 100644 index 000000000..6ee0ceb1a --- /dev/null +++ b/busybox/testsuite/sort/sort-r-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +sort -r "$d/README" > logfile.gnu +busybox sort -r "$d/README" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/sort/sort-works b/busybox/testsuite/sort/sort-works new file mode 100644 index 000000000..14a115abf --- /dev/null +++ b/busybox/testsuite/sort/sort-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +sort "$d/README" > logfile.gnu +busybox sort "$d/README" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/strings/strings-works-like-GNU b/busybox/testsuite/strings/strings-works-like-GNU new file mode 100644 index 000000000..2d6471033 --- /dev/null +++ b/busybox/testsuite/strings/strings-works-like-GNU @@ -0,0 +1,9 @@ +rm -f foo bar +strings -af ../../busybox > foo +busybox strings -af ../../busybox > bar +set +e +test ! -f foo -a -f bar +if [ $? = 0 ] ; then + set -e + diff -q foo bar +fi diff --git a/busybox/testsuite/tail/tail-n-works b/busybox/testsuite/tail/tail-n-works new file mode 100644 index 000000000..27a905f88 --- /dev/null +++ b/busybox/testsuite/tail/tail-n-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +tail -n 2 "$d/README" > logfile.gnu +busybox tail -n 2 "$d/README" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/tail/tail-works b/busybox/testsuite/tail/tail-works new file mode 100644 index 000000000..27a905f88 --- /dev/null +++ b/busybox/testsuite/tail/tail-works @@ -0,0 +1,4 @@ +[ -n "$d" ] || d=.. +tail -n 2 "$d/README" > logfile.gnu +busybox tail -n 2 "$d/README" > logfile.bb +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/tar/tar-archives-multiple-files b/busybox/testsuite/tar/tar-archives-multiple-files new file mode 100644 index 000000000..245d9e989 --- /dev/null +++ b/busybox/testsuite/tar/tar-archives-multiple-files @@ -0,0 +1,6 @@ +# FEATURE: CONFIG_FEATURE_TAR_CREATE +touch foo bar +busybox tar cf foo.tar foo bar +rm foo bar +tar xf foo.tar +test -f foo -a -f bar diff --git a/busybox/testsuite/tar/tar-complains-about-missing-file b/busybox/testsuite/tar/tar-complains-about-missing-file new file mode 100644 index 000000000..26e8cbb36 --- /dev/null +++ b/busybox/testsuite/tar/tar-complains-about-missing-file @@ -0,0 +1,3 @@ +touch foo +tar cf foo.tar foo +! busybox tar xf foo.tar bar diff --git a/busybox/testsuite/tar/tar-demands-at-least-one-ctx b/busybox/testsuite/tar/tar-demands-at-least-one-ctx new file mode 100644 index 000000000..85e7f6059 --- /dev/null +++ b/busybox/testsuite/tar/tar-demands-at-least-one-ctx @@ -0,0 +1 @@ +! busybox tar v diff --git a/busybox/testsuite/tar/tar-demands-at-most-one-ctx b/busybox/testsuite/tar/tar-demands-at-most-one-ctx new file mode 100644 index 000000000..130d0e7f9 --- /dev/null +++ b/busybox/testsuite/tar/tar-demands-at-most-one-ctx @@ -0,0 +1 @@ +! busybox tar tx diff --git a/busybox/testsuite/tar/tar-extracts-file b/busybox/testsuite/tar/tar-extracts-file new file mode 100644 index 000000000..ca72f2489 --- /dev/null +++ b/busybox/testsuite/tar/tar-extracts-file @@ -0,0 +1,5 @@ +touch foo +tar cf foo.tar foo +rm foo +busybox tar xf foo.tar +test -f foo diff --git a/busybox/testsuite/tar/tar-extracts-from-standard-input b/busybox/testsuite/tar/tar-extracts-from-standard-input new file mode 100644 index 000000000..a30e9f0b9 --- /dev/null +++ b/busybox/testsuite/tar/tar-extracts-from-standard-input @@ -0,0 +1,5 @@ +touch foo +tar cf foo.tar foo +rm foo +cat foo.tar | busybox tar x +test -f foo diff --git a/busybox/testsuite/tar/tar-extracts-multiple-files b/busybox/testsuite/tar/tar-extracts-multiple-files new file mode 100644 index 000000000..8ae8cdda5 --- /dev/null +++ b/busybox/testsuite/tar/tar-extracts-multiple-files @@ -0,0 +1,6 @@ +touch foo bar +busybox tar cf foo.tar foo bar +rm foo bar +busybox tar -xf foo.tar +test -f foo +test -f bar diff --git a/busybox/testsuite/tar/tar-extracts-to-standard-output b/busybox/testsuite/tar/tar-extracts-to-standard-output new file mode 100644 index 000000000..ca48e364e --- /dev/null +++ b/busybox/testsuite/tar/tar-extracts-to-standard-output @@ -0,0 +1,3 @@ +echo foo > foo +tar cf foo.tar foo +cat foo.tar | busybox tar Ox | cmp foo - diff --git a/busybox/testsuite/tar/tar-handles-cz-options b/busybox/testsuite/tar/tar-handles-cz-options new file mode 100644 index 000000000..5b55e46d3 --- /dev/null +++ b/busybox/testsuite/tar/tar-handles-cz-options @@ -0,0 +1,5 @@ +# FEATURE: CONFIG_FEATURE_TAR_CREATE +# FEATURE: CONFIG_FEATURE_TAR_GZIP +touch foo +busybox tar czf foo.tar.gz foo +gzip -d foo.tar.gz diff --git a/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list b/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list new file mode 100644 index 000000000..503364230 --- /dev/null +++ b/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list @@ -0,0 +1,6 @@ +# FEATURE: CONFIG_FEATURE_TAR_FROM +# FEATURE: CONFIG_FEATURE_TAR_CREATE +touch foo +tar cf foo.tar foo +echo foo >foo.exclude +busybox tar xf foo.tar -X foo.exclude diff --git a/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists b/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists new file mode 100644 index 000000000..2de0f0e91 --- /dev/null +++ b/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists @@ -0,0 +1,8 @@ +# FEATURE: CONFIG_FEATURE_TAR_FROM +# FEATURE: CONFIG_FEATURE_TAR_CREATE +touch foo bar baz +tar cf foo.tar foo bar baz +echo foo >foo.exclude +rm foo bar baz +busybox tar xf foo.tar foo bar -X foo.exclude +test ! -f foo -a -f bar -a ! -f baz diff --git a/busybox/testsuite/tar/tar-handles-multiple-X-options b/busybox/testsuite/tar/tar-handles-multiple-X-options new file mode 100644 index 000000000..155b27e68 --- /dev/null +++ b/busybox/testsuite/tar/tar-handles-multiple-X-options @@ -0,0 +1,10 @@ +# FEATURE: CONFIG_FEATURE_TAR_FROM +# FEATURE: CONFIG_FEATURE_TAR_CREATE +touch foo +touch bar +tar cf foo.tar foo bar +echo foo > foo.exclude +echo bar > bar.exclude +rm foo bar +busybox tar xf foo.tar -X foo.exclude -X bar.exclude +test ! -f foo -a ! -f bar diff --git a/busybox/testsuite/tar/tar-handles-nested-exclude b/busybox/testsuite/tar/tar-handles-nested-exclude new file mode 100644 index 000000000..39013a105 --- /dev/null +++ b/busybox/testsuite/tar/tar-handles-nested-exclude @@ -0,0 +1,9 @@ +# FEATURE: CONFIG_FEATURE_TAR_FROM +# FEATURE: CONFIG_FEATURE_TAR_CREATE +mkdir foo +touch foo/bar +tar cf foo.tar foo +rm -rf foo +echo foo/bar >foobar.exclude +busybox tar xf foo.tar foo -X foobar.exclude +test -d foo -a ! -f foo/bar diff --git a/busybox/testsuite/tee/tee-appends-input b/busybox/testsuite/tee/tee-appends-input new file mode 100644 index 000000000..cff20bf7f --- /dev/null +++ b/busybox/testsuite/tee/tee-appends-input @@ -0,0 +1,5 @@ +echo i\'m a little teapot >foo +cp foo bar +echo i\'m a little teapot >>foo +echo i\'m a little teapot | busybox tee -a bar >/dev/null +cmp foo bar diff --git a/busybox/testsuite/tee/tee-tees-input b/busybox/testsuite/tee/tee-tees-input new file mode 100644 index 000000000..26e217384 --- /dev/null +++ b/busybox/testsuite/tee/tee-tees-input @@ -0,0 +1,3 @@ +echo i\'m a little teapot >foo +echo i\'m a little teapot | busybox tee bar >baz +cmp foo bar && cmp foo baz diff --git a/busybox/testsuite/touch/touch-creates-file b/busybox/testsuite/touch/touch-creates-file new file mode 100644 index 000000000..4b4935421 --- /dev/null +++ b/busybox/testsuite/touch/touch-creates-file @@ -0,0 +1,2 @@ +busybox touch foo +test -f foo diff --git a/busybox/testsuite/touch/touch-does-not-create-file b/busybox/testsuite/touch/touch-does-not-create-file new file mode 100644 index 000000000..885259286 --- /dev/null +++ b/busybox/testsuite/touch/touch-does-not-create-file @@ -0,0 +1,2 @@ +busybox touch -c foo +test ! -f foo diff --git a/busybox/testsuite/touch/touch-touches-files-after-non-existent-file b/busybox/testsuite/touch/touch-touches-files-after-non-existent-file new file mode 100644 index 000000000..a869ec267 --- /dev/null +++ b/busybox/testsuite/touch/touch-touches-files-after-non-existent-file @@ -0,0 +1,3 @@ +touch -t 198001010000 bar +busybox touch -c foo bar +test x"`find bar -mtime -1`" = xbar diff --git a/busybox/testsuite/tr/tr-d-works b/busybox/testsuite/tr/tr-d-works new file mode 100644 index 000000000..d939e8b0f --- /dev/null +++ b/busybox/testsuite/tr/tr-d-works @@ -0,0 +1,4 @@ +echo testing | tr -d aeiou > logfile.gnu +echo testing | busybox tr -d aeiou > logfile.bb + +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/tr/tr-non-gnu b/busybox/testsuite/tr/tr-non-gnu new file mode 100644 index 000000000..ffa6951ae --- /dev/null +++ b/busybox/testsuite/tr/tr-non-gnu @@ -0,0 +1 @@ +echo fdhrnzvfu bffvsentr | busybox tr '[a-z]' '[n-z][a-m]' diff --git a/busybox/testsuite/tr/tr-works b/busybox/testsuite/tr/tr-works new file mode 100644 index 000000000..8753a3f28 --- /dev/null +++ b/busybox/testsuite/tr/tr-works @@ -0,0 +1,9 @@ +echo "cbaab" | tr abc zyx > logfile.gnu +echo "TESTING A B C" | tr [A-Z] [a-z] >> logfile.gnu +echo abc[] | tr a[b AXB >> logfile.gnu + +echo "cbaab" | busybox tr abc zyx > logfile.bb +echo "TESTING A B C" | busybox tr [A-Z] [a-z] >> logfile.bb +echo abc[] | busybox tr a[b AXB >> logfile.bb + +cmp logfile.gnu logfile.bb diff --git a/busybox/testsuite/true/true-is-silent b/busybox/testsuite/true/true-is-silent new file mode 100644 index 000000000..1d1bdb22f --- /dev/null +++ b/busybox/testsuite/true/true-is-silent @@ -0,0 +1 @@ +busybox true 2>&1 | cmp - /dev/null diff --git a/busybox/testsuite/true/true-returns-success b/busybox/testsuite/true/true-returns-success new file mode 100644 index 000000000..cdf2d55e5 --- /dev/null +++ b/busybox/testsuite/true/true-returns-success @@ -0,0 +1 @@ +busybox true diff --git a/busybox/testsuite/uptime/uptime-works b/busybox/testsuite/uptime/uptime-works new file mode 100644 index 000000000..80e578778 --- /dev/null +++ b/busybox/testsuite/uptime/uptime-works @@ -0,0 +1,2 @@ +busybox uptime + diff --git a/busybox/testsuite/uuencode/uuencode-sets-standard-input-mode-correctly b/busybox/testsuite/uuencode/uuencode-sets-standard-input-mode-correctly new file mode 100644 index 000000000..1a48a6656 --- /dev/null +++ b/busybox/testsuite/uuencode/uuencode-sets-standard-input-mode-correctly @@ -0,0 +1,4 @@ +saved_umask=$(umask) +umask 0 +busybox uuencode foo logfile.gnu +find "$d" -name \*works -type f | busybox xargs md5sum > logfile.bb +diff -u logfile.gnu logfile.bb diff --git a/busybox/util-linux/Config.in b/busybox/util-linux/Config.in new file mode 100644 index 000000000..24d548726 --- /dev/null +++ b/busybox/util-linux/Config.in @@ -0,0 +1,357 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Linux System Utilities" + + +config CONFIG_DMESG + bool "dmesg" + default n + help + dmesg is used to examine or control the kernel ring buffer. When the + Linux kernel prints messages to the system log, they are stored in + the kernel ring buffer. You can use dmesg to print the kernel's ring + buffer, clear the kernel ring buffer, change the size of the kernel + ring buffer, and change the priority level at which kernel messages + are also logged to the system console. Enable this option if you + wish to enable the 'dmesg' utility. + +config CONFIG_FBSET + bool "fbset" + default n + help + fbset is used to show or change the settings of a Linux frame buffer + device. The frame buffer device provides a simple and unique + interface to access a graphics display. Enable this option + if you wish to enable the 'fbset' utility. + + +config CONFIG_FEATURE_FBSET_FANCY + bool " Turn on extra fbset options" + default n + depends on CONFIG_FBSET + help + This option enables extended fbset options, allowing one to set the + framebuffer size, color depth, etc. interface to access a graphics + display. Enable this option if you wish to enable extended fbset + options. + +config CONFIG_FEATURE_FBSET_READMODE + bool " Turn on fbset readmode support" + default n + depends on CONFIG_FBSET + help + This option allows fbset to read the video mode database stored by + default as /etc/fb.modes, which can be used to set frame buffer + device to pre-defined video modes. + +config CONFIG_FDFLUSH + bool "fdflush" + default n + help + fdflush is only needed when changing media on slightly-broken + removable media drives. It is used to make Linux believe that a + hardware disk-change switch has been actuated, which causes Linux to + forget anything it has cached from the previous media. If you have + such a slightly-broken drive, you will need to run fdflush every time + you change a disk. Most people have working hardware and can safely + leave this disabled. + +config CONFIG_FDFORMAT + bool "fdformat" + default n + help + fdformat is used to low-level format a floppy disk. + +config CONFIG_FDISK + bool "fdisk" + default n + help + The fdisk utility is used to divide hard disks into one or more + logical disks, which are generally called partitions. This utility + can be used to list and edit the set of partitions or BSD style + 'disk slices' that are defined on a hard drive. + +config FDISK_SUPPORT_LARGE_DISKS + bool " support over 4GB disks" + default y + depends on CONFIG_FDISK + help + Enable this option to support large disks > 4GB. + +config CONFIG_FEATURE_FDISK_WRITABLE + bool " Write support" + default y + depends on CONFIG_FDISK + help + Enabling this option allows you to create or change a partition table + and write those changes out to disk. If you leave this option + disabled, you will only be able to view the partition table. + +config CONFIG_FEATURE_AIX_LABEL + bool " Support AIX disklabels" + default n + depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE + help + Enabling this option allows you to create or change AIX disklabels. + Most people can safely leave this option disabled. + +config CONFIG_FEATURE_SGI_LABEL + bool " Support SGI disklabels" + default n + depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE + help + Enabling this option allows you to create or change SGI disklabels. + Most people can safely leave this option disabled. + +config CONFIG_FEATURE_SUN_LABEL + bool " Support SUN disklabels" + default n + depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE + help + Enabling this option allows you to create or change SUN disklabels. + Most people can safely leave this option disabled. + +config CONFIG_FEATURE_OSF_LABEL + bool " Support BSD disklabels" + default n + depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE + help + Enabling this option allows you to create or change BSD disklabels + and define and edit BSD disk slices. + +config CONFIG_FEATURE_FDISK_ADVANCED + bool " Support expert mode" + default n + depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE + help + Enabling this option allows you to do terribly unsafe things like + define arbitrary drive geometry, move the beginning of data in a + partition, and similarly evil things. Unless you have a very good + reason you would be wise to leave this disabled. + +config CONFIG_FREERAMDISK + bool "freeramdisk" + default n + help + Linux allows you to create ramdisks. This utility allows you to + delete them and completely free all memory that was used for the + ramdisk. For example, if you boot Linux into a ramdisk and later + pivot_root, you may want to free the memory that is allocated to the + ramdisk. If you have no use for freeing memory from a ramdisk, leave + this disabled. + +config CONFIG_FSCK_MINIX + bool "fsck_minix" + default n + help + The minix filesystem is a nice, small, compact, read-write filesystem + with little overhead. It is not a journaling filesystem however and + can experience corruption if it is not properly unmounted or if the + power goes off in the middle of a write. This utility allows you to + check for and attempt to repair any corruption that occurs to a minix + filesystem. + +config CONFIG_MKFS_MINIX + bool "mkfs_minix" + default n + help + The minix filesystem is a nice, small, compact, read-write filesystem + with little overhead. If you wish to be able to create minix filesystems + this utility will do the job for you. + +comment "Minix filesystem support" + depends on CONFIG_FSCK_MINIX || CONFIG_MKFS_MINIX + +config CONFIG_FEATURE_MINIX2 + bool " Support Minix fs v2 (fsck_minix/mkfs_minix)" + default y + depends on CONFIG_FSCK_MINIX || CONFIG_MKFS_MINIX + help + If you wish to be able to create version 2 minix filesystems, enable this. + If you enabled 'mkfs_minix' then you almost certainly want to be using the + version 2 filesystem support. + +config CONFIG_GETOPT + bool "getopt" + default n + help + The getopt utility is used to break up (parse) options in command + lines to make it easy to write complex shell scripts that also check + for legal (and illegal) options. If you want to write horribly + complex shell scripts, or use some horribly complex shell script + written by others, this utility may be for you. Most people will + wisely leave this disabled. + +config CONFIG_HEXDUMP + bool "hexdump" + default n + help + The hexdump utility is used to display binary data in a readable + way that is comparable to the output from most hex editors. + +config CONFIG_HWCLOCK + bool "hwclock" + default n + help + The hwclock utility is used to read and set the hardware clock + on a system. This is primarily used to set the current time on + shutdown in the hardware clock, so the hardware will keep the + correct time when Linux is _not_ running. + +config CONFIG_FEATURE_HWCLOCK_LONGOPTIONS + bool " Support long options (--hctosys,...)" + default n + depends on CONFIG_HWCLOCK + help + By default, the hwclock utility only uses short options. If you + are overly fond of its long options, such as --hctosys, --utc, etc) + then enable this option. + +config CONFIG_LOSETUP + bool "losetup" + default n + help + losetup is used to associate or detach a loop device with a regular + file or block device, and to query the status of a loop device. This + version does not currently support enabling data encryption. + +config CONFIG_MKSWAP + bool "mkswap" + default n + help + The mkswap utility is used to configure a file or disk partition as + Linux swap space. This allows Linux to use the entire file or + partition as if it were additional RAM, which can greatly increase + the capability of low-memory machines. This additional memory is + much slower than real RAM, but can be very helpful at preventing your + applications being killed by the Linux out of memory (OOM) killer. + Once you have created swap space using 'mkswap' you need to enable + the swap space using the 'swapon' utility. + +config CONFIG_MORE + bool "more" + default n + help + more is a simple utility which allows you to read text one screen + sized page at a time. If you want to read text that is larger than + the screen, and you are using anything faster than a 300 baud modem, + you will probably find this utility very helpful. If you don't have + any need to reading text files, you can leave this disabled. + +config CONFIG_FEATURE_USE_TERMIOS + bool " Use termios to manipulate the screen" + default y + depends on CONFIG_MORE + help + This option allows utilities such as 'more' and 'top' to determine + the size of the screen. If you leave this disabled, your utilities + that display things on the screen will be especially primitive and + will be unable to determine the current screen size, and will be + unable to move the cursor. + +config CONFIG_PIVOT_ROOT + bool "pivot_root" + default n + help + The pivot_root utility swaps the mount points for the root filesystem + with some other mounted filesystem. This allows you to do all sorts + of wild and crazy things with your Linux system and is far more + powerful than 'chroot'. + +config CONFIG_RDATE + bool "rdate" + default n + help + The rdate utility allows you to synchronize the date and time of your + system clock with the date and time of a remote networked system using + the RFC868 protocol, which is built into the inetd daemon on most + systems. + +config CONFIG_SWAPONOFF + bool "swaponoff" + default n + help + This option enables both the 'swapon' and the 'swapoff' utilities. + Once you have created some swap space using 'mkswap', you also need + to enable your swap space with the 'swapon' utility. The 'swapoff' + utility is used, typically at system shutdown, to disable any swap + space. If you are not using any swap space, you can leave this + option disabled. + +config CONFIG_MOUNT + bool "mount" + default n + help + All files and filesystems in Unix are arranged into one big directory + tree. The 'mount' utility is used to graft a filesystem onto a + particular part of the tree. A filesystem can either live on a block + device, or it can be accessible over the network, as is the case with + NFS filesystems. Most people using BusyBox will also want to enable + the 'mount' utility. + +config CONFIG_NFSMOUNT + bool " Support mounting NFS file systems" + default n + depends on CONFIG_MOUNT + help + Enable mounting of NFS file systems. + +config CONFIG_UMOUNT + bool "umount" + default n + help + When you want to remove a mounted filesystem from its current mount point, + for example when you are shutting down the system, the 'umount' utility is + the tool to use. If you enabled the 'mount' utility, you almost certainly + also want to enable 'umount'. + +config CONFIG_FEATURE_MOUNT_FORCE + bool " Support forced filesystem unmounting" + default n + depends on CONFIG_UMOUNT + help + This allows you to _force_ a filesystem to be umounted. This is generally + only useful when you want to get rid of an unreachable NFS system. + +comment "Common options for mount/umount" + depends on CONFIG_MOUNT || CONFIG_UMOUNT + +config CONFIG_FEATURE_MOUNT_LOOP + bool " Support for loop devices" + default n + depends on CONFIG_MOUNT || CONFIG_UMOUNT + help + Enabling this feature allows mount to use the '-o' loop options, + which lets you loop mount files. Mount will automagically setup and + free the necessary loop devices so you do not need to mess with the + 'losetup' utility unless you really want to. This is really + only useful if you plan to loop mount files. + +config CONFIG_FEATURE_MTAB_SUPPORT + bool " Support for a real /etc/mtab (instead of /proc/mounts)" + default n + depends on CONFIG_MOUNT || CONFIG_UMOUNT + help + If your root filesystem is writable and you wish to have the 'mount' + utility create an mtab file listing the filesystems which have been + mounted then you should enable this option. Most people that use + BusyBox have a read-only root filesystem, so they will leave this + option disabled and BusyBox will use the /proc/mounts file. + +config CONFIG_FEATURE_MTAB_FILENAME + string " mtab file location" + default "/etc/mtab" + depends on CONFIG_FEATURE_MTAB_SUPPORT + help + Some people have a read only root filesystem, but they also wish to + have the 'mount' utility create an mtab file listing the filesystems + which have been mounted. This option allows you to specify an alternative + location for the mtab file, such as /var/mtab, or /tmp/mtab. The default + value is /etc/mtab, which is where this file is located on most desktop + Linux systems. + +endmenu + diff --git a/busybox/util-linux/Makefile b/busybox/util-linux/Makefile new file mode 100644 index 000000000..4401fd1ed --- /dev/null +++ b/busybox/util-linux/Makefile @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +top_srcdir=.. +top_buildddir=.. +srcdir=$(top_srcdir)/util-linux +UTILLINUX_DIR:=./ +include $(top_builddir)/Rules.mak +include $(top_builddir)/.config +include Makefile.in +all: $(libraries-y) +-include $(top_builddir)/.depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/busybox/util-linux/Makefile.in b/busybox/util-linux/Makefile.in new file mode 100644 index 000000000..0172b3562 --- /dev/null +++ b/busybox/util-linux/Makefile.in @@ -0,0 +1,66 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 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 +# + +UTILLINUX_AR:=util-linux.a +ifndef $(UTILLINUX_DIR) +UTILLINUX_DIR:=$(top_builddir)/util-linux/ +endif +srcdir=$(top_srcdir)/util-linux + +UTILLINUX-:= +UTILLINUX-$(CONFIG_DMESG) +=dmesg.o +UTILLINUX-$(CONFIG_FBSET) +=fbset.o +UTILLINUX-$(CONFIG_FDFLUSH) +=fdflush.o +UTILLINUX-$(CONFIG_FDFORMAT) +=fdformat.o +UTILLINUX-$(CONFIG_FDISK) +=fdisk.o +UTILLINUX-$(CONFIG_FREERAMDISK) +=freeramdisk.o +UTILLINUX-$(CONFIG_FSCK_MINIX) +=fsck_minix.o +UTILLINUX-$(CONFIG_GETOPT) +=getopt.o +UTILLINUX-$(CONFIG_HEXDUMP) +=hexdump.o +UTILLINUX-$(CONFIG_HWCLOCK) +=hwclock.o +UTILLINUX-$(CONFIG_LOSETUP) +=losetup.o +UTILLINUX-$(CONFIG_MKFS_MINIX) +=mkfs_minix.o +UTILLINUX-$(CONFIG_MKSWAP) +=mkswap.o +UTILLINUX-$(CONFIG_MORE) +=more.o +UTILLINUX-$(CONFIG_MOUNT) +=mount.o +UTILLINUX-$(CONFIG_NFSMOUNT) +=nfsmount.o +UTILLINUX-$(CONFIG_PIVOT_ROOT) +=pivot_root.o +UTILLINUX-$(CONFIG_RDATE) +=rdate.o +UTILLINUX-$(CONFIG_SWAPONOFF) +=swaponoff.o +UTILLINUX-$(CONFIG_UMOUNT) +=umount.o + +libraries-y+=$(UTILLINUX_DIR)$(UTILLINUX_AR) + +$(UTILLINUX_DIR)$(UTILLINUX_AR): $(patsubst %,$(UTILLINUX_DIR)%, $(UTILLINUX-y)) + $(AR) -ro $@ $(patsubst %,$(UTILLINUX_DIR)%, $(UTILLINUX-y)) + +$(UTILLINUX_DIR)%.o: $(srcdir)/%.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +ifneq ($(strip $(CONFIG_LFS)),y) +ifeq ($(strip $(FDISK_SUPPORT_LARGE_DISKS)),y) + +$(UTILLINUX_DIR)fdisk.o: $(srcdir)/fdisk.c + $(CC) $(CFLAGS) \ + -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \ + $(EXTRA_CFLAGS) -c -o $@ $< + +endif +endif + diff --git a/busybox/util-linux/dmesg.c b/busybox/util-linux/dmesg.c new file mode 100644 index 000000000..2ca882714 --- /dev/null +++ b/busybox/util-linux/dmesg.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* dmesg.c -- Print out the contents of the kernel ring buffer + * Created: Sat Oct 9 16:19:47 1993 + * Revised: Thu Oct 28 21:52:17 1993 by faith@cs.unc.edu + * Copyright 1993 Theodore Ts'o (tytso@athena.mit.edu) + * This program comes with ABSOLUTELY NO WARRANTY. + * Modifications by Rick Sladkey (jrs@world.std.com) + * Larger buffersize 3 June 1998 by Nicolai Langfeldt, based on a patch + * by Peeter Joot. This was also suggested by John Hudson. + * 1999-02-22 Arkadiusz Mi¶kiewicz + * - 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. + * + * Audited and cleaned up on 7 March 2003 to reduce size of + * check error handling by Erik Andersen + */ + +#include +#include +#include +#include +#include + +#include "busybox.h" + +int dmesg_main(int argc, char **argv) +{ + char *buf +#ifdef CONFIG_FEATURE_CLEAN_UP + = NULL +#endif + ; + int bufsize = 8196; + int i, n; + int level = 0; + int lastc; + int cmd = 3; + + while ((i = getopt(argc, argv, "cn:s:")) > 0) { + switch (i) { + case 'c': + cmd = 4; + break; + case 'n': + cmd = 8; + level = bb_xgetlarg(optarg, 10, 0, 10); + break; + case 's': + /* I think a 512k max kernel ring buffer is big enough for + * anybody, as the default is 16k... Could be wrong though. + * If so I'm sure I'll hear about it by the enraged masses*/ + bufsize = bb_xgetlarg(optarg, 10, 4096, 512*1024); + break; + default: + bb_show_usage(); + } + } + + if (optind < argc) { + bb_show_usage(); + } + + if (cmd == 8) { + if (klogctl(cmd, NULL, level) < 0) + goto die_the_death; + goto all_done; + } + + buf = xmalloc(bufsize); + if ((n = klogctl(cmd, buf, bufsize)) < 0) + goto die_the_death; + + lastc = '\n'; + for (i = 0; i < n; i++) { + if (lastc == '\n' && buf[i] == '<') { + i++; + while (buf[i] >= '0' && buf[i] <= '9') + i++; + if (buf[i] == '>') + i++; + } + lastc = buf[i]; + putchar(lastc); + } + if (lastc != '\n') + putchar('\n'); +all_done: +#ifdef CONFIG_FEATURE_CLEAN_UP + if (buf) { + free(buf); + } +#endif + return EXIT_SUCCESS; +die_the_death: + bb_perror_nomsg_and_die(); +} diff --git a/busybox/util-linux/fbset.c b/busybox/util-linux/fbset.c new file mode 100644 index 000000000..83bf309a3 --- /dev/null +++ b/busybox/util-linux/fbset.c @@ -0,0 +1,427 @@ +/* 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 +#include "busybox.h" + +#define DEFAULTFBDEV FB_0 +#define DEFAULTFBMODE "/etc/fb.modes" + +static const int OPT_CHANGE = (1 << 0); +static const int OPT_INFO = (1 << 1); +static const int OPT_READMODE = (1 << 2); + +enum { + CMD_FB = 1, + CMD_DB = 2, + CMD_GEOMETRY = 3, + CMD_TIMING = 4, + CMD_ACCEL = 5, + CMD_HSYNC = 6, + CMD_VSYNC = 7, + CMD_LACED = 8, + CMD_DOUBLE = 9, +/* CMD_XCOMPAT = 10, */ + CMD_ALL = 11, + CMD_INFO = 12, + CMD_CHANGE = 13, + +#ifdef CONFIG_FEATURE_FBSET_FANCY + CMD_XRES = 100, + CMD_YRES = 101, + CMD_VXRES = 102, + CMD_VYRES = 103, + CMD_DEPTH = 104, + CMD_MATCH = 105, + CMD_PIXCLOCK = 106, + CMD_LEFT = 107, + CMD_RIGHT = 108, + CMD_UPPER = 109, + CMD_LOWER = 110, + CMD_HSLEN = 111, + CMD_VSLEN = 112, + CMD_CSYNC = 113, + CMD_GSYNC = 114, + CMD_EXTSYNC = 115, + CMD_BCAST = 116, + CMD_RGBA = 117, + CMD_STEP = 118, + CMD_MOVE = 119, +#endif +}; + +static unsigned int g_options = 0; + +/* Stuff stolen from the kernel's fb.h */ +static const int FBIOGET_VSCREENINFO = 0x4600; +static const int FBIOPUT_VSCREENINFO = 0x4601; +struct fb_bitfield { + uint32_t offset; /* beginning of bitfield */ + uint32_t length; /* length of bitfield */ + uint32_t msb_right; /* != 0 : Most significant bit is */ + /* right */ +}; +struct fb_var_screeninfo { + uint32_t xres; /* visible resolution */ + uint32_t yres; + uint32_t xres_virtual; /* virtual resolution */ + uint32_t yres_virtual; + uint32_t xoffset; /* offset from virtual to visible */ + uint32_t yoffset; /* resolution */ + + uint32_t bits_per_pixel; /* guess what */ + uint32_t grayscale; /* != 0 Graylevels instead of colors */ + + struct fb_bitfield red; /* bitfield in fb mem if true color, */ + struct fb_bitfield green; /* else only length is significant */ + struct fb_bitfield blue; + struct fb_bitfield transp; /* transparency */ + + uint32_t nonstd; /* != 0 Non standard pixel format */ + + uint32_t activate; /* see FB_ACTIVATE_* */ + + uint32_t height; /* height of picture in mm */ + uint32_t width; /* width of picture in mm */ + + uint32_t accel_flags; /* acceleration flags (hints) */ + + /* Timing: All values in pixclocks, except pixclock (of course) */ + uint32_t pixclock; /* pixel clock in ps (pico seconds) */ + uint32_t left_margin; /* time from sync to picture */ + uint32_t right_margin; /* time from picture to sync */ + uint32_t upper_margin; /* time from sync to picture */ + uint32_t lower_margin; + uint32_t hsync_len; /* length of horizontal sync */ + uint32_t vsync_len; /* length of vertical sync */ + uint32_t sync; /* see FB_SYNC_* */ + uint32_t vmode; /* see FB_VMODE_* */ + uint32_t reserved[6]; /* Reserved for future compatibility */ +}; + + +const static struct cmdoptions_t { + const char *name; + const unsigned char param_count; + const unsigned char code; +} g_cmdoptions[] = { + { + "-fb", 1, CMD_FB}, { + "-db", 1, CMD_DB}, { + "-a", 0, CMD_ALL}, { + "-i", 0, CMD_INFO}, { + "-g", 5, CMD_GEOMETRY}, { + "-t", 7, CMD_TIMING}, { + "-accel", 1, CMD_ACCEL}, { + "-hsync", 1, CMD_HSYNC}, { + "-vsync", 1, CMD_VSYNC}, { + "-laced", 1, CMD_LACED}, { + "-double", 1, CMD_DOUBLE}, { + "-n", 0, CMD_CHANGE}, { +#ifdef CONFIG_FEATURE_FBSET_FANCY + "-all", 0, CMD_ALL}, { + "-xres", 1, CMD_XRES}, { + "-yres", 1, CMD_YRES}, { + "-vxres", 1, CMD_VXRES}, { + "-vyres", 1, CMD_VYRES}, { + "-depth", 1, CMD_DEPTH}, { + "-match", 0, CMD_MATCH}, { + "-geometry", 5, CMD_GEOMETRY}, { + "-pixclock", 1, CMD_PIXCLOCK}, { + "-left", 1, CMD_LEFT}, { + "-right", 1, CMD_RIGHT}, { + "-upper", 1, CMD_UPPER}, { + "-lower", 1, CMD_LOWER}, { + "-hslen", 1, CMD_HSLEN}, { + "-vslen", 1, CMD_VSLEN}, { + "-timings", 7, CMD_TIMING}, { + "-csync", 1, CMD_CSYNC}, { + "-gsync", 1, CMD_GSYNC}, { + "-extsync", 1, CMD_EXTSYNC}, { + "-bcast", 1, CMD_BCAST}, { + "-rgba", 1, CMD_RGBA}, { + "-step", 1, CMD_STEP}, { + "-move", 1, CMD_MOVE}, { +#endif + 0, 0, 0} +}; + +#ifdef CONFIG_FEATURE_FBSET_READMODE +/* taken from linux/fb.h */ +static const int FB_VMODE_INTERLACED = 1; /* interlaced */ +static const int FB_VMODE_DOUBLE = 2; /* double scan */ +static const int FB_SYNC_HOR_HIGH_ACT = 1; /* horizontal sync high active */ +static const int FB_SYNC_VERT_HIGH_ACT = 2; /* vertical sync high active */ +static const int FB_SYNC_EXT = 4; /* external sync */ +static const int FB_SYNC_COMP_HIGH_ACT = 8; /* composite sync high active */ +#endif +static int readmode(struct fb_var_screeninfo *base, const char *fn, + const char *mode) +{ +#ifdef CONFIG_FEATURE_FBSET_READMODE + FILE *f; + char buf[256]; + char *p = buf; + + f = bb_xfopen(fn, "r"); + while (!feof(f)) { + fgets(buf, sizeof(buf), f); + if ((p = strstr(buf, "mode ")) || (p = strstr(buf, "mode\t"))) { + p += 5; + if ((p = strstr(buf, mode))) { + p += strlen(mode); + if (!isspace(*p) && (*p != 0) && (*p != '"') + && (*p != '\r') && (*p != '\n')) + continue; /* almost, but not quite */ + while (!feof(f)) { + fgets(buf, sizeof(buf), f); + + if ((p = strstr(buf, "geometry "))) { + p += 9; + + sscanf(p, "%d %d %d %d %d", + &(base->xres), &(base->yres), + &(base->xres_virtual), &(base->yres_virtual), + &(base->bits_per_pixel)); + } else if ((p = strstr(buf, "timings "))) { + p += 8; + + sscanf(p, "%d %d %d %d %d %d %d", + &(base->pixclock), + &(base->left_margin), &(base->right_margin), + &(base->upper_margin), &(base->lower_margin), + &(base->hsync_len), &(base->vsync_len)); + } else if ((p = strstr(buf, "laced "))) { + p += 6; + + if (strstr(buf, "false")) { + base->vmode &= ~FB_VMODE_INTERLACED; + } else { + base->vmode |= FB_VMODE_INTERLACED; + } + } else if ((p = strstr(buf, "double "))) { + p += 7; + + if (strstr(buf, "false")) { + base->vmode &= ~FB_VMODE_DOUBLE; + } else { + base->vmode |= FB_VMODE_DOUBLE; + } + } else if ((p = strstr(buf, "vsync "))) { + p += 6; + + if (strstr(buf, "low")) { + base->sync &= ~FB_SYNC_VERT_HIGH_ACT; + } else { + base->sync |= FB_SYNC_VERT_HIGH_ACT; + } + } else if ((p = strstr(buf, "hsync "))) { + p += 6; + + if (strstr(buf, "low")) { + base->sync &= ~FB_SYNC_HOR_HIGH_ACT; + } else { + base->sync |= FB_SYNC_HOR_HIGH_ACT; + } + } else if ((p = strstr(buf, "csync "))) { + p += 6; + + if (strstr(buf, "low")) { + base->sync &= ~FB_SYNC_COMP_HIGH_ACT; + } else { + base->sync |= FB_SYNC_COMP_HIGH_ACT; + } + } else if ((p = strstr(buf, "extsync "))) { + p += 8; + + if (strstr(buf, "false")) { + base->sync &= ~FB_SYNC_EXT; + } else { + base->sync |= FB_SYNC_EXT; + } + } + + if (strstr(buf, "endmode")) + return 1; + } + } + } + } +#else + bb_error_msg( "mode reading not compiled in"); +#endif + return 0; +} + +static inline void setmode(struct fb_var_screeninfo *base, + struct fb_var_screeninfo *set) +{ + if ((int) set->xres > 0) + base->xres = set->xres; + if ((int) set->yres > 0) + base->yres = set->yres; + if ((int) set->xres_virtual > 0) + base->xres_virtual = set->xres_virtual; + if ((int) set->yres_virtual > 0) + base->yres_virtual = set->yres_virtual; + if ((int) set->bits_per_pixel > 0) + base->bits_per_pixel = set->bits_per_pixel; +} + +static inline void showmode(struct fb_var_screeninfo *v) +{ + double drate = 0, hrate = 0, vrate = 0; + + if (v->pixclock) { + drate = 1e12 / v->pixclock; + hrate = + drate / (v->left_margin + v->xres + v->right_margin + + v->hsync_len); + vrate = + hrate / (v->upper_margin + v->yres + v->lower_margin + + v->vsync_len); + } + printf("\nmode \"%ux%u-%u\"\n" +#ifdef CONFIG_FEATURE_FBSET_FANCY + "\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n" +#endif + "\tgeometry %u %u %u %u %u\n\ttimings %u %u %u %u %u %u %u\n\taccel %s\n\trgba %u/%u,%u/%u,%u/%u,%u/%u\nendmode\n\n", + v->xres, v->yres, (int) (vrate + 0.5), +#ifdef CONFIG_FEATURE_FBSET_FANCY + drate / 1e6, hrate / 1e3, vrate, +#endif + v->xres, v->yres, v->xres_virtual, v->yres_virtual, + v->bits_per_pixel, v->pixclock, v->left_margin, + v->right_margin, v->upper_margin, v->lower_margin, v->hsync_len, + v->vsync_len, (v->accel_flags > 0 ? "true" : "false"), v->red.length, + v->red.offset, v->green.length, v->green.offset, v->blue.length, + v->blue.offset, v->transp.length, v->transp.offset); +} + +#ifdef STANDALONE +int main(int argc, char **argv) +#else +extern int fbset_main(int argc, char **argv) +#endif +{ + struct fb_var_screeninfo var, varset; + int fh, i; + char *fbdev = DEFAULTFBDEV; + char *modefile = DEFAULTFBMODE; + char *thisarg, *mode = NULL; + + memset(&varset, 0xFF, sizeof(varset)); + + /* parse cmd args.... why do they have to make things so difficult? */ + argv++; + argc--; + for (; argc > 0 && (thisarg = *argv); argc--, argv++) { + for (i = 0; g_cmdoptions[i].name; i++) { + if (!strcmp(thisarg, g_cmdoptions[i].name)) { + if (argc - 1 < g_cmdoptions[i].param_count) + bb_show_usage(); + switch (g_cmdoptions[i].code) { + case CMD_FB: + fbdev = argv[1]; + break; + case CMD_DB: + modefile = argv[1]; + break; + case CMD_GEOMETRY: + varset.xres = strtoul(argv[1], 0, 0); + varset.yres = strtoul(argv[2], 0, 0); + varset.xres_virtual = strtoul(argv[3], 0, 0); + varset.yres_virtual = strtoul(argv[4], 0, 0); + varset.bits_per_pixel = strtoul(argv[5], 0, 0); + break; + case CMD_TIMING: + varset.pixclock = strtoul(argv[1], 0, 0); + varset.left_margin = strtoul(argv[2], 0, 0); + varset.right_margin = strtoul(argv[3], 0, 0); + varset.upper_margin = strtoul(argv[4], 0, 0); + varset.lower_margin = strtoul(argv[5], 0, 0); + varset.hsync_len = strtoul(argv[6], 0, 0); + varset.vsync_len = strtoul(argv[7], 0, 0); + break; + case CMD_CHANGE: + g_options |= OPT_CHANGE; + break; +#ifdef CONFIG_FEATURE_FBSET_FANCY + case CMD_XRES: + varset.xres = strtoul(argv[1], 0, 0); + break; + case CMD_YRES: + varset.yres = strtoul(argv[1], 0, 0); + break; + case CMD_DEPTH: + varset.bits_per_pixel = strtoul(argv[1], 0, 0); + break; +#endif + } + argc -= g_cmdoptions[i].param_count; + argv += g_cmdoptions[i].param_count; + break; + } + } + if (!g_cmdoptions[i].name) { + if (argc == 1) { + mode = *argv; + g_options |= OPT_READMODE; + } else { + bb_show_usage(); + } + } + } + + if ((fh = open(fbdev, O_RDONLY)) < 0) + bb_perror_msg_and_die("fbset(open)"); + if (ioctl(fh, FBIOGET_VSCREENINFO, &var)) + bb_perror_msg_and_die("fbset(ioctl)"); + if (g_options & OPT_READMODE) { + if (!readmode(&var, modefile, mode)) { + bb_error_msg("Unknown video mode `%s'", mode); + return EXIT_FAILURE; + } + } + + setmode(&var, &varset); + if (g_options & OPT_CHANGE) + if (ioctl(fh, FBIOPUT_VSCREENINFO, &var)) + bb_perror_msg_and_die("fbset(ioctl)"); + showmode(&var); + /* Don't close the file, as exiting will take care of that */ + /* close(fh); */ + + return EXIT_SUCCESS; +} diff --git a/busybox/util-linux/fdflush.c b/busybox/util-linux/fdflush.c new file mode 100644 index 000000000..c3fcf3325 --- /dev/null +++ b/busybox/util-linux/fdflush.c @@ -0,0 +1,54 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fdflush implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * Copyright (C) 2003 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" + +/* From */ +#define FDFLUSH _IO(2,0x4b) + +extern int fdflush_main(int argc, char **argv) +{ + int fd, result; + + if (argc <= 1) + bb_show_usage(); + + fd = bb_xopen(argv[1], 0); + + result = ioctl(fd, FDFLUSH, 0); +#ifdef CONFIG_FEATURE_CLEAN_UP + close(fd); +#endif + if (result) { + bb_perror_nomsg_and_die(); + } + + /* Don't bother closing. Exit does + * that, so we can save a few bytes */ + return EXIT_SUCCESS; +} diff --git a/busybox/util-linux/fdformat.c b/busybox/util-linux/fdformat.c new file mode 100644 index 000000000..bd4527581 --- /dev/null +++ b/busybox/util-linux/fdformat.c @@ -0,0 +1,161 @@ +/* fdformat.c - Low-level formats a floppy disk - Werner Almesberger */ + +/* 1999-02-22 Arkadiusz Mi¶kiewicz + * - added Native Language Support + * 1999-03-20 Arnaldo Carvalho de Melo + * - more i18n/nls translatable strings marked + * + * 5 July 2003 -- modified for Busybox by Erik Andersen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + + +/* Stuff extracted from linux/fd.h */ +struct floppy_struct { + unsigned int size, /* nr of sectors total */ + sect, /* sectors per track */ + head, /* nr of heads */ + track, /* nr of tracks */ + stretch; /* !=0 means double track steps */ +#define FD_STRETCH 1 +#define FD_SWAPSIDES 2 + + unsigned char gap, /* gap1 size */ + + rate, /* data rate. |= 0x40 for perpendicular */ +#define FD_2M 0x4 +#define FD_SIZECODEMASK 0x38 +#define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8) +#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \ + 512 : 128 << FD_SIZECODE(floppy) ) +#define FD_PERP 0x40 + + spec1, /* stepping rate, head unload time */ + fmt_gap; /* gap2 size */ + const char * name; /* used only for predefined formats */ +}; +struct format_descr { + unsigned int device,head,track; +}; +#define FDFMTBEG _IO(2,0x47) +#define FDFMTTRK _IOW(2,0x48, struct format_descr) +#define FDFMTEND _IO(2,0x49) +#define FDGETPRM _IOR(2, 0x04, struct floppy_struct) +#define FD_FILL_BYTE 0xF6 /* format fill byte. */ + +static void print_and_flush(const char * __restrict format, ...) +{ + va_list arg; + + va_start(arg, format); + bb_vfprintf(stdout, format, arg); + va_end(arg); + bb_xfflush_stdout(); +} + +static void bb_xioctl(int fd, int request, void *argp, const char *string) +{ + if (ioctl (fd, request, argp) < 0) { + bb_perror_msg_and_die(string); + } +} + +int fdformat_main(int argc,char **argv) +{ + int fd, n, cyl, read_bytes, verify; + unsigned char *data; + struct stat st; + struct floppy_struct param; + struct format_descr descr; + + if (argc < 2) { + bb_show_usage(); + } + verify = !bb_getopt_ulflags(argc, argv, "n"); + argv += optind; + + /* R_OK is needed for verifying */ + if (stat(*argv,&st) < 0 || access(*argv,W_OK | R_OK ) < 0) { + bb_perror_msg_and_die(*argv); + } + if (!S_ISBLK(st.st_mode)) { + bb_error_msg_and_die("%s: not a block device",*argv); + /* do not test major - perhaps this was an USB floppy */ + } + + + /* O_RDWR for formatting and verifying */ + fd = bb_xopen(*argv,O_RDWR ); + + bb_xioctl(fd, FDGETPRM, ¶m, "FDGETPRM");/*original message was: "Could not determine current format type" */ + + print_and_flush("%s-sided, %d tracks, %d sec/track. Total capacity %d kB.\n", + (param.head == 2) ? "Double" : "Single", + param.track, param.sect, param.size >> 1); + + /* FORMAT */ + print_and_flush("Formatting ... ", NULL); + bb_xioctl(fd, FDFMTBEG,NULL,"FDFMTBEG"); + + /* n == track */ + for (n = 0; n < param.track; n++) + { + descr.head = 0; + descr.track = n; + bb_xioctl(fd, FDFMTTRK,&descr,"FDFMTTRK"); + print_and_flush("%3d\b\b\b", n); + if (param.head == 2) { + descr.head = 1; + bb_xioctl(fd, FDFMTTRK,&descr,"FDFMTTRK"); + } + } + + bb_xioctl(fd,FDFMTEND,NULL,"FDFMTEND"); + print_and_flush("done\n", NULL); + + /* VERIFY */ + if(verify) { + /* n == cyl_size */ + n = param.sect*param.head*512; + + data = xmalloc(n); + print_and_flush("Verifying ... ", NULL); + for (cyl = 0; cyl < param.track; cyl++) { + print_and_flush("%3d\b\b\b", cyl); + if((read_bytes = safe_read(fd,data,n))!= n ) { + if(read_bytes < 0) { + bb_perror_msg("Read: "); + } + bb_error_msg_and_die("Problem reading cylinder %d, expected %d, read %d", cyl, n, read_bytes); + } + /* Check backwards so we don't need a counter */ + while(--read_bytes>=0) { + if( data[read_bytes] != FD_FILL_BYTE) { + print_and_flush("bad data in cyl %d\nContinuing ... ",cyl); + } + } + } + /* There is no point in freeing blocks at the end of a program, because + all of the program's space is given back to the system when the process + terminates.*/ +#ifdef CONFIG_FEATURE_CLEAN_UP + free(data); +#endif + print_and_flush("done\n", NULL); + } +#ifdef CONFIG_FEATURE_CLEAN_UP + close(fd); +#endif + /* Don't bother closing. Exit does + * that, so we can save a few bytes */ + return EXIT_SUCCESS; +} diff --git a/busybox/util-linux/fdisk.c b/busybox/util-linux/fdisk.c new file mode 100644 index 000000000..8580bec70 --- /dev/null +++ b/busybox/util-linux/fdisk.c @@ -0,0 +1,5880 @@ +/* fdisk.c -- Partition table manipulator for Linux. + * + * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) + * + * This program is free software. You can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation: either version 1 or + * (at your option) any later version. + * + * Vladimir Oleynik 2001,2002 Busybox port + */ + +#define UTIL_LINUX_VERSION "2.12" + +#define PROC_PARTITIONS "/proc/partitions" + +#include +#include +#include /* stat */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* assert */ +#include +#include +#include +#include +#include /* major */ + +#include /* for uint32_t, uint16_t, uint8_t, int16_t, etc */ + +/* Copied from linux/major.h */ +#define FLOPPY_MAJOR 2 + +#include + +#include "busybox.h" + +#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r)) + +#define DKTYPENAMES + +#define _(Text) Text + +#define BLKRRPART _IO(0x12,95) /* re-read partition table */ +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +#define BLKSSZGET _IO(0x12,104) /* get block device sector size */ + +/* Avoid conflicts with the 2.6 kernel headers, which define + * _IOR rather differently */ +#undef _IOR +#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) +#define BLKGETSIZE64 _IOR(0x12,114,uint64_t) + +/* + fdisk.h +*/ + +#define DEFAULT_SECTOR_SIZE 512 +#define MAX_SECTOR_SIZE 2048 +#define SECTOR_SIZE 512 /* still used in BSD code */ +#define MAXIMUM_PARTS 60 + +#define ACTIVE_FLAG 0x80 + +#define EXTENDED 0x05 +#define WIN98_EXTENDED 0x0f +#define LINUX_PARTITION 0x81 +#define LINUX_SWAP 0x82 +#define LINUX_NATIVE 0x83 +#define LINUX_EXTENDED 0x85 +#define LINUX_LVM 0x8e +#define LINUX_RAID 0xfd + +#define SUNOS_SWAP 3 +#define WHOLE_DISK 5 + +#define IS_EXTENDED(i) \ + ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED) + +#define SIZE(a) (sizeof(a)/sizeof((a)[0])) + +#define cround(n) (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n)) +#define scround(x) (((x)+units_per_sector-1)/units_per_sector) + +#ifdef CONFIG_FEATURE_SUN_LABEL +#define SCSI_IOCTL_GET_IDLUN 0x5382 +#endif + + +/* including also fails */ +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; + +#define HDIO_GETGEO 0x0301 /* get device geometry */ + + +struct systypes { + const unsigned char *name; +}; + +static uint sector_size = DEFAULT_SECTOR_SIZE, + user_set_sector_size, + sector_offset = 1; + +/* + * Raw disk label. For DOS-type partition tables the MBR, + * with descriptions of the primary partitions. + */ +static char MBRbuffer[MAX_SECTOR_SIZE]; + +#ifdef CONFIG_FEATURE_SUN_LABEL +static int sun_label; /* looking at sun disklabel */ +#else +#define sun_label 0 +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL +static int sgi_label; /* looking at sgi disklabel */ +#else +#define sgi_label 0 +#endif +#ifdef CONFIG_FEATURE_AIX_LABEL +static int aix_label; /* looking at aix disklabel */ +#else +#define aix_label 0 +#endif +#ifdef CONFIG_FEATURE_OSF_LABEL +static int osf_label; /* looking at OSF/1 disklabel */ +static int possibly_osf_label; +#else +#define osf_label 0 +#endif + +#define dos_label (!sun_label && !sgi_label && !aix_label && !osf_label) + +static uint heads, sectors, cylinders; +static void update_units(void); + + +/* + * return partition name - uses static storage unless buf is supplied + */ +static const char * +partname(const char *dev, int pno, int lth) { + static char buffer[80]; + const char *p; + int w, wp; + int bufsiz; + char *bufp; + + bufp = buffer; + bufsiz = sizeof(buffer); + + w = strlen(dev); + p = ""; + + if (isdigit(dev[w-1])) + p = "p"; + + /* devfs kludge - note: fdisk partition names are not supposed + to equal kernel names, so there is no reason to do this */ + if (strcmp (dev + w - 4, "disc") == 0) { + w -= 4; + p = "part"; + } + + wp = strlen(p); + + if (lth) { + snprintf(bufp, bufsiz, "%*.*s%s%-2u", + lth-wp-2, w, dev, p, pno); + } else { + snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno); + } + return bufp; +} + +struct partition { + unsigned char boot_ind; /* 0x80 - active */ + unsigned char head; /* starting head */ + unsigned char sector; /* starting sector */ + unsigned char cyl; /* starting cylinder */ + unsigned char sys_ind; /* What partition type */ + unsigned char end_head; /* end head */ + unsigned char end_sector; /* end sector */ + unsigned char end_cyl; /* end cylinder */ + unsigned char start4[4]; /* starting sector counting from 0 */ + unsigned char size4[4]; /* nr of sectors in partition */ +} __attribute__((__packed__)); + +enum failure { + ioctl_error, unable_to_open, unable_to_read, unable_to_seek, + unable_to_write +}; + +enum action {fdisk, require, try_only, create_empty_dos, create_empty_sun}; + +static const char *disk_device; +static int fd; /* the disk */ +static int partitions = 4; /* maximum partition + 1 */ +static uint display_in_cyl_units = 1; +static uint units_per_sector = 1; +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static char *line_ptr; +static void change_units(void); +static void reread_partition_table(int leave); +static void delete_partition(int i); +static int get_partition(int warn, int max); +static void list_types(const struct systypes *sys); +static uint read_int(uint low, uint dflt, uint high, uint base, char *mesg); +#endif +static const char *partition_type(unsigned char type); +static void fdisk_fatal(enum failure why) __attribute__ ((noreturn)); +static void get_geometry(void); +static int get_boot(enum action what); + +#define PLURAL 0 +#define SINGULAR 1 + +#define hex_val(c) ({ \ + char _c = (c); \ + isdigit(_c) ? _c - '0' : \ + tolower(_c) + 10 - 'a'; \ + }) + + +#define LINE_LENGTH 800 +#define pt_offset(b, n) ((struct partition *)((b) + 0x1be + \ + (n) * sizeof(struct partition))) +#define sector(s) ((s) & 0x3f) +#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) + +#define hsc2sector(h,s,c) (sector(s) - 1 + sectors * \ + ((h) + heads * cylinder(s,c))) +#define set_hsc(h,s,c,sector) { \ + s = sector % sectors + 1; \ + sector /= sectors; \ + h = sector % heads; \ + sector /= heads; \ + c = sector & 0xff; \ + s |= (sector >> 2) & 0xc0; \ + } + + +static int32_t get_start_sect(const struct partition *p); +static int32_t get_nr_sects(const struct partition *p); + +/* + * per partition table entry data + * + * The four primary partitions have the same sectorbuffer (MBRbuffer) + * and have NULL ext_pointer. + * Each logical partition table entry has two pointers, one for the + * partition and one link to the next one. + */ +static struct pte { + struct partition *part_table; /* points into sectorbuffer */ + struct partition *ext_pointer; /* points into sectorbuffer */ +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + char changed; /* boolean */ +#endif + off_t offset; /* disk sector number */ + char *sectorbuffer; /* disk sector contents */ +} ptes[MAXIMUM_PARTS]; + + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +set_all_unchanged(void) { + int i; + + for (i = 0; i < MAXIMUM_PARTS; i++) + ptes[i].changed = 0; +} + +static void +set_changed(int i) { + ptes[i].changed = 1; +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_OSF_LABEL) +static struct partition * +get_part_table(int i) { + return ptes[i].part_table; +} +#endif + +static const char * +str_units(int n) { /* n==1: use singular */ + if (n == 1) + return display_in_cyl_units ? _("cylinder") : _("sector"); + else + return display_in_cyl_units ? _("cylinders") : _("sectors"); +} + +static int +valid_part_table_flag(const unsigned char *b) { + return (b[510] == 0x55 && b[511] == 0xaa); +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static char line_buffer[LINE_LENGTH]; + +/* read line; return 0 or first char */ +static int +read_line(void) +{ + static int got_eof = 0; + + fflush (stdout); /* requested by niles@scyld.com */ + line_ptr = line_buffer; + if (!fgets(line_buffer, LINE_LENGTH, stdin)) { + if (feof(stdin)) + got_eof++; /* user typed ^D ? */ + if (got_eof >= 3) { + fprintf(stderr, _("\ngot EOF thrice - exiting..\n")); + exit(1); + } + return 0; + } + while (*line_ptr && !isgraph(*line_ptr)) + line_ptr++; + return *line_ptr; +} + +static char +read_char(const char *mesg) +{ + do { + fputs(mesg, stdout); + } while (!read_line()); + return *line_ptr; +} + +static char +read_chars(const char *mesg) +{ + fputs(mesg, stdout); + if (!read_line()) { + *line_ptr = '\n'; + line_ptr[1] = 0; + } + return *line_ptr; +} + +static int +read_hex(const struct systypes *sys) +{ + int hex; + + while (1) + { + read_char(_("Hex code (type L to list codes): ")); + if (*line_ptr == 'l' || *line_ptr == 'L') + list_types(sys); + else if (isxdigit (*line_ptr)) + { + hex = 0; + do + hex = hex << 4 | hex_val(*line_ptr++); + while (isxdigit(*line_ptr)); + return hex; + } + } +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +#ifdef CONFIG_FEATURE_AIX_LABEL +/* + * Copyright (C) Andreas Neuper, Sep 1998. + * This file may be redistributed under + * the terms of the GNU Public License. + */ + +typedef struct { + unsigned int magic; /* expect AIX_LABEL_MAGIC */ + unsigned int fillbytes1[124]; + unsigned int physical_volume_id; + unsigned int fillbytes2[124]; +} aix_partition; + +#define AIX_LABEL_MAGIC 0xc9c2d4c1 +#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9 +#define AIX_INFO_MAGIC 0x00072959 +#define AIX_INFO_MAGIC_SWAPPED 0x59290700 + +#define aixlabel ((aix_partition *)MBRbuffer) + + +/* + Changes: + * 1999-03-20 Arnaldo Carvalho de Melo + * Internationalization + * + * 2003-03-20 Phillip Kesling + * Some fixes +*/ + +static int aix_other_endian; +static short aix_volumes=1; + +/* + * only dealing with free blocks here + */ + +static void +aix_info( void ) { + puts( + _("\n\tThere is a valid AIX label on this disk.\n" + "\tUnfortunately Linux cannot handle these\n" + "\tdisks at the moment. Nevertheless some\n" + "\tadvice:\n" + "\t1. fdisk will destroy its contents on write.\n" + "\t2. Be sure that this disk is NOT a still vital\n" + "\t part of a volume group. (Otherwise you may\n" + "\t erase the other disks as well, if unmirrored.)\n" + "\t3. Before deleting this physical volume be sure\n" + "\t to remove the disk logically from your AIX\n" + "\t machine. (Otherwise you become an AIXpert).") + ); +} + +static void +aix_nolabel( void ) +{ + aixlabel->magic = 0; + aix_label = 0; + partitions = 4; + memset( MBRbuffer, 0, sizeof(MBRbuffer) ); /* avoid fdisk cores */ + return; +} + +static int +check_aix_label( void ) +{ + if (aixlabel->magic != AIX_LABEL_MAGIC && + aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED) { + aix_label = 0; + aix_other_endian = 0; + return 0; + } + aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED); + update_units(); + aix_label = 1; + partitions= 1016; + aix_volumes = 15; + aix_info(); + aix_nolabel(); /* %% */ + aix_label = 1; /* %% */ + return 1; +} +#endif /* AIX_LABEL */ + +#ifdef CONFIG_FEATURE_OSF_LABEL +/* + * Copyright (c) 1987, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef BSD_DISKMAGIC +#define BSD_DISKMAGIC ((uint32_t) 0x82564557) +#endif + +#ifndef BSD_MAXPARTITIONS +#define BSD_MAXPARTITIONS 16 +#endif + +#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec" + +#if defined (i386) || defined (__sparc__) || defined (__arm__) || defined (__mips__) || defined (__s390__) || defined (__sh__) || defined(__x86_64__) +#define BSD_LABELSECTOR 1 +#define BSD_LABELOFFSET 0 +#elif defined (__alpha__) || defined (__powerpc__) || defined (__ia64__) || defined (__hppa__) +#define BSD_LABELSECTOR 0 +#define BSD_LABELOFFSET 64 +#elif defined (__s390__) || defined (__s390x__) +#define BSD_LABELSECTOR 1 +#define BSD_LABELOFFSET 0 +#else +#error unknown architecture +#endif + +#define BSD_BBSIZE 8192 /* size of boot area, with label */ +#define BSD_SBSIZE 8192 /* max size of fs superblock */ + +struct xbsd_disklabel { + uint32_t d_magic; /* the magic number */ + int16_t d_type; /* drive type */ + int16_t d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + char d_packname[16]; /* pack identifier */ + /* disk geometry: */ + uint32_t d_secsize; /* # of bytes per sector */ + uint32_t d_nsectors; /* # of data sectors per track */ + uint32_t d_ntracks; /* # of tracks per cylinder */ + uint32_t d_ncylinders; /* # of data cylinders per unit */ + uint32_t d_secpercyl; /* # of data sectors per cylinder */ + uint32_t d_secperunit; /* # of data sectors per unit */ + /* + * Spares (bad sector replacements) below + * are not counted in d_nsectors or d_secpercyl. + * Spare sectors are assumed to be physical sectors + * which occupy space at the end of each track and/or cylinder. + */ + uint16_t d_sparespertrack; /* # of spare sectors per track */ + uint16_t d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternate cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + uint32_t d_acylinders; /* # of alt. cylinders per unit */ + + /* hardware characteristics: */ + /* + * d_interleave, d_trackskew and d_cylskew describe perturbations + * in the media format used to compensate for a slow controller. + * Interleave is physical sector interleave, set up by the formatter + * or controller when formatting. When interleaving is in use, + * logically adjacent sectors are not physically contiguous, + * but instead are separated by some number of sectors. + * It is specified as the ratio of physical sectors traversed + * per logical sector. Thus an interleave of 1:1 implies contiguous + * layout, while 2:1 implies that logical sector 0 is separated + * by one sector from logical sector 1. + * d_trackskew is the offset of sector 0 on track N + * relative to sector 0 on track N-1 on the same cylinder. + * Finally, d_cylskew is the offset of sector 0 on cylinder N + * relative to sector 0 on cylinder N-1. + */ + uint16_t d_rpm; /* rotational speed */ + uint16_t d_interleave; /* hardware sector interleave */ + uint16_t d_trackskew; /* sector 0 skew, per track */ + uint16_t d_cylskew; /* sector 0 skew, per cylinder */ + uint32_t d_headswitch; /* head switch time, usec */ + uint32_t d_trkseek; /* track-to-track seek, usec */ + uint32_t d_flags; /* generic flags */ +#define NDDATA 5 + uint32_t d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + uint32_t d_spare[NSPARE]; /* reserved for future use */ + uint32_t d_magic2; /* the magic number (again) */ + uint16_t d_checksum; /* xor of data incl. partitions */ + /* filesystem and partition information: */ + uint16_t d_npartitions; /* number of partitions in following */ + uint32_t d_bbsize; /* size of boot area at sn0, bytes */ + uint32_t d_sbsize; /* max size of fs superblock, bytes */ + struct xbsd_partition { /* the partition table */ + uint32_t p_size; /* number of sectors in partition */ + uint32_t p_offset; /* starting sector */ + uint32_t p_fsize; /* filesystem basic fragment size */ + uint8_t p_fstype; /* filesystem type, see below */ + uint8_t p_frag; /* filesystem fragments per block */ + uint16_t p_cpg; /* filesystem cylinders per group */ + } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */ +}; + +/* d_type values: */ +#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ +#define BSD_DTYPE_MSCP 2 /* MSCP */ +#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */ +#define BSD_DTYPE_SCSI 4 /* SCSI */ +#define BSD_DTYPE_ESDI 5 /* ESDI interface */ +#define BSD_DTYPE_ST506 6 /* ST506 etc. */ +#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */ +#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */ +#define BSD_DTYPE_FLOPPY 10 /* floppy */ + +/* d_subtype values: */ +#define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */ +#define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */ +#define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */ + +#ifdef DKTYPENAMES +static const char * const xbsd_dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "ST506", + "HP-IB", + "HP-FL", + "type 9", + "floppy", + 0 +}; +#define BSD_DKMAXTYPES (sizeof(xbsd_dktypenames) / sizeof(xbsd_dktypenames[0]) - 1) +#endif + +/* + * Filesystem type and version. + * Used to interpret other filesystem-specific + * per-partition information. + */ +#define BSD_FS_UNUSED 0 /* unused */ +#define BSD_FS_SWAP 1 /* swap */ +#define BSD_FS_V6 2 /* Sixth Edition */ +#define BSD_FS_V7 3 /* Seventh Edition */ +#define BSD_FS_SYSV 4 /* System V */ +#define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */ +#define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */ +#define BSD_FS_HPFS 11 /* OS/2 high-performance file system */ +#define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */ +#define BSD_FS_ISOFS BSD_FS_ISO9660 +#define BSD_FS_BOOT 13 /* partition contains bootstrap */ +#define BSD_FS_ADOS 14 /* AmigaDOS fast file system */ +#define BSD_FS_HFS 15 /* Macintosh HFS */ +#define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */ + +/* this is annoying, but it's also the way it is :-( */ +#ifdef __alpha__ +#define BSD_FS_EXT2 8 /* ext2 file system */ +#else +#define BSD_FS_MSDOS 8 /* MS-DOS file system */ +#endif + +#ifdef DKTYPENAMES +static const struct systypes xbsd_fstypes[] = { +/* BSD_FS_UNUSED */ {"\x00" "unused"}, +/* BSD_FS_SWAP */ {"\x01" "swap"}, +/* BSD_FS_V6 */ {"\x02" "Version 6"}, +/* BSD_FS_V7 */ {"\x03" "Version 7"}, +/* BSD_FS_SYSV */ {"\x04" "System V"}, +/* BSD_FS_V71K */ {"\x05" "4.1BSD"}, +/* BSD_FS_V8 */ {"\x06" "Eighth Edition"}, +/* BSD_FS_BSDFFS */ {"\x07" "4.2BSD"}, +#ifdef __alpha__ +/* BSD_FS_EXT2 */ {"\x08" "ext2"}, +#else +/* BSD_FS_MSDOS */ {"\x08" "MS-DOS"}, +#endif +/* BSD_FS_BSDLFS */ {"\x09" "4.4LFS"}, +/* BSD_FS_OTHER */ {"\x0a" "unknown"}, +/* BSD_FS_HPFS */ {"\x0b" "HPFS"}, +/* BSD_FS_ISO9660 */ {"\x0c" "ISO-9660"}, +/* BSD_FS_BOOT */ {"\x0d" "boot"}, +/* BSD_FS_ADOS */ {"\x0e" "ADOS"}, +/* BSD_FS_HFS */ {"\x0f" "HFS"}, +/* BSD_FS_ADVFS */ {"\x10" "AdvFS"}, + { NULL } +}; +#define BSD_FSMAXTYPES (SIZE(xbsd_fstypes)-1) + +#endif + +/* + * flags shared by various drives: + */ +#define BSD_D_REMOVABLE 0x01 /* removable media */ +#define BSD_D_ECC 0x02 /* supports ECC */ +#define BSD_D_BADSECT 0x04 /* supports bad sector forw. */ +#define BSD_D_RAMDISK 0x08 /* disk emulator */ +#define BSD_D_CHAIN 0x10 /* can do back-back transfers */ +#define BSD_D_DOSPART 0x20 /* within MSDOS partition */ + +#endif /* OSF_LABEL */ + +/* + * Copyright (C) Andreas Neuper, Sep 1998. + * This file may be modified and redistributed under + * the terms of the GNU Public License. + */ + +struct device_parameter { /* 48 bytes */ + unsigned char skew; + unsigned char gap1; + unsigned char gap2; + unsigned char sparecyl; + unsigned short pcylcount; + unsigned short head_vol0; + unsigned short ntrks; /* tracks in cyl 0 or vol 0 */ + unsigned char cmd_tag_queue_depth; + unsigned char unused0; + unsigned short unused1; + unsigned short nsect; /* sectors/tracks in cyl 0 or vol 0 */ + unsigned short bytes; + unsigned short ilfact; + unsigned int flags; /* controller flags */ + unsigned int datarate; + unsigned int retries_on_error; + unsigned int ms_per_word; + unsigned short xylogics_gap1; + unsigned short xylogics_syncdelay; + unsigned short xylogics_readdelay; + unsigned short xylogics_gap2; + unsigned short xylogics_readgate; + unsigned short xylogics_writecont; +}; + +#define SGI_VOLHDR 0x00 +/* 1 and 2 were used for drive types no longer supported by SGI */ +#define SGI_SWAP 0x03 +/* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */ +#define SGI_VOLUME 0x06 +#define SGI_EFS 0x07 +#define SGI_LVOL 0x08 +#define SGI_RLVOL 0x09 +#define SGI_XFS 0x0a +#define SGI_XFSLOG 0x0b +#define SGI_XLV 0x0c +#define SGI_XVM 0x0d +#define ENTIRE_DISK SGI_VOLUME +/* + * controller flags + */ +#define SECTOR_SLIP 0x01 +#define SECTOR_FWD 0x02 +#define TRACK_FWD 0x04 +#define TRACK_MULTIVOL 0x08 +#define IGNORE_ERRORS 0x10 +#define RESEEK 0x20 +#define ENABLE_CMDTAGQ 0x40 + +typedef struct { + unsigned int magic; /* expect SGI_LABEL_MAGIC */ + unsigned short boot_part; /* active boot partition */ + unsigned short swap_part; /* active swap partition */ + unsigned char boot_file[16]; /* name of the bootfile */ + struct device_parameter devparam; /* 1 * 48 bytes */ + struct volume_directory { /* 15 * 16 bytes */ + unsigned char vol_file_name[8]; /* a character array */ + unsigned int vol_file_start; /* number of logical block */ + unsigned int vol_file_size; /* number of bytes */ + } directory[15]; + struct sgi_partition { /* 16 * 12 bytes */ + unsigned int num_sectors; /* number of blocks */ + unsigned int start_sector; /* must be cylinder aligned */ + unsigned int id; + } partitions[16]; + unsigned int csum; + unsigned int fillbytes; +} sgi_partition; + +typedef struct { + unsigned int magic; /* looks like a magic number */ + unsigned int a2; + unsigned int a3; + unsigned int a4; + unsigned int b1; + unsigned short b2; + unsigned short b3; + unsigned int c[16]; + unsigned short d[3]; + unsigned char scsi_string[50]; + unsigned char serial[137]; + unsigned short check1816; + unsigned char installer[225]; +} sgiinfo; + +#define SGI_LABEL_MAGIC 0x0be5a941 +#define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b +#define SGI_INFO_MAGIC 0x00072959 +#define SGI_INFO_MAGIC_SWAPPED 0x59290700 +#define SGI_SSWAP16(x) (sgi_other_endian ? __swap16(x) \ + : (uint16_t)(x)) +#define SGI_SSWAP32(x) (sgi_other_endian ? __swap32(x) \ + : (uint32_t)(x)) + +#define sgilabel ((sgi_partition *)MBRbuffer) +#define sgiparam (sgilabel->devparam) + +typedef struct { + unsigned char info[128]; /* Informative text string */ + unsigned char spare0[14]; + struct sun_info { + unsigned char spare1; + unsigned char id; + unsigned char spare2; + unsigned char flags; + } infos[8]; + unsigned char spare1[246]; /* Boot information etc. */ + unsigned short rspeed; /* Disk rotational speed */ + unsigned short pcylcount; /* Physical cylinder count */ + unsigned short sparecyl; /* extra sects per cylinder */ + unsigned char spare2[4]; /* More magic... */ + unsigned short ilfact; /* Interleave factor */ + unsigned short ncyl; /* Data cylinder count */ + unsigned short nacyl; /* Alt. cylinder count */ + unsigned short ntrks; /* Tracks per cylinder */ + unsigned short nsect; /* Sectors per track */ + unsigned char spare3[4]; /* Even more magic... */ + struct sun_partition { + uint32_t start_cylinder; + uint32_t num_sectors; + } partitions[8]; + unsigned short magic; /* Magic number */ + unsigned short csum; /* Label xor'd checksum */ +} sun_partition; + + +#define SUN_LABEL_MAGIC 0xDABE +#define SUN_LABEL_MAGIC_SWAPPED 0xBEDA +#define sunlabel ((sun_partition *)MBRbuffer) +#define SUN_SSWAP16(x) (sun_other_endian ? __swap16(x) \ + : (uint16_t)(x)) +#define SUN_SSWAP32(x) (sun_other_endian ? __swap32(x) \ + : (uint32_t)(x)) + + +#ifdef CONFIG_FEATURE_OSF_LABEL +/* + Changes: + 19990319 - Arnaldo Carvalho de Melo - i18n/nls + + 20000101 - David Huggins-Daines - Better + support for OSF/1 disklabels on Alpha. + Also fixed unaligned accesses in alpha_bootblock_checksum() +*/ + +#define FREEBSD_PARTITION 0xa5 +#define NETBSD_PARTITION 0xa9 + +static void xbsd_delete_part (void); +static void xbsd_new_part (void); +static void xbsd_write_disklabel (void); +static int xbsd_create_disklabel (void); +static void xbsd_edit_disklabel (void); +static void xbsd_write_bootstrap (void); +static void xbsd_change_fstype (void); +static int xbsd_get_part_index (int max); +static int xbsd_check_new_partition (int *i); +static void xbsd_list_types (void); +static u_short xbsd_dkcksum (struct xbsd_disklabel *lp); +static int xbsd_initlabel (struct partition *p, struct xbsd_disklabel *d, + int pindex); +static int xbsd_readlabel (struct partition *p, struct xbsd_disklabel *d); +static int xbsd_writelabel (struct partition *p, struct xbsd_disklabel *d); + +#if defined (__alpha__) +static void alpha_bootblock_checksum (char *boot); +#endif + +#if !defined (__alpha__) +static int xbsd_translate_fstype (int linux_type); +static void xbsd_link_part (void); +static struct partition *xbsd_part; +static int xbsd_part_index; +#endif + +#if defined (__alpha__) +/* We access this through a uint64_t * when checksumming */ +static char disklabelbuffer[BSD_BBSIZE] __attribute__((aligned(8))); +#else +static char disklabelbuffer[BSD_BBSIZE]; +#endif + +static struct xbsd_disklabel xbsd_dlabel; + +#define bsd_cround(n) \ + (display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n)) + +/* + * Test whether the whole disk has BSD disk label magic. + * + * Note: often reformatting with DOS-type label leaves the BSD magic, + * so this does not mean that there is a BSD disk label. + */ +static int +check_osf_label(void) { + if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0) + return 0; + return 1; +} + +static void xbsd_print_disklabel(int); + +static int +btrydev (const char * dev) { + if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0) + return -1; + printf(_("\nBSD label for device: %s\n"), dev); + xbsd_print_disklabel (0); + return 0; +} + +static void +bmenu (void) { + puts (_("Command action")); + puts (_("\td\tdelete a BSD partition")); + puts (_("\te\tedit drive data")); + puts (_("\ti\tinstall bootstrap")); + puts (_("\tl\tlist known filesystem types")); + puts (_("\tm\tprint this menu")); + puts (_("\tn\tadd a new BSD partition")); + puts (_("\tp\tprint BSD partition table")); + puts (_("\tq\tquit without saving changes")); + puts (_("\tr\treturn to main menu")); + puts (_("\ts\tshow complete disklabel")); + puts (_("\tt\tchange a partition's filesystem id")); + puts (_("\tu\tchange units (cylinders/sectors)")); + puts (_("\tw\twrite disklabel to disk")); +#if !defined (__alpha__) + puts (_("\tx\tlink BSD partition to non-BSD partition")); +#endif +} + +#if !defined (__alpha__) +static int +hidden(int type) { + return type ^ 0x10; +} + +static int +is_bsd_partition_type(int type) { + return (type == FREEBSD_PARTITION || + type == hidden(FREEBSD_PARTITION) || + type == NETBSD_PARTITION || + type == hidden(NETBSD_PARTITION)); +} +#endif + +static void +bselect (void) { +#if !defined (__alpha__) + int t, ss; + struct partition *p; + + for (t=0; t<4; t++) { + p = get_part_table(t); + if (p && is_bsd_partition_type(p->sys_ind)) { + xbsd_part = p; + xbsd_part_index = t; + ss = get_start_sect(xbsd_part); + if (ss == 0) { + fprintf (stderr, _("Partition %s has invalid starting sector 0.\n"), + partname(disk_device, t+1, 0)); + return; + } + printf (_("Reading disklabel of %s at sector %d.\n"), + partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR); + if (xbsd_readlabel (xbsd_part, &xbsd_dlabel) == 0) + if (xbsd_create_disklabel () == 0) + return; + break; + } + } + + if (t == 4) { + printf (_("There is no *BSD partition on %s.\n"), disk_device); + return; + } + +#elif defined (__alpha__) + + if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0) + if (xbsd_create_disklabel () == 0) + exit ( EXIT_SUCCESS ); + +#endif + + while (1) { + putchar ('\n'); + switch (tolower (read_char (_("BSD disklabel command (m for help): ")))) { + case 'd': + xbsd_delete_part (); + break; + case 'e': + xbsd_edit_disklabel (); + break; + case 'i': + xbsd_write_bootstrap (); + break; + case 'l': + xbsd_list_types (); + break; + case 'n': + xbsd_new_part (); + break; + case 'p': + xbsd_print_disklabel (0); + break; + case 'q': + close (fd); + exit ( EXIT_SUCCESS ); + case 'r': + return; + case 's': + xbsd_print_disklabel (1); + break; + case 't': + xbsd_change_fstype (); + break; + case 'u': + change_units(); + break; + case 'w': + xbsd_write_disklabel (); + break; +#if !defined (__alpha__) + case 'x': + xbsd_link_part (); + break; +#endif + default: + bmenu (); + break; + } + } +} + +static void +xbsd_delete_part (void) +{ + int i; + + i = xbsd_get_part_index (xbsd_dlabel.d_npartitions); + xbsd_dlabel.d_partitions[i].p_size = 0; + xbsd_dlabel.d_partitions[i].p_offset = 0; + xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED; + if (xbsd_dlabel.d_npartitions == i + 1) + while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0) + xbsd_dlabel.d_npartitions--; +} + +static void +xbsd_new_part (void) +{ + off_t begin, end; + char mesg[256]; + int i; + + if (!xbsd_check_new_partition (&i)) + return; + +#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__) + begin = get_start_sect(xbsd_part); + end = begin + get_nr_sects(xbsd_part) - 1; +#else + begin = 0; + end = xbsd_dlabel.d_secperunit - 1; +#endif + + snprintf (mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); + begin = read_int (bsd_cround (begin), bsd_cround (begin), bsd_cround (end), + 0, mesg); + + if (display_in_cyl_units) + begin = (begin - 1) * xbsd_dlabel.d_secpercyl; + + snprintf (mesg, sizeof(mesg), _("Last %s or +size or +sizeM or +sizeK"), + str_units(SINGULAR)); + end = read_int (bsd_cround (begin), bsd_cround (end), bsd_cround (end), + bsd_cround (begin), mesg); + + if (display_in_cyl_units) + end = end * xbsd_dlabel.d_secpercyl - 1; + + xbsd_dlabel.d_partitions[i].p_size = end - begin + 1; + xbsd_dlabel.d_partitions[i].p_offset = begin; + xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED; +} + +static void +xbsd_print_disklabel (int show_all) { + struct xbsd_disklabel *lp = &xbsd_dlabel; + struct xbsd_partition *pp; + int i, j; + + if (show_all) { +#if defined (__alpha__) + printf("# %s:\n", disk_device); +#else + printf("# %s:\n", partname(disk_device, xbsd_part_index+1, 0)); +#endif + if ((unsigned) lp->d_type < BSD_DKMAXTYPES) + printf(_("type: %s\n"), xbsd_dktypenames[lp->d_type]); + else + printf(_("type: %d\n"), lp->d_type); + printf(_("disk: %.*s\n"), (int) sizeof(lp->d_typename), lp->d_typename); + printf(_("label: %.*s\n"), (int) sizeof(lp->d_packname), lp->d_packname); + printf(_("flags:")); + if (lp->d_flags & BSD_D_REMOVABLE) + printf(_(" removable")); + if (lp->d_flags & BSD_D_ECC) + printf(_(" ecc")); + if (lp->d_flags & BSD_D_BADSECT) + printf(_(" badsect")); + printf("\n"); + /* On various machines the fields of *lp are short/int/long */ + /* In order to avoid problems, we cast them all to long. */ + printf(_("bytes/sector: %ld\n"), (long) lp->d_secsize); + printf(_("sectors/track: %ld\n"), (long) lp->d_nsectors); + printf(_("tracks/cylinder: %ld\n"), (long) lp->d_ntracks); + printf(_("sectors/cylinder: %ld\n"), (long) lp->d_secpercyl); + printf(_("cylinders: %ld\n"), (long) lp->d_ncylinders); + printf(_("rpm: %d\n"), lp->d_rpm); + printf(_("interleave: %d\n"), lp->d_interleave); + printf(_("trackskew: %d\n"), lp->d_trackskew); + printf(_("cylinderskew: %d\n"), lp->d_cylskew); + printf(_("headswitch: %ld\t\t# milliseconds\n"), + (long) lp->d_headswitch); + printf(_("track-to-track seek: %ld\t# milliseconds\n"), + (long) lp->d_trkseek); + printf(_("drivedata: ")); + for (i = NDDATA - 1; i >= 0; i--) + if (lp->d_drivedata[i]) + break; + if (i < 0) + i = 0; + for (j = 0; j <= i; j++) + printf("%ld ", (long) lp->d_drivedata[j]); + } + printf(_("\n%d partitions:\n"), lp->d_npartitions); + printf(_("# start end size fstype [fsize bsize cpg]\n")); + pp = lp->d_partitions; + for (i = 0; i < lp->d_npartitions; i++, pp++) { + if (pp->p_size) { + if (display_in_cyl_units && lp->d_secpercyl) { + printf(" %c: %8ld%c %8ld%c %8ld%c ", + 'a' + i, + (long) pp->p_offset / lp->d_secpercyl + 1, + (pp->p_offset % lp->d_secpercyl) ? '*' : ' ', + (long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1) + / lp->d_secpercyl, + ((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ', + (long) pp->p_size / lp->d_secpercyl, + (pp->p_size % lp->d_secpercyl) ? '*' : ' '); + } else { + printf(" %c: %8ld %8ld %8ld ", + 'a' + i, + (long) pp->p_offset, + (long) pp->p_offset + pp->p_size - 1, + (long) pp->p_size); + } + if ((unsigned) pp->p_fstype < BSD_FSMAXTYPES) + printf("%8.8s", xbsd_fstypes[pp->p_fstype].name); + else + printf("%8x", pp->p_fstype); + switch (pp->p_fstype) { + case BSD_FS_UNUSED: + printf(" %5ld %5ld %5.5s ", + (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, ""); + break; + + case BSD_FS_BSDFFS: + printf(" %5ld %5ld %5d ", + (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, + pp->p_cpg); + break; + + default: + printf("%22.22s", ""); + break; + } + printf("\n"); + } + } +} + +static void +xbsd_write_disklabel (void) { +#if defined (__alpha__) + printf (_("Writing disklabel to %s.\n"), disk_device); + xbsd_writelabel (NULL, &xbsd_dlabel); +#else + printf (_("Writing disklabel to %s.\n"), + partname(disk_device, xbsd_part_index+1, 0)); + xbsd_writelabel (xbsd_part, &xbsd_dlabel); +#endif + reread_partition_table(0); /* no exit yet */ +} + +static int +xbsd_create_disklabel (void) { + char c; + +#if defined (__alpha__) + fprintf (stderr, _("%s contains no disklabel.\n"), disk_device); +#else + fprintf (stderr, _("%s contains no disklabel.\n"), + partname(disk_device, xbsd_part_index+1, 0)); +#endif + + while (1) { + c = read_char (_("Do you want to create a disklabel? (y/n) ")); + if (c == 'y' || c == 'Y') { + if (xbsd_initlabel ( +#if defined (__alpha__) || defined (__powerpc__) || defined (__hppa__) || \ + defined (__s390__) || defined (__s390x__) + NULL, &xbsd_dlabel, 0 +#else + xbsd_part, &xbsd_dlabel, xbsd_part_index +#endif + ) == 1) { + xbsd_print_disklabel (1); + return 1; + } else + return 0; + } else if (c == 'n') + return 0; + } +} + +static int +edit_int (int def, char *mesg) +{ + do { + fputs (mesg, stdout); + printf (" (%d): ", def); + if (!read_line ()) + return def; + } + while (!isdigit (*line_ptr)); + return atoi (line_ptr); +} + +static void +xbsd_edit_disklabel (void) +{ + struct xbsd_disklabel *d; + + d = &xbsd_dlabel; + +#if defined (__alpha__) || defined (__ia64__) + d -> d_secsize = (u_long) edit_int ((u_long) d -> d_secsize ,_("bytes/sector")); + d -> d_nsectors = (u_long) edit_int ((u_long) d -> d_nsectors ,_("sectors/track")); + d -> d_ntracks = (u_long) edit_int ((u_long) d -> d_ntracks ,_("tracks/cylinder")); + d -> d_ncylinders = (u_long) edit_int ((u_long) d -> d_ncylinders ,_("cylinders")); +#endif + + /* d -> d_secpercyl can be != d -> d_nsectors * d -> d_ntracks */ + while (1) + { + d -> d_secpercyl = (u_long) edit_int ((u_long) d -> d_nsectors * d -> d_ntracks, + _("sectors/cylinder")); + if (d -> d_secpercyl <= d -> d_nsectors * d -> d_ntracks) + break; + + printf (_("Must be <= sectors/track * tracks/cylinder (default).\n")); + } + d -> d_rpm = (u_short) edit_int ((u_short) d -> d_rpm ,_("rpm")); + d -> d_interleave = (u_short) edit_int ((u_short) d -> d_interleave,_("interleave")); + d -> d_trackskew = (u_short) edit_int ((u_short) d -> d_trackskew ,_("trackskew")); + d -> d_cylskew = (u_short) edit_int ((u_short) d -> d_cylskew ,_("cylinderskew")); + d -> d_headswitch = (u_long) edit_int ((u_long) d -> d_headswitch ,_("headswitch")); + d -> d_trkseek = (u_long) edit_int ((u_long) d -> d_trkseek ,_("track-to-track seek")); + + d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders; +} + +static int +xbsd_get_bootstrap (char *path, void *ptr, int size) +{ + int fdb; + + if ((fdb = open (path, O_RDONLY)) < 0) + { + perror (path); + return 0; + } + if (read (fdb, ptr, size) < 0) + { + perror (path); + close (fdb); + return 0; + } + printf (" ... %s\n", path); + close (fdb); + return 1; +} + +static void +sync_disks (void) +{ + printf (_("\nSyncing disks.\n")); + sync (); + sleep (4); +} + +static void +xbsd_write_bootstrap (void) +{ + char *bootdir = BSD_LINUX_BOOTDIR; + char path[MAXPATHLEN]; + char *dkbasename; + struct xbsd_disklabel dl; + char *d, *p, *e; + int sector; + + if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI) + dkbasename = "sd"; + else + dkbasename = "wd"; + + printf (_("Bootstrap: %sboot -> boot%s (%s): "), + dkbasename, dkbasename, dkbasename); + if (read_line ()) { + line_ptr[strlen (line_ptr)-1] = '\0'; + dkbasename = line_ptr; + } + snprintf (path, sizeof(path), "%s/%sboot", bootdir, dkbasename); + if (!xbsd_get_bootstrap (path, disklabelbuffer, (int) xbsd_dlabel.d_secsize)) + return; + + /* We need a backup of the disklabel (xbsd_dlabel might have changed). */ + d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE]; + bcopy (d, &dl, sizeof (struct xbsd_disklabel)); + + /* The disklabel will be overwritten by 0's from bootxx anyway */ + bzero (d, sizeof (struct xbsd_disklabel)); + + snprintf (path, sizeof(path), "%s/boot%s", bootdir, dkbasename); + if (!xbsd_get_bootstrap (path, &disklabelbuffer[xbsd_dlabel.d_secsize], + (int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize)) + return; + + e = d + sizeof (struct xbsd_disklabel); + for (p=d; p < e; p++) + if (*p) { + fprintf (stderr, _("Bootstrap overlaps with disk label!\n")); + exit ( EXIT_FAILURE ); + } + + bcopy (&dl, d, sizeof (struct xbsd_disklabel)); + +#if defined (__powerpc__) || defined (__hppa__) + sector = 0; +#elif defined (__alpha__) + sector = 0; + alpha_bootblock_checksum (disklabelbuffer); +#else + sector = get_start_sect(xbsd_part); +#endif + + if (lseek (fd, sector * SECTOR_SIZE, SEEK_SET) == -1) + fdisk_fatal (unable_to_seek); + if (BSD_BBSIZE != write (fd, disklabelbuffer, BSD_BBSIZE)) + fdisk_fatal (unable_to_write); + +#if defined (__alpha__) + printf (_("Bootstrap installed on %s.\n"), disk_device); +#else + printf (_("Bootstrap installed on %s.\n"), + partname (disk_device, xbsd_part_index+1, 0)); +#endif + + sync_disks (); +} + +static void +xbsd_change_fstype (void) +{ + int i; + + i = xbsd_get_part_index (xbsd_dlabel.d_npartitions); + xbsd_dlabel.d_partitions[i].p_fstype = read_hex (xbsd_fstypes); +} + +static int +xbsd_get_part_index (int max) +{ + char prompt[256]; + char l; + + snprintf (prompt, sizeof(prompt), _("Partition (a-%c): "), 'a' + max - 1); + do + l = tolower (read_char (prompt)); + while (l < 'a' || l > 'a' + max - 1); + return l - 'a'; +} + +static int +xbsd_check_new_partition (int *i) { + + /* room for more? various BSD flavours have different maxima */ + if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) { + int t; + + for (t = 0; t < BSD_MAXPARTITIONS; t++) + if (xbsd_dlabel.d_partitions[t].p_size == 0) + break; + + if (t == BSD_MAXPARTITIONS) { + fprintf (stderr, _("The maximum number of partitions " + "has been created\n")); + return 0; + } + } + + *i = xbsd_get_part_index (BSD_MAXPARTITIONS); + + if (*i >= xbsd_dlabel.d_npartitions) + xbsd_dlabel.d_npartitions = (*i) + 1; + + if (xbsd_dlabel.d_partitions[*i].p_size != 0) { + fprintf (stderr, _("This partition already exists.\n")); + return 0; + } + + return 1; +} + +static void +xbsd_list_types (void) { + list_types (xbsd_fstypes); +} + +static u_short +xbsd_dkcksum (struct xbsd_disklabel *lp) { + u_short *start, *end; + u_short sum = 0; + + start = (u_short *) lp; + end = (u_short *) &lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return sum; +} + +static int +xbsd_initlabel (struct partition *p, struct xbsd_disklabel *d, int pindex) { + struct xbsd_partition *pp; + + get_geometry (); + bzero (d, sizeof (struct xbsd_disklabel)); + + d -> d_magic = BSD_DISKMAGIC; + + if (strncmp (disk_device, "/dev/sd", 7) == 0) + d -> d_type = BSD_DTYPE_SCSI; + else + d -> d_type = BSD_DTYPE_ST506; + +#if 0 /* not used (at least not written to disk) by NetBSD/i386 1.0 */ + d -> d_subtype = BSD_DSTYPE_INDOSPART & pindex; +#endif + +#if !defined (__alpha__) + d -> d_flags = BSD_D_DOSPART; +#else + d -> d_flags = 0; +#endif + d -> d_secsize = SECTOR_SIZE; /* bytes/sector */ + d -> d_nsectors = sectors; /* sectors/track */ + d -> d_ntracks = heads; /* tracks/cylinder (heads) */ + d -> d_ncylinders = cylinders; + d -> d_secpercyl = sectors * heads;/* sectors/cylinder */ + if (d -> d_secpercyl == 0) + d -> d_secpercyl = 1; /* avoid segfaults */ + d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders; + + d -> d_rpm = 3600; + d -> d_interleave = 1; + d -> d_trackskew = 0; + d -> d_cylskew = 0; + d -> d_headswitch = 0; + d -> d_trkseek = 0; + + d -> d_magic2 = BSD_DISKMAGIC; + d -> d_bbsize = BSD_BBSIZE; + d -> d_sbsize = BSD_SBSIZE; + +#if !defined (__alpha__) + d -> d_npartitions = 4; + pp = &d -> d_partitions[2]; /* Partition C should be + the NetBSD partition */ + pp -> p_offset = get_start_sect(p); + pp -> p_size = get_nr_sects(p); + pp -> p_fstype = BSD_FS_UNUSED; + pp = &d -> d_partitions[3]; /* Partition D should be + the whole disk */ + pp -> p_offset = 0; + pp -> p_size = d -> d_secperunit; + pp -> p_fstype = BSD_FS_UNUSED; +#elif defined (__alpha__) + d -> d_npartitions = 3; + pp = &d -> d_partitions[2]; /* Partition C should be + the whole disk */ + pp -> p_offset = 0; + pp -> p_size = d -> d_secperunit; + pp -> p_fstype = BSD_FS_UNUSED; +#endif + + return 1; +} + +/* + * Read a xbsd_disklabel from sector 0 or from the starting sector of p. + * If it has the right magic, return 1. + */ +static int +xbsd_readlabel (struct partition *p, struct xbsd_disklabel *d) +{ + int t, sector; + + /* p is used only to get the starting sector */ +#if !defined (__alpha__) + sector = (p ? get_start_sect(p) : 0); +#elif defined (__alpha__) + sector = 0; +#endif + + if (lseek (fd, sector * SECTOR_SIZE, SEEK_SET) == -1) + fdisk_fatal (unable_to_seek); + if (BSD_BBSIZE != read (fd, disklabelbuffer, BSD_BBSIZE)) + fdisk_fatal (unable_to_read); + + bcopy (&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], + d, sizeof (struct xbsd_disklabel)); + + if (d -> d_magic != BSD_DISKMAGIC || d -> d_magic2 != BSD_DISKMAGIC) + return 0; + + for (t = d -> d_npartitions; t < BSD_MAXPARTITIONS; t++) { + d -> d_partitions[t].p_size = 0; + d -> d_partitions[t].p_offset = 0; + d -> d_partitions[t].p_fstype = BSD_FS_UNUSED; + } + + if (d -> d_npartitions > BSD_MAXPARTITIONS) + fprintf (stderr, _("Warning: too many partitions " + "(%d, maximum is %d).\n"), + d -> d_npartitions, BSD_MAXPARTITIONS); + return 1; +} + +static int +xbsd_writelabel (struct partition *p, struct xbsd_disklabel *d) +{ + unsigned int sector; + +#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__) + sector = get_start_sect(p) + BSD_LABELSECTOR; +#else + sector = BSD_LABELSECTOR; +#endif + + d -> d_checksum = 0; + d -> d_checksum = xbsd_dkcksum (d); + + /* This is necessary if we want to write the bootstrap later, + otherwise we'd write the old disklabel with the bootstrap. + */ + bcopy (d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], + sizeof (struct xbsd_disklabel)); + +#if defined (__alpha__) && BSD_LABELSECTOR == 0 + alpha_bootblock_checksum (disklabelbuffer); + if (lseek (fd, 0, SEEK_SET) == -1) + fdisk_fatal (unable_to_seek); + if (BSD_BBSIZE != write (fd, disklabelbuffer, BSD_BBSIZE)) + fdisk_fatal (unable_to_write); +#else + if (lseek (fd, sector * SECTOR_SIZE + BSD_LABELOFFSET, + SEEK_SET) == -1) + fdisk_fatal (unable_to_seek); + if (sizeof (struct xbsd_disklabel) != write (fd, d, sizeof (struct xbsd_disklabel))) + fdisk_fatal (unable_to_write); +#endif + + sync_disks (); + + return 1; +} + + +#if !defined (__alpha__) +static int +xbsd_translate_fstype (int linux_type) +{ + switch (linux_type) + { + case 0x01: /* DOS 12-bit FAT */ + case 0x04: /* DOS 16-bit <32M */ + case 0x06: /* DOS 16-bit >=32M */ + case 0xe1: /* DOS access */ + case 0xe3: /* DOS R/O */ + case 0xf2: /* DOS secondary */ + return BSD_FS_MSDOS; + case 0x07: /* OS/2 HPFS */ + return BSD_FS_HPFS; + default: + return BSD_FS_OTHER; + } +} + +static void +xbsd_link_part (void) +{ + int k, i; + struct partition *p; + + k = get_partition (1, partitions); + + if (!xbsd_check_new_partition (&i)) + return; + + p = get_part_table(k); + + xbsd_dlabel.d_partitions[i].p_size = get_nr_sects(p); + xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p); + xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind); +} +#endif + +#if defined (__alpha__) + +#if !defined(__GLIBC__) +typedef unsigned long long uint64_t; +#endif + +static void +alpha_bootblock_checksum (char *boot) +{ + uint64_t *dp, sum; + int i; + + dp = (uint64_t *)boot; + sum = 0; + for (i = 0; i < 63; i++) + sum += dp[i]; + dp[63] = sum; +} +#endif /* __alpha__ */ + +#endif /* OSF_LABEL */ + +#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_SUN_LABEL) +static inline unsigned short +__swap16(unsigned short x) { + return (((uint16_t)(x) & 0xFF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8); +} + +static inline uint32_t +__swap32(uint32_t x) { + return (((x & 0xFF) << 24) | + ((x & 0xFF00) << 8) | + ((x & 0xFF0000) >> 8) | + ((x & 0xFF000000) >> 24)); +} +#endif + +#ifdef CONFIG_FEATURE_SGI_LABEL +/* + * + * fdisksgilabel.c + * + * Copyright (C) Andreas Neuper, Sep 1998. + * This file may be modified and redistributed under + * the terms of the GNU Public License. + * + * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo + * Internationalization + */ + + +static int sgi_other_endian; +static int debug; +static short sgi_volumes=1; + +/* + * only dealing with free blocks here + */ + +typedef struct { unsigned int first; unsigned int last; } freeblocks; +static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */ + +static void +setfreelist(int i, unsigned int f, unsigned int l) { + freelist[i].first = f; + freelist[i].last = l; +} + +static void +add2freelist(unsigned int f, unsigned int l) { + int i = 0; + for ( ; i < 17 ; i++) + if (freelist[i].last == 0) + break; + setfreelist(i, f, l); +} + +static void +clearfreelist(void) { + int i; + + for (i = 0; i < 17 ; i++) + setfreelist(i, 0, 0); +} + +static unsigned int +isinfreelist(unsigned int b) { + int i; + + for (i = 0; i < 17 ; i++) + if (freelist[i].first <= b && freelist[i].last >= b) + return freelist[i].last; + return 0; +} + /* return last vacant block of this stride (never 0). */ + /* the '>=' is not quite correct, but simplifies the code */ +/* + * end of free blocks section + */ + +static const struct systypes sgi_sys_types[] = { +/* SGI_VOLHDR */ {"\x00" "SGI volhdr" }, +/* 0x01 */ {"\x01" "SGI trkrepl" }, +/* 0x02 */ {"\x02" "SGI secrepl" }, +/* SGI_SWAP */ {"\x03" "SGI raw" }, +/* 0x04 */ {"\x04" "SGI bsd" }, +/* 0x05 */ {"\x05" "SGI sysv" }, +/* ENTIRE_DISK */ {"\x06" "SGI volume" }, +/* SGI_EFS */ {"\x07" "SGI efs" }, +/* 0x08 */ {"\x08" "SGI lvol" }, +/* 0x09 */ {"\x09" "SGI rlvol" }, +/* SGI_XFS */ {"\x0a" "SGI xfs" }, +/* SGI_XFSLOG */ {"\x0b" "SGI xfslog" }, +/* SGI_XLV */ {"\x0c" "SGI xlv" }, +/* SGI_XVM */ {"\x0d" "SGI xvm" }, +/* LINUX_SWAP */ {"\x82" "Linux swap" }, +/* LINUX_NATIVE */ {"\x83" "Linux native" }, +/* LINUX_LVM */ {"\x8d" "Linux LVM" }, +/* LINUX_RAID */ {"\xfd" "Linux RAID" }, + { NULL } +}; + + +static int +sgi_get_nsect(void) { + return SGI_SSWAP16(sgilabel->devparam.nsect); +} + +static int +sgi_get_ntrks(void) { + return SGI_SSWAP16(sgilabel->devparam.ntrks); +} + +static void +sgi_nolabel(void) { + sgilabel->magic = 0; + sgi_label = 0; + partitions = 4; +} + +static unsigned int +two_s_complement_32bit_sum(unsigned int* base, int size /* in bytes */) { + int i=0; + unsigned int sum=0; + + size /= sizeof(unsigned int); + for (i = 0; i < size; i++) + sum -= SGI_SSWAP32(base[i]); + return sum; +} + +static int +check_sgi_label(void) { + if (sizeof(sgilabel) > 512) { + fprintf(stderr, + _("According to MIPS Computer Systems, Inc the " + "Label must not contain more than 512 bytes\n")); + exit(1); + } + + if (sgilabel->magic != SGI_LABEL_MAGIC && + sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED) { + sgi_label = 0; + sgi_other_endian = 0; + return 0; + } + + sgi_other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED); + /* + * test for correct checksum + */ + if (two_s_complement_32bit_sum((unsigned int*)sgilabel, + sizeof(*sgilabel))) { + fprintf(stderr, + _("Detected sgi disklabel with wrong checksum.\n")); + } + update_units(); + sgi_label = 1; + partitions= 16; + sgi_volumes = 15; + return 1; +} + +static unsigned int +sgi_get_start_sector(int i) { + return SGI_SSWAP32(sgilabel->partitions[i].start_sector); +} + +static unsigned int +sgi_get_num_sectors(int i) { + return SGI_SSWAP32(sgilabel->partitions[i].num_sectors); +} + +static int +sgi_get_sysid(int i) +{ + return SGI_SSWAP32(sgilabel->partitions[i].id); +} + +static int +sgi_get_bootpartition(void) +{ + return SGI_SSWAP16(sgilabel->boot_part); +} + +static int +sgi_get_swappartition(void) +{ + return SGI_SSWAP16(sgilabel->swap_part); +} + +static void +sgi_list_table(int xtra) { + int i, w, wd; + int kpi = 0; /* kernel partition ID */ + + if(xtra) { + printf(_("\nDisk %s (SGI disk label): %d heads, %d sectors\n" + "%d cylinders, %d physical cylinders\n" + "%d extra sects/cyl, interleave %d:1\n" + "%s\n" + "Units = %s of %d * 512 bytes\n\n"), + disk_device, heads, sectors, cylinders, + SGI_SSWAP16(sgiparam.pcylcount), + SGI_SSWAP16(sgiparam.sparecyl), + SGI_SSWAP16(sgiparam.ilfact), + (char *)sgilabel, + str_units(PLURAL), units_per_sector); + } else { + printf( _("\nDisk %s (SGI disk label): " + "%d heads, %d sectors, %d cylinders\n" + "Units = %s of %d * 512 bytes\n\n"), + disk_device, heads, sectors, cylinders, + str_units(PLURAL), units_per_sector ); + } + + w = strlen(disk_device); + wd = strlen(_("Device")); + if (w < wd) + w = wd; + + printf(_("----- partitions -----\n" + "Pt# %*s Info Start End Sectors Id System\n"), + w + 2, _("Device")); + for (i = 0 ; i < partitions; i++) { + if( sgi_get_num_sectors(i) || debug ) { + uint32_t start = sgi_get_start_sector(i); + uint32_t len = sgi_get_num_sectors(i); + kpi++; /* only count nonempty partitions */ + printf( + "%2d: %s %4s %9ld %9ld %9ld %2x %s\n", +/* fdisk part number */ i+1, +/* device */ partname(disk_device, kpi, w+3), +/* flags */ (sgi_get_swappartition() == i) ? "swap" : +/* flags */ (sgi_get_bootpartition() == i) ? "boot" : " ", +/* start */ (long) scround(start), +/* end */ (long) scround(start+len)-1, +/* no odd flag on end */ (long) len, +/* type id */ sgi_get_sysid(i), +/* type name */ partition_type(sgi_get_sysid(i))); + } + } + printf(_("----- Bootinfo -----\nBootfile: %s\n" + "----- Directory Entries -----\n"), + sgilabel->boot_file); + for (i = 0 ; i < sgi_volumes; i++) { + if (sgilabel->directory[i].vol_file_size) { + uint32_t start = SGI_SSWAP32(sgilabel->directory[i].vol_file_start); + uint32_t len = SGI_SSWAP32(sgilabel->directory[i].vol_file_size); + char*name = sgilabel->directory[i].vol_file_name; + + printf(_("%2d: %-10s sector%5u size%8u\n"), + i, name, (unsigned int) start, (unsigned int) len); + } + } +} + +static void +sgi_set_bootpartition( int i ) +{ + sgilabel->boot_part = SGI_SSWAP16(((short)i)); +} + +static unsigned int +sgi_get_lastblock(void) { + return heads * sectors * cylinders; +} + +static void +sgi_set_swappartition( int i ) { + sgilabel->swap_part = SGI_SSWAP16(((short)i)); +} + +static int +sgi_check_bootfile(const char* aFile) { + + if (strlen(aFile) < 3) /* "/a\n" is minimum */ { + printf(_("\nInvalid Bootfile!\n" + "\tThe bootfile must be an absolute non-zero pathname,\n" + "\te.g. \"/unix\" or \"/unix.save\".\n")); + return 0; + } else { + if (strlen(aFile) > 16) { + printf(_("\n\tName of Bootfile too long: " + "16 bytes maximum.\n")); + return 0; + } else { + if (aFile[0] != '/') { + printf(_("\n\tBootfile must have a " + "fully qualified pathname.\n")); + return 0; + } + } + } + if (strncmp(aFile, sgilabel->boot_file, 16)) { + printf(_("\n\tBe aware, that the bootfile is not checked for existence.\n\t" + "SGI's default is \"/unix\" and for backup \"/unix.save\".\n")); + /* filename is correct and did change */ + return 1; + } + return 0; /* filename did not change */ +} + +static const char * +sgi_get_bootfile(void) { + return sgilabel->boot_file; +} + +static void +sgi_set_bootfile(const char* aFile) { + int i = 0; + + if (sgi_check_bootfile(aFile)) { + while (i < 16) { + if ((aFile[i] != '\n') /* in principle caught again by next line */ + && (strlen(aFile) > i)) + sgilabel->boot_file[i] = aFile[i]; + else + sgilabel->boot_file[i] = 0; + i++; + } + printf(_("\n\tBootfile is changed to \"%s\".\n"), sgilabel->boot_file); + } +} + +static void +create_sgiinfo(void) +{ + /* I keep SGI's habit to write the sgilabel to the second block */ + sgilabel->directory[0].vol_file_start = SGI_SSWAP32(2); + sgilabel->directory[0].vol_file_size = SGI_SSWAP32(sizeof(sgiinfo)); + strncpy(sgilabel->directory[0].vol_file_name, "sgilabel", 8); +} + +static sgiinfo *fill_sgiinfo(void); + +static void +sgi_write_table(void) { + sgilabel->csum = 0; + sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum( + (unsigned int*)sgilabel, + sizeof(*sgilabel))); + assert(two_s_complement_32bit_sum( + (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0); + if (lseek(fd, 0, SEEK_SET) < 0) + fdisk_fatal(unable_to_seek); + if (write(fd, sgilabel, SECTOR_SIZE) != SECTOR_SIZE) + fdisk_fatal(unable_to_write); + if (! strncmp(sgilabel->directory[0].vol_file_name, "sgilabel", 8)) { + /* + * keep this habit of first writing the "sgilabel". + * I never tested whether it works without (AN 981002). + */ + sgiinfo *info = fill_sgiinfo(); + int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start); + if (lseek(fd, infostartblock*SECTOR_SIZE, SEEK_SET) < 0) + fdisk_fatal(unable_to_seek); + if (write(fd, info, SECTOR_SIZE) != SECTOR_SIZE) + fdisk_fatal(unable_to_write); + free(info); + } +} + +static int +compare_start(int *x, int *y) { + /* + * sort according to start sectors + * and prefers largest partition: + * entry zero is entire disk entry + */ + unsigned int i = *x; + unsigned int j = *y; + unsigned int a = sgi_get_start_sector(i); + unsigned int b = sgi_get_start_sector(j); + unsigned int c = sgi_get_num_sectors(i); + unsigned int d = sgi_get_num_sectors(j); + + if (a == b) + return (d > c) ? 1 : (d == c) ? 0 : -1; + return (a > b) ? 1 : -1; +} + + +static int +verify_sgi(int verbose) +{ + int Index[16]; /* list of valid partitions */ + int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */ + int entire = 0, i = 0; + unsigned int start = 0; + long long gap = 0; /* count unused blocks */ + unsigned int lastblock = sgi_get_lastblock(); + + clearfreelist(); + for (i=0; i<16; i++) { + if (sgi_get_num_sectors(i) != 0) { + Index[sortcount++]=i; + if (sgi_get_sysid(i) == ENTIRE_DISK) { + if (entire++ == 1) { + if (verbose) + printf(_("More than one entire disk entry present.\n")); + } + } + } + } + if (sortcount == 0) { + if (verbose) + printf(_("No partitions defined\n")); + return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1; + } + qsort(Index, sortcount, sizeof(Index[0]), (void*)compare_start); + if (sgi_get_sysid(Index[0]) == ENTIRE_DISK) { + if ((Index[0] != 10) && verbose) + printf(_("IRIX likes when Partition 11 covers the entire disk.\n")); + if ((sgi_get_start_sector(Index[0]) != 0) && verbose) + printf(_("The entire disk partition should start " + "at block 0,\n" + "not at diskblock %d.\n"), + sgi_get_start_sector(Index[0])); + if (debug) /* I do not understand how some disks fulfil it */ + if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose) + printf(_("The entire disk partition is only %d diskblock large,\n" + "but the disk is %d diskblocks long.\n"), + sgi_get_num_sectors(Index[0]), lastblock); + lastblock = sgi_get_num_sectors(Index[0]); + } else { + if (verbose) + printf(_("One Partition (#11) should cover the entire disk.\n")); + if (debug>2) + printf("sysid=%d\tpartition=%d\n", + sgi_get_sysid(Index[0]), Index[0]+1); + } + for (i=1, start=0; i sgi_get_start_sector(Index[i])) { + if (verbose) + printf(_("The Partition %d and %d overlap by %d sectors.\n"), + Index[i-1]+1, Index[i]+1, + start - sgi_get_start_sector(Index[i])); + if (gap > 0) gap = -gap; + if (gap == 0) gap = -1; + } + if (start < sgi_get_start_sector(Index[i])) { + if (verbose) + printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"), + sgi_get_start_sector(Index[i]) - start, + start, sgi_get_start_sector(Index[i])-1); + gap += sgi_get_start_sector(Index[i]) - start; + add2freelist(start, sgi_get_start_sector(Index[i])); + } + start = sgi_get_start_sector(Index[i]) + + sgi_get_num_sectors(Index[i]); + if (debug > 1) { + if (verbose) + printf("%2d:%12d\t%12d\t%12d\n", Index[i], + sgi_get_start_sector(Index[i]), + sgi_get_num_sectors(Index[i]), + sgi_get_sysid(Index[i])); + } + } + if (start < lastblock) { + if (verbose) + printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"), + lastblock - start, start, lastblock-1); + gap += lastblock - start; + add2freelist(start, lastblock); + } + /* + * Done with arithmetics + * Go for details now + */ + if (verbose) { + if (!sgi_get_num_sectors(sgi_get_bootpartition())) { + printf(_("\nThe boot partition does not exist.\n")); + } + if (!sgi_get_num_sectors(sgi_get_swappartition())) { + printf(_("\nThe swap partition does not exist.\n")); + } else { + if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP) + && (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP)) + printf(_("\nThe swap partition has no swap type.\n")); + } + if (sgi_check_bootfile("/unix")) + printf(_("\tYou have chosen an unusual boot file name.\n")); + } + return (gap > 0) ? 1 : (gap == 0) ? 0 : -1; +} + +static int +sgi_gaps(void) { + /* + * returned value is: + * = 0 : disk is properly filled to the rim + * < 0 : there is an overlap + * > 0 : there is still some vacant space + */ + return verify_sgi(0); +} + +static void +sgi_change_sysid( int i, int sys ) +{ + if( sgi_get_num_sectors(i) == 0 ) /* caught already before, ... */ + { + printf(_("Sorry You may change the Tag of non-empty partitions.\n")); + return; + } + if( ((sys != ENTIRE_DISK ) && (sys != SGI_VOLHDR)) + && (sgi_get_start_sector(i)<1) ) + { + read_chars( + _("It is highly recommended that the partition at offset 0\n" + "is of type \"SGI volhdr\", the IRIX system will rely on it to\n" + "retrieve from its directory standalone tools like sash and fx.\n" + "Only the \"SGI volume\" entire disk section may violate this.\n" + "Type YES if you are sure about tagging this partition differently.\n")); + if (strcmp (line_ptr, _("YES\n"))) + return; + } + sgilabel->partitions[i].id = SGI_SSWAP32(sys); +} + +/* returns partition index of first entry marked as entire disk */ +static int +sgi_entire(void) { + int i; + + for(i=0; i<16; i++) + if(sgi_get_sysid(i) == SGI_VOLUME) + return i; + return -1; +} + +static void +sgi_set_partition(int i, unsigned int start, unsigned int length, int sys) { + + sgilabel->partitions[i].id = SGI_SSWAP32(sys); + sgilabel->partitions[i].num_sectors = SGI_SSWAP32(length); + sgilabel->partitions[i].start_sector = SGI_SSWAP32(start); + set_changed(i); + if (sgi_gaps() < 0) /* rebuild freelist */ + printf(_("Do You know, You got a partition overlap on the disk?\n")); +} + +static void +sgi_set_entire(void) { + int n; + + for(n=10; n < partitions; n++) { + if(!sgi_get_num_sectors(n) ) { + sgi_set_partition(n, 0, sgi_get_lastblock(), SGI_VOLUME); + break; + } + } +} + +static void +sgi_set_volhdr(void) +{ + int n; + for( n=8; n 33.8 GB.\n"), disk_device, cylinders); + } + } + for (i = 0; i < 4; i++) { + old[i].sysid = 0; + if(valid_part_table_flag(MBRbuffer)) { + if(get_part_table(i)->sys_ind) { + old[i].sysid = get_part_table(i)->sys_ind; + old[i].start = get_start_sect(get_part_table(i)); + old[i].nsect = get_nr_sects(get_part_table(i)); + printf(_("Trying to keep parameters of partition %d.\n"), i); + if (debug) + printf(_("ID=%02x\tSTART=%d\tLENGTH=%d\n"), + old[i].sysid, old[i].start, old[i].nsect); + } + } + } + + memset(MBRbuffer, 0, sizeof(MBRbuffer)); + sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC); + sgilabel->boot_part = SGI_SSWAP16(0); + sgilabel->swap_part = SGI_SSWAP16(1); + + /* sizeof(sgilabel->boot_file) = 16 > 6 */ + memset(sgilabel->boot_file, 0, 16); + strcpy(sgilabel->boot_file, "/unix"); + + sgilabel->devparam.skew = (0); + sgilabel->devparam.gap1 = (0); + sgilabel->devparam.gap2 = (0); + sgilabel->devparam.sparecyl = (0); + sgilabel->devparam.pcylcount = SGI_SSWAP16(geometry.cylinders); + sgilabel->devparam.head_vol0 = SGI_SSWAP16(0); + sgilabel->devparam.ntrks = SGI_SSWAP16(geometry.heads); + /* tracks/cylinder (heads) */ + sgilabel->devparam.cmd_tag_queue_depth = (0); + sgilabel->devparam.unused0 = (0); + sgilabel->devparam.unused1 = SGI_SSWAP16(0); + sgilabel->devparam.nsect = SGI_SSWAP16(geometry.sectors); + /* sectors/track */ + sgilabel->devparam.bytes = SGI_SSWAP16(512); + sgilabel->devparam.ilfact = SGI_SSWAP16(1); + sgilabel->devparam.flags = SGI_SSWAP32(TRACK_FWD| + IGNORE_ERRORS|RESEEK); + sgilabel->devparam.datarate = SGI_SSWAP32(0); + sgilabel->devparam.retries_on_error = SGI_SSWAP32(1); + sgilabel->devparam.ms_per_word = SGI_SSWAP32(0); + sgilabel->devparam.xylogics_gap1 = SGI_SSWAP16(0); + sgilabel->devparam.xylogics_syncdelay = SGI_SSWAP16(0); + sgilabel->devparam.xylogics_readdelay = SGI_SSWAP16(0); + sgilabel->devparam.xylogics_gap2 = SGI_SSWAP16(0); + sgilabel->devparam.xylogics_readgate = SGI_SSWAP16(0); + sgilabel->devparam.xylogics_writecont = SGI_SSWAP16(0); + memset( &(sgilabel->directory), 0, sizeof(struct volume_directory)*15 ); + memset( &(sgilabel->partitions), 0, sizeof(struct sgi_partition)*16 ); + sgi_label = 1; + partitions = 16; + sgi_volumes = 15; + sgi_set_entire(); + sgi_set_volhdr(); + for (i = 0; i < 4; i++) { + if(old[i].sysid) { + sgi_set_partition(i, old[i].start, old[i].nsect, old[i].sysid); + } + } +} + +static void +sgi_set_xcyl(void) +{ + /* do nothing in the beginning */ +} +#endif /* CONFIG_FEATURE_FDISK_ADVANCED */ + +/* _____________________________________________________________ + */ + +static sgiinfo * +fill_sgiinfo(void) +{ + sgiinfo *info = calloc(1, sizeof(sgiinfo)); + + info->magic=SGI_SSWAP32(SGI_INFO_MAGIC); + info->b1=SGI_SSWAP32(-1); + info->b2=SGI_SSWAP16(-1); + info->b3=SGI_SSWAP16(1); + /* You may want to replace this string !!!!!!! */ + strcpy( info->scsi_string, "IBM OEM 0662S12 3 30" ); + strcpy( info->serial, "0000" ); + info->check1816 = SGI_SSWAP16(18*256 +16 ); + strcpy( info->installer, "Sfx version 5.3, Oct 18, 1994" ); + return info; +} +#endif /* SGI_LABEL */ + + +#ifdef CONFIG_FEATURE_SUN_LABEL +/* + * fdisksunlabel.c + * + * I think this is mostly, or entirely, due to + * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996 + * + * Merged with fdisk for other architectures, aeb, June 1998. + * + * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo + * Internationalization + */ + + +static int sun_other_endian; +static int scsi_disk; +static int floppy; + +#ifndef IDE0_MAJOR +#define IDE0_MAJOR 3 +#endif +#ifndef IDE1_MAJOR +#define IDE1_MAJOR 22 +#endif + +static void guess_device_type(void) { + struct stat bootstat; + + if (fstat (fd, &bootstat) < 0) { + scsi_disk = 0; + floppy = 0; + } else if (S_ISBLK(bootstat.st_mode) + && (major(bootstat.st_rdev) == IDE0_MAJOR || + major(bootstat.st_rdev) == IDE1_MAJOR)) { + scsi_disk = 0; + floppy = 0; + } else if (S_ISBLK(bootstat.st_mode) + && major(bootstat.st_rdev) == FLOPPY_MAJOR) { + scsi_disk = 0; + floppy = 1; + } else { + scsi_disk = 1; + floppy = 0; + } +} + +static const struct systypes sun_sys_types[] = { +/* 0 */ {"\x00" "Empty" }, +/* 1 */ {"\x01" "Boot" }, +/* 2 */ {"\x02" "SunOS root" }, +/* SUNOS_SWAP */ {"\x03" "SunOS swap" }, +/* 4 */ {"\x04" "SunOS usr" }, +/* WHOLE_DISK */ {"\x05" "Whole disk" }, +/* 6 */ {"\x06" "SunOS stand" }, +/* 7 */ {"\x07" "SunOS var" }, +/* 8 */ {"\x08" "SunOS home" }, +/* LINUX_SWAP */ {"\x82" "Linux swap" }, +/* LINUX_NATIVE */ {"\x83" "Linux native" }, +/* 0x8e */ {"\x8e" "Linux LVM" }, +/* New (2.2.x) raid partition with autodetect using persistent superblock */ +/* 0xfd */ {"\xfd" "Linux raid autodetect" }, + { NULL } +}; + + +static void +set_sun_partition(int i, uint start, uint stop, int sysid) { + sunlabel->infos[i].id = sysid; + sunlabel->partitions[i].start_cylinder = + SUN_SSWAP32(start / (heads * sectors)); + sunlabel->partitions[i].num_sectors = + SUN_SSWAP32(stop - start); + set_changed(i); +} + +static void +sun_nolabel(void) { + sun_label = 0; + sunlabel->magic = 0; + partitions = 4; +} + +static int +check_sun_label(void) { + unsigned short *ush; + int csum; + + if (sunlabel->magic != SUN_LABEL_MAGIC && + sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) { + sun_label = 0; + sun_other_endian = 0; + return 0; + } + sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED); + ush = ((unsigned short *) (sunlabel + 1)) - 1; + for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--; + if (csum) { + fprintf(stderr,_("Detected sun disklabel with wrong checksum.\n" + "Probably you'll have to set all the values,\n" + "e.g. heads, sectors, cylinders and partitions\n" + "or force a fresh label (s command in main menu)\n")); + } else { + heads = SUN_SSWAP16(sunlabel->ntrks); + cylinders = SUN_SSWAP16(sunlabel->ncyl); + sectors = SUN_SSWAP16(sunlabel->nsect); + } + update_units(); + sun_label = 1; + partitions = 8; + return 1; +} + +static const struct sun_predefined_drives { + const char *vendor; + const char *model; + unsigned short sparecyl; + unsigned short ncyl; + unsigned short nacyl; + unsigned short pcylcount; + unsigned short ntrks; + unsigned short nsect; + unsigned short rspeed; +} sun_drives[] = { +{"Quantum","ProDrive 80S",1,832,2,834,6,34,3662}, +{"Quantum","ProDrive 105S",1,974,2,1019,6,35,3662}, +{"CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600}, +{"IBM","DPES-31080",0,4901,2,4903,4,108,5400}, +{"IBM","DORS-32160",0,1015,2,1017,67,62,5400}, +{"IBM","DNES-318350",0,11199,2,11474,10,320,7200}, +{"SEAGATE","ST34371",0,3880,2,3882,16,135,7228}, +{"","SUN0104",1,974,2,1019,6,35,3662}, +{"","SUN0207",4,1254,2,1272,9,36,3600}, +{"","SUN0327",3,1545,2,1549,9,46,3600}, +{"","SUN0340",0,1538,2,1544,6,72,4200}, +{"","SUN0424",2,1151,2,2500,9,80,4400}, +{"","SUN0535",0,1866,2,2500,7,80,5400}, +{"","SUN0669",5,1614,2,1632,15,54,3600}, +{"","SUN1.0G",5,1703,2,1931,15,80,3597}, +{"","SUN1.05",0,2036,2,2038,14,72,5400}, +{"","SUN1.3G",6,1965,2,3500,17,80,5400}, +{"","SUN2.1G",0,2733,2,3500,19,80,5400}, +{"IOMEGA","Jaz",0,1019,2,1021,64,32,5394}, +}; + +static const struct sun_predefined_drives * +sun_autoconfigure_scsi(void) { + const struct sun_predefined_drives *p = NULL; + +#ifdef SCSI_IOCTL_GET_IDLUN + unsigned int id[2]; + char buffer[2048]; + char buffer2[2048]; + FILE *pfd; + char *vendor; + char *model; + char *q; + int i; + + if (!ioctl(fd, SCSI_IOCTL_GET_IDLUN, &id)) { + sprintf(buffer, + "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n", +#if 0 + ((id[0]>>24)&0xff)-/*PROC_SCSI_SCSI+PROC_SCSI_FILE*/33, +#else + /* This is very wrong (works only if you have one HBA), + but I haven't found a way how to get hostno + from the current kernel */ + 0, +#endif + (id[0]>>16)&0xff, + id[0]&0xff, + (id[0]>>8)&0xff); + pfd = fopen("/proc/scsi/scsi","r"); + if (pfd) { + while (fgets(buffer2,2048,pfd)) { + if (!strcmp(buffer, buffer2)) { + if (fgets(buffer2,2048,pfd)) { + q = strstr(buffer2,"Vendor: "); + if (q) { + q += 8; + vendor = q; + q = strstr(q," "); + *q++ = 0; /* truncate vendor name */ + q = strstr(q,"Model: "); + if (q) { + *q = 0; + q += 7; + model = q; + q = strstr(q," Rev: "); + if (q) { + *q = 0; + for (i = 0; i < SIZE(sun_drives); i++) { + if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor)) + continue; + if (!strstr(model, sun_drives[i].model)) + continue; + printf(_("Autoconfigure found a %s%s%s\n"),sun_drives[i].vendor,(*sun_drives[i].vendor) ? " " : "",sun_drives[i].model); + p = sun_drives + i; + break; + } + } + } + } + } + break; + } + } + fclose(pfd); + } + } +#endif + return p; +} + +static void create_sunlabel(void) +{ + struct hd_geometry geometry; + unsigned int ndiv; + int i; + unsigned char c; + const struct sun_predefined_drives *p = NULL; + + fprintf(stderr, + _("Building a new sun disklabel. Changes will remain in memory only,\n" + "until you decide to write them. After that, of course, the previous\n" + "content won't be recoverable.\n\n")); +#if BYTE_ORDER == LITTLE_ENDIAN + sun_other_endian = 1; +#else + sun_other_endian = 0; +#endif + memset(MBRbuffer, 0, sizeof(MBRbuffer)); + sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC); + if (!floppy) { + puts(_("Drive type\n" + " ? auto configure\n" + " 0 custom (with hardware detected defaults)")); + for (i = 0; i < SIZE(sun_drives); i++) { + printf(" %c %s%s%s\n", + i + 'a', sun_drives[i].vendor, + (*sun_drives[i].vendor) ? " " : "", + sun_drives[i].model); + } + for (;;) { + c = read_char(_("Select type (? for auto, 0 for custom): ")); + if (c >= 'a' && c < 'a' + SIZE(sun_drives)) { + p = sun_drives + c - 'a'; + break; + } else if (c >= 'A' && c < 'A' + SIZE(sun_drives)) { + p = sun_drives + c - 'A'; + break; + } else if (c == '0') { + break; + } else if (c == '?' && scsi_disk) { + p = sun_autoconfigure_scsi(); + if (!p) + printf(_("Autoconfigure failed.\n")); + else + break; + } + } + } + if (!p || floppy) { + if (!ioctl(fd, HDIO_GETGEO, &geometry)) { + heads = geometry.heads; + sectors = geometry.sectors; + cylinders = geometry.cylinders; + } else { + heads = 0; + sectors = 0; + cylinders = 0; + } + if (floppy) { + sunlabel->nacyl = 0; + sunlabel->pcylcount = SUN_SSWAP16(cylinders); + sunlabel->rspeed = SUN_SSWAP16(300); + sunlabel->ilfact = SUN_SSWAP16(1); + sunlabel->sparecyl = 0; + } else { + heads = read_int(1,heads,1024,0,_("Heads")); + sectors = read_int(1,sectors,1024,0,_("Sectors/track")); + if (cylinders) + cylinders = read_int(1,cylinders-2,65535,0,_("Cylinders")); + else + cylinders = read_int(1,0,65535,0,_("Cylinders")); + sunlabel->nacyl = + SUN_SSWAP16(read_int(0,2,65535,0, + _("Alternate cylinders"))); + sunlabel->pcylcount = + SUN_SSWAP16(read_int(0,cylinders+SUN_SSWAP16(sunlabel->nacyl), + 65535,0,_("Physical cylinders"))); + sunlabel->rspeed = + SUN_SSWAP16(read_int(1,5400,100000,0, + _("Rotation speed (rpm)"))); + sunlabel->ilfact = + SUN_SSWAP16(read_int(1,1,32,0,_("Interleave factor"))); + sunlabel->sparecyl = + SUN_SSWAP16(read_int(0,0,sectors,0, + _("Extra sectors per cylinder"))); + } + } else { + sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl); + sunlabel->ncyl = SUN_SSWAP16(p->ncyl); + sunlabel->nacyl = SUN_SSWAP16(p->nacyl); + sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount); + sunlabel->ntrks = SUN_SSWAP16(p->ntrks); + sunlabel->nsect = SUN_SSWAP16(p->nsect); + sunlabel->rspeed = SUN_SSWAP16(p->rspeed); + sunlabel->ilfact = SUN_SSWAP16(1); + cylinders = p->ncyl; + heads = p->ntrks; + sectors = p->nsect; + puts(_("You may change all the disk params from the x menu")); + } + + snprintf(sunlabel->info, sizeof(sunlabel->info), + "%s%s%s cyl %d alt %d hd %d sec %d", + p ? p->vendor : "", (p && *p->vendor) ? " " : "", + p ? p->model + : (floppy ? _("3,5\" floppy") : _("Linux custom")), + cylinders, SUN_SSWAP16(sunlabel->nacyl), heads, sectors); + + sunlabel->ntrks = SUN_SSWAP16(heads); + sunlabel->nsect = SUN_SSWAP16(sectors); + sunlabel->ncyl = SUN_SSWAP16(cylinders); + if (floppy) + set_sun_partition(0, 0, cylinders * heads * sectors, LINUX_NATIVE); + else { + if (cylinders * heads * sectors >= 150 * 2048) { + ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */ + } else + ndiv = cylinders * 2 / 3; + set_sun_partition(0, 0, ndiv * heads * sectors, LINUX_NATIVE); + set_sun_partition(1, ndiv * heads * sectors, cylinders * heads * sectors, LINUX_SWAP); + sunlabel->infos[1].flags |= 0x01; /* Not mountable */ + } + set_sun_partition(2, 0, cylinders * heads * sectors, WHOLE_DISK); + { + unsigned short *ush = (unsigned short *)sunlabel; + unsigned short csum = 0; + while(ush < (unsigned short *)(&sunlabel->csum)) + csum ^= *ush++; + sunlabel->csum = csum; + } + + set_all_unchanged(); + set_changed(0); + get_boot(create_empty_sun); +} + +static void +toggle_sunflags(int i, unsigned char mask) { + if (sunlabel->infos[i].flags & mask) + sunlabel->infos[i].flags &= ~mask; + else sunlabel->infos[i].flags |= mask; + set_changed(i); +} + +static void +fetch_sun(uint *starts, uint *lens, uint *start, uint *stop) { + int i, continuous = 1; + *start = 0; *stop = cylinders * heads * sectors; + for (i = 0; i < partitions; i++) { + if (sunlabel->partitions[i].num_sectors + && sunlabel->infos[i].id + && sunlabel->infos[i].id != WHOLE_DISK) { + starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors; + lens[i] = SUN_SSWAP32(sunlabel->partitions[i].num_sectors); + if (continuous) { + if (starts[i] == *start) + *start += lens[i]; + else if (starts[i] + lens[i] >= *stop) + *stop = starts[i]; + else + continuous = 0; + /* There will be probably more gaps + than one, so lets check afterwards */ + } + } else { + starts[i] = 0; + lens[i] = 0; + } + } +} + +static uint *verify_sun_starts; + +static int +verify_sun_cmp(int *a, int *b) { + if (*a == -1) return 1; + if (*b == -1) return -1; + if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1; + return -1; +} + +static void +verify_sun(void) { + uint starts[8], lens[8], start, stop; + int i,j,k,starto,endo; + int array[8]; + + verify_sun_starts = starts; + fetch_sun(starts,lens,&start,&stop); + for (k = 0; k < 7; k++) { + for (i = 0; i < 8; i++) { + if (k && (lens[i] % (heads * sectors))) { + printf(_("Partition %d doesn't end on cylinder boundary\n"), i+1); + } + if (lens[i]) { + for (j = 0; j < i; j++) + if (lens[j]) { + if (starts[j] == starts[i]+lens[i]) { + starts[j] = starts[i]; lens[j] += lens[i]; + lens[i] = 0; + } else if (starts[i] == starts[j]+lens[j]){ + lens[j] += lens[i]; + lens[i] = 0; + } else if (!k) { + if (starts[i] < starts[j]+lens[j] && + starts[j] < starts[i]+lens[i]) { + starto = starts[i]; + if (starts[j] > starto) + starto = starts[j]; + endo = starts[i]+lens[i]; + if (starts[j]+lens[j] < endo) + endo = starts[j]+lens[j]; + printf(_("Partition %d overlaps with others in " + "sectors %d-%d\n"), i+1, starto, endo); + } + } + } + } + } + } + for (i = 0; i < 8; i++) { + if (lens[i]) + array[i] = i; + else + array[i] = -1; + } + qsort(array,SIZE(array),sizeof(array[0]), + (int (*)(const void *,const void *)) verify_sun_cmp); + if (array[0] == -1) { + printf(_("No partitions defined\n")); + return; + } + stop = cylinders * heads * sectors; + if (starts[array[0]]) + printf(_("Unused gap - sectors 0-%d\n"),starts[array[0]]); + for (i = 0; i < 7 && array[i+1] != -1; i++) { + printf(_("Unused gap - sectors %d-%d\n"),starts[array[i]]+lens[array[i]],starts[array[i+1]]); + } + start = starts[array[i]]+lens[array[i]]; + if (start < stop) + printf(_("Unused gap - sectors %d-%d\n"),start,stop); +} + +static void +add_sun_partition(int n, int sys) { + uint start, stop, stop2; + uint starts[8], lens[8]; + int whole_disk = 0; + + char mesg[256]; + int i, first, last; + + if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) { + printf(_("Partition %d is already defined. Delete " + "it before re-adding it.\n"), n + 1); + return; + } + + fetch_sun(starts,lens,&start,&stop); + if (stop <= start) { + if (n == 2) + whole_disk = 1; + else { + printf(_("Other partitions already cover the whole disk.\nDelete " + "some/shrink them before retry.\n")); + return; + } + } + snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); + for (;;) { + if (whole_disk) + first = read_int(0, 0, 0, 0, mesg); + else + first = read_int(scround(start), scround(stop)+1, + scround(stop), 0, mesg); + if (display_in_cyl_units) + first *= units_per_sector; + else + /* Starting sector has to be properly aligned */ + first = (first + heads * sectors - 1) / (heads * sectors); + if (n == 2 && first != 0) + printf ("\ +It is highly recommended that the third partition covers the whole disk\n\ +and is of type `Whole disk'\n"); + /* ewt asks to add: "don't start a partition at cyl 0" + However, edmundo@rano.demon.co.uk writes: + "In addition to having a Sun partition table, to be able to + boot from the disc, the first partition, /dev/sdX1, must + start at cylinder 0. This means that /dev/sdX1 contains + the partition table and the boot block, as these are the + first two sectors of the disc. Therefore you must be + careful what you use /dev/sdX1 for. In particular, you must + not use a partition starting at cylinder 0 for Linux swap, + as that would overwrite the partition table and the boot + block. You may, however, use such a partition for a UFS + or EXT2 file system, as these file systems leave the first + 1024 bytes undisturbed. */ + /* On the other hand, one should not use partitions + starting at block 0 in an md, or the label will + be trashed. */ + for (i = 0; i < partitions; i++) + if (lens[i] && starts[i] <= first + && starts[i] + lens[i] > first) + break; + if (i < partitions && !whole_disk) { + if (n == 2 && !first) { + whole_disk = 1; + break; + } + printf(_("Sector %d is already allocated\n"), first); + } else + break; + } + stop = cylinders * heads * sectors; + stop2 = stop; + for (i = 0; i < partitions; i++) { + if (starts[i] > first && starts[i] < stop) + stop = starts[i]; + } + snprintf(mesg, sizeof(mesg), + _("Last %s or +size or +sizeM or +sizeK"), + str_units(SINGULAR)); + if (whole_disk) + last = read_int(scround(stop2), scround(stop2), scround(stop2), + 0, mesg); + else if (n == 2 && !first) + last = read_int(scround(first), scround(stop2), scround(stop2), + scround(first), mesg); + else + last = read_int(scround(first), scround(stop), scround(stop), + scround(first), mesg); + if (display_in_cyl_units) + last *= units_per_sector; + if (n == 2 && !first) { + if (last >= stop2) { + whole_disk = 1; + last = stop2; + } else if (last > stop) { + printf ( + _("You haven't covered the whole disk with the 3rd partition, but your value\n" + "%d %s covers some other partition. Your entry has been changed\n" + "to %d %s\n"), + scround(last), str_units(SINGULAR), + scround(stop), str_units(SINGULAR)); + last = stop; + } + } else if (!whole_disk && last > stop) + last = stop; + + if (whole_disk) sys = WHOLE_DISK; + set_sun_partition(n, first, last, sys); +} + +static void +sun_delete_partition(int i) { + unsigned int nsec; + + if (i == 2 && sunlabel->infos[i].id == WHOLE_DISK && + !sunlabel->partitions[i].start_cylinder && + (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors)) + == heads * sectors * cylinders) + printf(_("If you want to maintain SunOS/Solaris compatibility, " + "consider leaving this\n" + "partition as Whole disk (5), starting at 0, with %u " + "sectors\n"), nsec); + sunlabel->infos[i].id = 0; + sunlabel->partitions[i].num_sectors = 0; +} + +static void +sun_change_sysid(int i, int sys) { + if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) { + read_chars( + _("It is highly recommended that the partition at offset 0\n" + "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n" + "there may destroy your partition table and bootblock.\n" + "Type YES if you're very sure you would like that partition\n" + "tagged with 82 (Linux swap): ")); + if (strcmp (line_ptr, _("YES\n"))) + return; + } + switch (sys) { + case SUNOS_SWAP: + case LINUX_SWAP: + /* swaps are not mountable by default */ + sunlabel->infos[i].flags |= 0x01; + break; + default: + /* assume other types are mountable; + user can change it anyway */ + sunlabel->infos[i].flags &= ~0x01; + break; + } + sunlabel->infos[i].id = sys; +} + +static void +sun_list_table(int xtra) { + int i, w; + + w = strlen(disk_device); + if (xtra) + printf( + _("\nDisk %s (Sun disk label): %d heads, %d sectors, %d rpm\n" + "%d cylinders, %d alternate cylinders, %d physical cylinders\n" + "%d extra sects/cyl, interleave %d:1\n" + "%s\n" + "Units = %s of %d * 512 bytes\n\n"), + disk_device, heads, sectors, SUN_SSWAP16(sunlabel->rspeed), + cylinders, SUN_SSWAP16(sunlabel->nacyl), + SUN_SSWAP16(sunlabel->pcylcount), + SUN_SSWAP16(sunlabel->sparecyl), + SUN_SSWAP16(sunlabel->ilfact), + (char *)sunlabel, + str_units(PLURAL), units_per_sector); + else + printf( + _("\nDisk %s (Sun disk label): %d heads, %d sectors, %d cylinders\n" + "Units = %s of %d * 512 bytes\n\n"), + disk_device, heads, sectors, cylinders, + str_units(PLURAL), units_per_sector); + + printf(_("%*s Flag Start End Blocks Id System\n"), + w + 1, _("Device")); + for (i = 0 ; i < partitions; i++) { + if (sunlabel->partitions[i].num_sectors) { + uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors; + uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors); + printf( + "%s %c%c %9ld %9ld %9ld%c %2x %s\n", +/* device */ partname(disk_device, i+1, w), +/* flags */ (sunlabel->infos[i].flags & 0x01) ? 'u' : ' ', + (sunlabel->infos[i].flags & 0x10) ? 'r' : ' ', +/* start */ (long) scround(start), +/* end */ (long) scround(start+len), +/* odd flag on end */ (long) len / 2, len & 1 ? '+' : ' ', +/* type id */ sunlabel->infos[i].id, +/* type name */ partition_type(sunlabel->infos[i].id)); + } + } +} + +#ifdef CONFIG_FEATURE_FDISK_ADVANCED + +static void +sun_set_alt_cyl(void) { + sunlabel->nacyl = + SUN_SSWAP16(read_int(0,SUN_SSWAP16(sunlabel->nacyl), 65535, 0, + _("Number of alternate cylinders"))); +} + +static void +sun_set_ncyl(int cyl) { + sunlabel->ncyl = SUN_SSWAP16(cyl); +} + +static void +sun_set_xcyl(void) { + sunlabel->sparecyl = + SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), sectors, 0, + _("Extra sectors per cylinder"))); +} + +static void +sun_set_ilfact(void) { + sunlabel->ilfact = + SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0, + _("Interleave factor"))); +} + +static void +sun_set_rspeed(void) { + sunlabel->rspeed = + SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0, + _("Rotation speed (rpm)"))); +} + +static void +sun_set_pcylcount(void) { + sunlabel->pcylcount = + SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0, + _("Number of physical cylinders"))); +} +#endif /* CONFIG_FEATURE_FDISK_ADVANCED */ + +static void +sun_write_table(void) { + unsigned short *ush = (unsigned short *)sunlabel; + unsigned short csum = 0; + + while(ush < (unsigned short *)(&sunlabel->csum)) + csum ^= *ush++; + sunlabel->csum = csum; + if (lseek(fd, 0, SEEK_SET) < 0) + fdisk_fatal(unable_to_seek); + if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE) + fdisk_fatal(unable_to_write); +} +#endif /* SUN_LABEL */ + +/* DOS partition types */ + +static const struct systypes i386_sys_types[] = { + {"\x00" "Empty"}, + {"\x01" "FAT12"}, + {"\x04" "FAT16 <32M"}, + {"\x05" "Extended"}, /* DOS 3.3+ extended partition */ + {"\x06" "FAT16"}, /* DOS 16-bit >=32M */ + {"\x07" "HPFS/NTFS"}, /* OS/2 IFS, eg, HPFS or NTFS or QNX */ + {"\x0a" "OS/2 Boot Manager"},/* OS/2 Boot Manager */ + {"\x0b" "Win95 FAT32"}, + {"\x0c" "Win95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */ + {"\x0e" "Win95 FAT16 (LBA)"}, + {"\x0f" "Win95 Ext'd (LBA)"}, + {"\x11" "Hidden FAT12"}, + {"\x12" "Compaq diagnostics"}, + {"\x14" "Hidden FAT16 <32M"}, + {"\x16" "Hidden FAT16"}, + {"\x17" "Hidden HPFS/NTFS"}, + {"\x1b" "Hidden Win95 FAT32"}, + {"\x1c" "Hidden Win95 FAT32 (LBA)"}, + {"\x1e" "Hidden Win95 FAT16 (LBA)"}, + {"\x3c" "PartitionMagic recovery"}, + {"\x41" "PPC PReP Boot"}, + {"\x42" "SFS"}, + {"\x63" "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */ + {"\x80" "Old Minix"}, /* Minix 1.4a and earlier */ + {"\x81" "Minix / old Linux"},/* Minix 1.4b and later */ + {"\x82" "Linux swap"}, /* also Solaris */ + {"\x83" "Linux"}, + {"\x84" "OS/2 hidden C: drive"}, + {"\x85" "Linux extended"}, + {"\x86" "NTFS volume set"}, + {"\x87" "NTFS volume set"}, + {"\x8e" "Linux LVM"}, + {"\x9f" "BSD/OS"}, /* BSDI */ + {"\xa0" "IBM Thinkpad hibernation"}, + {"\xa5" "FreeBSD"}, /* various BSD flavours */ + {"\xa6" "OpenBSD"}, + {"\xa8" "Darwin UFS"}, + {"\xa9" "NetBSD"}, + {"\xab" "Darwin boot"}, + {"\xb7" "BSDI fs"}, + {"\xb8" "BSDI swap"}, + {"\xbe" "Solaris boot"}, + {"\xeb" "BeOS fs"}, + {"\xee" "EFI GPT"}, /* Intel EFI GUID Partition Table */ + {"\xef" "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */ + {"\xf0" "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */ + {"\xf2" "DOS secondary"}, /* DOS 3.3+ secondary */ + {"\xfd" "Linux raid autodetect"},/* New (2.2.x) raid partition with + autodetect using persistent + superblock */ +#ifdef CONFIG_WEIRD_PARTITION_TYPES + {"\x02" "XENIX root"}, + {"\x03" "XENIX usr"}, + {"\x08" "AIX"}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */ + {"\x09" "AIX bootable"}, /* AIX data or Coherent */ + {"\x10" "OPUS"}, + {"\x18" "AST SmartSleep"}, + {"\x24" "NEC DOS"}, + {"\x39" "Plan 9"}, + {"\x40" "Venix 80286"}, + {"\x4d" "QNX4.x"}, + {"\x4e" "QNX4.x 2nd part"}, + {"\x4f" "QNX4.x 3rd part"}, + {"\x50" "OnTrack DM"}, + {"\x51" "OnTrack DM6 Aux1"}, /* (or Novell) */ + {"\x52" "CP/M"}, /* CP/M or Microport SysV/AT */ + {"\x53" "OnTrack DM6 Aux3"}, + {"\x54" "OnTrackDM6"}, + {"\x55" "EZ-Drive"}, + {"\x56" "Golden Bow"}, + {"\x5c" "Priam Edisk"}, + {"\x61" "SpeedStor"}, + {"\x64" "Novell Netware 286"}, + {"\x65" "Novell Netware 386"}, + {"\x70" "DiskSecure Multi-Boot"}, + {"\x75" "PC/IX"}, + {"\x93" "Amoeba"}, + {"\x94" "Amoeba BBT"}, /* (bad block table) */ + {"\xa7" "NeXTSTEP"}, + {"\xbb" "Boot Wizard hidden"}, + {"\xc1" "DRDOS/sec (FAT-12)"}, + {"\xc4" "DRDOS/sec (FAT-16 < 32M)"}, + {"\xc6" "DRDOS/sec (FAT-16)"}, + {"\xc7" "Syrinx"}, + {"\xda" "Non-FS data"}, + {"\xdb" "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or + Concurrent DOS or CTOS */ + {"\xde" "Dell Utility"}, /* Dell PowerEdge Server utilities */ + {"\xdf" "BootIt"}, /* BootIt EMBRM */ + {"\xe1" "DOS access"}, /* DOS access or SpeedStor 12-bit FAT + extended partition */ + {"\xe3" "DOS R/O"}, /* DOS R/O or SpeedStor */ + {"\xe4" "SpeedStor"}, /* SpeedStor 16-bit FAT extended + partition < 1024 cyl. */ + {"\xf1" "SpeedStor"}, + {"\xf4" "SpeedStor"}, /* SpeedStor large partition */ + {"\xfe" "LANstep"}, /* SpeedStor >1024 cyl. or LANstep */ + {"\xff" "BBT"}, /* Xenix Bad Block Table */ +#endif + { 0 } +}; + + + +/* A valid partition table sector ends in 0x55 0xaa */ +static unsigned int +part_table_flag(const char *b) { + return ((uint) b[510]) + (((uint) b[511]) << 8); +} + + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +write_part_table_flag(char *b) { + b[510] = 0x55; + b[511] = 0xaa; +} + +/* start_sect and nr_sects are stored little endian on all machines */ +/* moreover, they are not aligned correctly */ +static void +store4_little_endian(unsigned char *cp, unsigned int val) { + cp[0] = (val & 0xff); + cp[1] = ((val >> 8) & 0xff); + cp[2] = ((val >> 16) & 0xff); + cp[3] = ((val >> 24) & 0xff); +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +static unsigned int +read4_little_endian(const unsigned char *cp) { + return (uint)(cp[0]) + ((uint)(cp[1]) << 8) + + ((uint)(cp[2]) << 16) + ((uint)(cp[3]) << 24); +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +set_start_sect(struct partition *p, unsigned int start_sect) { + store4_little_endian(p->start4, start_sect); +} +#endif + +static int32_t +get_start_sect(const struct partition *p) { + return read4_little_endian(p->start4); +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +set_nr_sects(struct partition *p, int32_t nr_sects) { + store4_little_endian(p->size4, nr_sects); +} +#endif + +static int32_t +get_nr_sects(const struct partition *p) { + return read4_little_endian(p->size4); +} + +/* normally O_RDWR, -l option gives O_RDONLY */ +static int type_open = O_RDWR; + + +static int ext_index, /* the prime extended partition */ + listing, /* no aborts for fdisk -l */ + dos_compatible_flag = ~0; +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static int dos_changed; +static int nowarn; /* no warnings for fdisk -l/-s */ +#endif + + + +static uint user_cylinders, user_heads, user_sectors; +static uint pt_heads, pt_sectors; +static uint kern_heads, kern_sectors; + +static off_t extended_offset; /* offset of link pointers */ + +static unsigned long long total_number_of_sectors; + + +static jmp_buf listingbuf; + +static void fdisk_fatal(enum failure why) { + const char *message; + + if (listing) { + close(fd); + longjmp(listingbuf, 1); + } + + switch (why) { + case unable_to_open: + message = "Unable to open %s\n"; + break; + case unable_to_read: + message = "Unable to read %s\n"; + break; + case unable_to_seek: + message = "Unable to seek on %s\n"; + break; + case unable_to_write: + message = "Unable to write %s\n"; + break; + case ioctl_error: + message = "BLKGETSIZE ioctl failed on %s\n"; + break; + default: + message = "Fatal error\n"; + } + + fputc('\n', stderr); + fprintf(stderr, message, disk_device); + exit(1); +} + +static void +seek_sector(off_t secno) { + off_t offset = secno * sector_size; + if (lseek(fd, offset, SEEK_SET) == (off_t) -1) + fdisk_fatal(unable_to_seek); +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +write_sector(off_t secno, char *buf) { + seek_sector(secno); + if (write(fd, buf, sector_size) != sector_size) + fdisk_fatal(unable_to_write); +} +#endif + +/* Allocate a buffer and read a partition table sector */ +static void +read_pte(struct pte *pe, off_t offset) { + + pe->offset = offset; + pe->sectorbuffer = (char *) xmalloc(sector_size); + seek_sector(offset); + if (read(fd, pe->sectorbuffer, sector_size) != sector_size) + fdisk_fatal(unable_to_read); +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + pe->changed = 0; +#endif + pe->part_table = pe->ext_pointer = NULL; +} + +static unsigned int +get_partition_start(const struct pte *pe) { + return pe->offset + get_start_sect(pe->part_table); +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +/* + * Avoid warning about DOS partitions when no DOS partition was changed. + * Here a heuristic "is probably dos partition". + * We might also do the opposite and warn in all cases except + * for "is probably nondos partition". + */ +static int +is_dos_partition(int t) { + return (t == 1 || t == 4 || t == 6 || + t == 0x0b || t == 0x0c || t == 0x0e || + t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 || + t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 || + t == 0xc1 || t == 0xc4 || t == 0xc6); +} + +static void +menu(void) { +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) { + puts(_("Command action")); + puts(_("\ta\ttoggle a read only flag")); /* sun */ + puts(_("\tb\tedit bsd disklabel")); + puts(_("\tc\ttoggle the mountable flag")); /* sun */ + puts(_("\td\tdelete a partition")); + puts(_("\tl\tlist known partition types")); + puts(_("\tm\tprint this menu")); + puts(_("\tn\tadd a new partition")); + puts(_("\to\tcreate a new empty DOS partition table")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ + puts(_("\tt\tchange a partition's system id")); + puts(_("\tu\tchange display/entry units")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); +#ifdef CONFIG_FEATURE_FDISK_ADVANCED + puts(_("\tx\textra functionality (experts only)")); +#endif + } else +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) { + puts(_("Command action")); + puts(_("\ta\tselect bootable partition")); /* sgi flavour */ + puts(_("\tb\tedit bootfile entry")); /* sgi */ + puts(_("\tc\tselect sgi swap partition")); /* sgi flavour */ + puts(_("\td\tdelete a partition")); + puts(_("\tl\tlist known partition types")); + puts(_("\tm\tprint this menu")); + puts(_("\tn\tadd a new partition")); + puts(_("\to\tcreate a new empty DOS partition table")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ + puts(_("\tt\tchange a partition's system id")); + puts(_("\tu\tchange display/entry units")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + } else +#endif +#ifdef CONFIG_FEATURE_AIX_LABEL + if (aix_label) { + puts(_("Command action")); + puts(_("\tm\tprint this menu")); + puts(_("\to\tcreate a new empty DOS partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ + } else +#endif + { + puts(_("Command action")); + puts(_("\ta\ttoggle a bootable flag")); + puts(_("\tb\tedit bsd disklabel")); + puts(_("\tc\ttoggle the dos compatibility flag")); + puts(_("\td\tdelete a partition")); + puts(_("\tl\tlist known partition types")); + puts(_("\tm\tprint this menu")); + puts(_("\tn\tadd a new partition")); + puts(_("\to\tcreate a new empty DOS partition table")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */ + puts(_("\tt\tchange a partition's system id")); + puts(_("\tu\tchange display/entry units")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); +#ifdef CONFIG_FEATURE_FDISK_ADVANCED + puts(_("\tx\textra functionality (experts only)")); +#endif + } +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + + +#ifdef CONFIG_FEATURE_FDISK_ADVANCED +static void +xmenu(void) { +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) { + puts(_("Command action")); + puts(_("\ta\tchange number of alternate cylinders")); /*sun*/ + puts(_("\tc\tchange number of cylinders")); + puts(_("\td\tprint the raw data in the partition table")); + puts(_("\te\tchange number of extra sectors per cylinder"));/*sun*/ + puts(_("\th\tchange number of heads")); + puts(_("\ti\tchange interleave factor")); /*sun*/ + puts(_("\to\tchange rotation speed (rpm)")); /*sun*/ + puts(_("\tm\tprint this menu")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\tr\treturn to main menu")); + puts(_("\ts\tchange number of sectors/track")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + puts(_("\ty\tchange number of physical cylinders")); /*sun*/ + } else +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) { + puts(_("Command action")); + puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ + puts(_("\tc\tchange number of cylinders")); + puts(_("\td\tprint the raw data in the partition table")); + puts(_("\te\tlist extended partitions")); /* !sun */ + puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ + puts(_("\th\tchange number of heads")); + puts(_("\tm\tprint this menu")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\tr\treturn to main menu")); + puts(_("\ts\tchange number of sectors/track")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + } else +#endif +#ifdef CONFIG_FEATURE_AIX_LABEL + if (aix_label) { + puts(_("Command action")); + puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ + puts(_("\tc\tchange number of cylinders")); + puts(_("\td\tprint the raw data in the partition table")); + puts(_("\te\tlist extended partitions")); /* !sun */ + puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ + puts(_("\th\tchange number of heads")); + puts(_("\tm\tprint this menu")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\tr\treturn to main menu")); + puts(_("\ts\tchange number of sectors/track")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + } else +#endif + { + puts(_("Command action")); + puts(_("\tb\tmove beginning of data in a partition")); /* !sun */ + puts(_("\tc\tchange number of cylinders")); + puts(_("\td\tprint the raw data in the partition table")); + puts(_("\te\tlist extended partitions")); /* !sun */ + puts(_("\tf\tfix partition order")); /* !sun, !aix, !sgi */ +#ifdef CONFIG_FEATURE_SGI_LABEL + puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */ +#endif + puts(_("\th\tchange number of heads")); + puts(_("\tm\tprint this menu")); + puts(_("\tp\tprint the partition table")); + puts(_("\tq\tquit without saving changes")); + puts(_("\tr\treturn to main menu")); + puts(_("\ts\tchange number of sectors/track")); + puts(_("\tv\tverify the partition table")); + puts(_("\tw\twrite table to disk and exit")); + } +} +#endif /* ADVANCED mode */ + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static const struct systypes * +get_sys_types(void) { + return ( +#ifdef CONFIG_FEATURE_SUN_LABEL + sun_label ? sun_sys_types : +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + sgi_label ? sgi_sys_types : +#endif + i386_sys_types); +} +#else +#define get_sys_types() i386_sys_types +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +static const char *partition_type(unsigned char type) +{ + int i; + const struct systypes *types = get_sys_types(); + + for (i=0; types[i].name; i++) + if (types[i].name[0] == type) + return types[i].name + 1; + + return _("Unknown"); +} + + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static int +get_sysid(int i) { + return ( +#ifdef CONFIG_FEATURE_SUN_LABEL + sun_label ? sunlabel->infos[i].id : +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + sgi_label ? sgi_get_sysid(i) : +#endif + ptes[i].part_table->sys_ind); +} + +void list_types(const struct systypes *sys) +{ + uint last[4], done = 0, next = 0, size; + int i; + + for (i = 0; sys[i].name; i++); + size = i; + + for (i = 3; i >= 0; i--) + last[3 - i] = done += (size + i - done) / (i + 1); + i = done = 0; + + do { + printf("%c%2x %-15.15s", i ? ' ' : '\n', + sys[next].name[0], partition_type(sys[next].name[0])); + next = last[i++] + done; + if (i > 3 || next >= last[i]) { + i = 0; + next = ++done; + } + } while (done < last[0]); + putchar('\n'); +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +static int +is_cleared_partition(const struct partition *p) { + return !(!p || p->boot_ind || p->head || p->sector || p->cyl || + p->sys_ind || p->end_head || p->end_sector || p->end_cyl || + get_start_sect(p) || get_nr_sects(p)); +} + +static void +clear_partition(struct partition *p) { + if (!p) + return; + memset(p, 0, sizeof(struct partition)); +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +set_partition(int i, int doext, off_t start, off_t stop, int sysid) { + struct partition *p; + off_t offset; + + if (doext) { + p = ptes[i].ext_pointer; + offset = extended_offset; + } else { + p = ptes[i].part_table; + offset = ptes[i].offset; + } + p->boot_ind = 0; + p->sys_ind = sysid; + set_start_sect(p, start - offset); + set_nr_sects(p, stop - start + 1); + if (dos_compatible_flag && (start/(sectors*heads) > 1023)) + start = heads*sectors*1024 - 1; + set_hsc(p->head, p->sector, p->cyl, start); + if (dos_compatible_flag && (stop/(sectors*heads) > 1023)) + stop = heads*sectors*1024 - 1; + set_hsc(p->end_head, p->end_sector, p->end_cyl, stop); + ptes[i].changed = 1; +} +#endif + +static int +test_c(const char **m, const char *mesg) { + int val = 0; + if (!*m) + fprintf(stderr, _("You must set")); + else { + fprintf(stderr, " %s", *m); + val = 1; + } + *m = mesg; + return val; +} + +static int +warn_geometry(void) { + const char *m = NULL; + int prev = 0; + + if (!heads) + prev = test_c(&m, _("heads")); + if (!sectors) + prev = test_c(&m, _("sectors")); + if (!cylinders) + prev = test_c(&m, _("cylinders")); + if (!m) + return 0; + + fprintf(stderr, "%s%s.\n" +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + "You can do this from the extra functions menu.\n" +#endif + , prev ? _(" and ") : " ", m); + + return 1; +} + +static void update_units(void) +{ + int cyl_units = heads * sectors; + + if (display_in_cyl_units && cyl_units) + units_per_sector = cyl_units; + else + units_per_sector = 1; /* in sectors */ +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +warn_cylinders(void) { + if (dos_label && cylinders > 1024 && !nowarn) + fprintf(stderr, _("\n" +"The number of cylinders for this disk is set to %d.\n" +"There is nothing wrong with that, but this is larger than 1024,\n" +"and could in certain setups cause problems with:\n" +"1) software that runs at boot time (e.g., old versions of LILO)\n" +"2) booting and partitioning software from other OSs\n" +" (e.g., DOS FDISK, OS/2 FDISK)\n"), + cylinders); +} +#endif + +static void +read_extended(int ext) { + int i; + struct pte *pex; + struct partition *p, *q; + + ext_index = ext; + pex = &ptes[ext]; + pex->ext_pointer = pex->part_table; + + p = pex->part_table; + if (!get_start_sect(p)) { + fprintf(stderr, + _("Bad offset in primary extended partition\n")); + return; + } + + while (IS_EXTENDED (p->sys_ind)) { + struct pte *pe = &ptes[partitions]; + + if (partitions >= MAXIMUM_PARTS) { + /* This is not a Linux restriction, but + this program uses arrays of size MAXIMUM_PARTS. + Do not try to `improve' this test. */ + struct pte *pre = &ptes[partitions-1]; +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + fprintf(stderr, + _("Warning: deleting partitions after %d\n"), + partitions); + pre->changed = 1; +#endif + clear_partition(pre->ext_pointer); + return; + } + + read_pte(pe, extended_offset + get_start_sect(p)); + + if (!extended_offset) + extended_offset = get_start_sect(p); + + q = p = pt_offset(pe->sectorbuffer, 0); + for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) { + if (IS_EXTENDED (p->sys_ind)) { + if (pe->ext_pointer) + fprintf(stderr, + _("Warning: extra link " + "pointer in partition table" + " %d\n"), partitions + 1); + else + pe->ext_pointer = p; + } else if (p->sys_ind) { + if (pe->part_table) + fprintf(stderr, + _("Warning: ignoring extra " + "data in partition table" + " %d\n"), partitions + 1); + else + pe->part_table = p; + } + } + + /* very strange code here... */ + if (!pe->part_table) { + if (q != pe->ext_pointer) + pe->part_table = q; + else + pe->part_table = q + 1; + } + if (!pe->ext_pointer) { + if (q != pe->part_table) + pe->ext_pointer = q; + else + pe->ext_pointer = q + 1; + } + + p = pe->ext_pointer; + partitions++; + } + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + /* remove empty links */ + remove: + for (i = 4; i < partitions; i++) { + struct pte *pe = &ptes[i]; + + if (!get_nr_sects(pe->part_table) && + (partitions > 5 || ptes[4].part_table->sys_ind)) { + printf("omitting empty partition (%d)\n", i+1); + delete_partition(i); + goto remove; /* numbering changed */ + } + } +#endif +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +create_doslabel(void) { + int i; + + fprintf(stderr, + _("Building a new DOS disklabel. Changes will remain in memory only,\n" + "until you decide to write them. After that, of course, the previous\n" + "content won't be recoverable.\n\n")); +#ifdef CONFIG_FEATURE_SUN_LABEL + sun_nolabel(); /* otherwise always recognised as sun */ +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + sgi_nolabel(); /* otherwise always recognised as sgi */ +#endif +#ifdef CONFIG_FEATURE_AIX_LABEL + aix_label = 0; +#endif +#ifdef CONFIG_FEATURE_OSF_LABEL + osf_label = 0; + possibly_osf_label = 0; +#endif + partitions = 4; + + for (i = 510-64; i < 510; i++) + MBRbuffer[i] = 0; + write_part_table_flag(MBRbuffer); + extended_offset = 0; + set_all_unchanged(); + set_changed(0); + get_boot(create_empty_dos); +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +static void +get_sectorsize(void) { + if (!user_set_sector_size && + get_kernel_revision() >= MAKE_VERSION(2,3,3)) { + int arg; + if (ioctl(fd, BLKSSZGET, &arg) == 0) + sector_size = arg; + if (sector_size != DEFAULT_SECTOR_SIZE) + printf(_("Note: sector size is %d (not %d)\n"), + sector_size, DEFAULT_SECTOR_SIZE); + } +} + +static inline void +get_kernel_geometry(void) { + struct hd_geometry geometry; + + if (!ioctl(fd, HDIO_GETGEO, &geometry)) { + kern_heads = geometry.heads; + kern_sectors = geometry.sectors; + /* never use geometry.cylinders - it is truncated */ + } +} + +static void +get_partition_table_geometry(void) { + const unsigned char *bufp = MBRbuffer; + struct partition *p; + int i, h, s, hh, ss; + int first = 1; + int bad = 0; + + if (!(valid_part_table_flag(bufp))) + return; + + hh = ss = 0; + for (i=0; i<4; i++) { + p = pt_offset(bufp, i); + if (p->sys_ind != 0) { + h = p->end_head + 1; + s = (p->end_sector & 077); + if (first) { + hh = h; + ss = s; + first = 0; + } else if (hh != h || ss != s) + bad = 1; + } + } + + if (!first && !bad) { + pt_heads = hh; + pt_sectors = ss; + } +} + +void +get_geometry(void) { + int sec_fac; + unsigned long long bytes; /* really u64 */ + + get_sectorsize(); + sec_fac = sector_size / 512; +#ifdef CONFIG_FEATURE_SUN_LABEL + guess_device_type(); +#endif + heads = cylinders = sectors = 0; + kern_heads = kern_sectors = 0; + pt_heads = pt_sectors = 0; + + get_kernel_geometry(); + get_partition_table_geometry(); + + heads = user_heads ? user_heads : + pt_heads ? pt_heads : + kern_heads ? kern_heads : 255; + sectors = user_sectors ? user_sectors : + pt_sectors ? pt_sectors : + kern_sectors ? kern_sectors : 63; + if (ioctl(fd, BLKGETSIZE64, &bytes) == 0) { + /* got bytes */ + } else { + unsigned long longsectors; + + if (ioctl(fd, BLKGETSIZE, &longsectors)) + longsectors = 0; + bytes = ((unsigned long long) longsectors) << 9; + } + + total_number_of_sectors = (bytes >> 9); + + sector_offset = 1; + if (dos_compatible_flag) + sector_offset = sectors; + + cylinders = total_number_of_sectors / (heads * sectors * sec_fac); + if (!cylinders) + cylinders = user_cylinders; +} + +/* + * Read MBR. Returns: + * -1: no 0xaa55 flag present (possibly entire disk BSD) + * 0: found or created label + * 1: I/O error + */ +int +get_boot(enum action what) { + int i; + + partitions = 4; + + for (i = 0; i < 4; i++) { + struct pte *pe = &ptes[i]; + + pe->part_table = pt_offset(MBRbuffer, i); + pe->ext_pointer = NULL; + pe->offset = 0; + pe->sectorbuffer = MBRbuffer; +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + pe->changed = (what == create_empty_dos); +#endif + } + +#ifdef CONFIG_FEATURE_SUN_LABEL + if (what == create_empty_sun && check_sun_label()) + return 0; +#endif + + memset(MBRbuffer, 0, 512); + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + if (what == create_empty_dos) + goto got_dos_table; /* skip reading disk */ + + if ((fd = open(disk_device, type_open)) < 0) { + if ((fd = open(disk_device, O_RDONLY)) < 0) { + if (what == try_only) + return 1; + fdisk_fatal(unable_to_open); + } else + printf(_("You will not be able to write " + "the partition table.\n")); + } + + if (512 != read(fd, MBRbuffer, 512)) { + if (what == try_only) + return 1; + fdisk_fatal(unable_to_read); + } +#else + if ((fd = open(disk_device, O_RDONLY)) < 0) + return 1; + if (512 != read(fd, MBRbuffer, 512)) + return 1; +#endif + + get_geometry(); + + update_units(); + +#ifdef CONFIG_FEATURE_SUN_LABEL + if (check_sun_label()) + return 0; +#endif + +#ifdef CONFIG_FEATURE_SGI_LABEL + if (check_sgi_label()) + return 0; +#endif + +#ifdef CONFIG_FEATURE_AIX_LABEL + if (check_aix_label()) + return 0; +#endif + +#ifdef CONFIG_FEATURE_OSF_LABEL + if (check_osf_label()) { + possibly_osf_label = 1; + if (!valid_part_table_flag(MBRbuffer)) { + osf_label = 1; + return 0; + } + printf(_("This disk has both DOS and BSD magic.\n" + "Give the 'b' command to go to BSD mode.\n")); + } +#endif + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +got_dos_table: +#endif + + if (!valid_part_table_flag(MBRbuffer)) { +#ifndef CONFIG_FEATURE_FDISK_WRITABLE + return -1; +#else + switch(what) { + case fdisk: + fprintf(stderr, + _("Device contains neither a valid DOS " + "partition table, nor Sun, SGI or OSF " + "disklabel\n")); +#ifdef __sparc__ +#ifdef CONFIG_FEATURE_SUN_LABEL + create_sunlabel(); +#endif +#else + create_doslabel(); +#endif + return 0; + case try_only: + return -1; + case create_empty_dos: +#ifdef CONFIG_FEATURE_SUN_LABEL + case create_empty_sun: +#endif + break; + default: + fprintf(stderr, _("Internal error\n")); + exit(1); + } +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + } + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + warn_cylinders(); +#endif + warn_geometry(); + + for (i = 0; i < 4; i++) { + struct pte *pe = &ptes[i]; + + if (IS_EXTENDED (pe->part_table->sys_ind)) { + if (partitions != 4) + fprintf(stderr, _("Ignoring extra extended " + "partition %d\n"), i + 1); + else + read_extended(i); + } + } + + for (i = 3; i < partitions; i++) { + struct pte *pe = &ptes[i]; + + if (!valid_part_table_flag(pe->sectorbuffer)) { + fprintf(stderr, + _("Warning: invalid flag 0x%04x of partition " + "table %d will be corrected by w(rite)\n"), + part_table_flag(pe->sectorbuffer), i + 1); +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + pe->changed = 1; +#endif + } + } + + return 0; +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +/* + * Print the message MESG, then read an integer between LOW and HIGH (inclusive). + * If the user hits Enter, DFLT is returned. + * Answers like +10 are interpreted as offsets from BASE. + * + * There is no default if DFLT is not between LOW and HIGH. + */ +static uint +read_int(uint low, uint dflt, uint high, uint base, char *mesg) +{ + uint i; + int default_ok = 1; + static char *ms = NULL; + static int mslen = 0; + + if (!ms || strlen(mesg)+100 > mslen) { + mslen = strlen(mesg)+200; + ms = xrealloc(ms,mslen); + } + + if (dflt < low || dflt > high) + default_ok = 0; + + if (default_ok) + snprintf(ms, mslen, _("%s (%u-%u, default %u): "), + mesg, low, high, dflt); + else + snprintf(ms, mslen, "%s (%u-%u): ", + mesg, low, high); + + while (1) { + int use_default = default_ok; + + /* ask question and read answer */ + while (read_chars(ms) != '\n' && !isdigit(*line_ptr) + && *line_ptr != '-' && *line_ptr != '+') + continue; + + if (*line_ptr == '+' || *line_ptr == '-') { + int minus = (*line_ptr == '-'); + int absolute = 0; + + i = atoi(line_ptr+1); + + while (isdigit(*++line_ptr)) + use_default = 0; + + switch (*line_ptr) { + case 'c': + case 'C': + if (!display_in_cyl_units) + i *= heads * sectors; + break; + case 'K': + absolute = 1024; + break; + case 'k': + absolute = 1000; + break; + case 'm': + case 'M': + absolute = 1000000; + break; + case 'g': + case 'G': + absolute = 1000000000; + break; + default: + break; + } + if (absolute) { + unsigned long long bytes; + unsigned long unit; + + bytes = (unsigned long long) i * absolute; + unit = sector_size * units_per_sector; + bytes += unit/2; /* round */ + bytes /= unit; + i = bytes; + } + if (minus) + i = -i; + i += base; + } else { + i = atoi(line_ptr); + while (isdigit(*line_ptr)) { + line_ptr++; + use_default = 0; + } + } + if (use_default) + printf(_("Using default value %u\n"), i = dflt); + if (i >= low && i <= high) + break; + else + printf(_("Value out of range.\n")); + } + return i; +} + +int +get_partition(int warn, int max) { + struct pte *pe; + int i; + + i = read_int(1, 0, max, 0, _("Partition number")) - 1; + pe = &ptes[i]; + + if (warn) { + if ((!sun_label && !sgi_label && !pe->part_table->sys_ind) +#ifdef CONFIG_FEATURE_SUN_LABEL + || (sun_label && + (!sunlabel->partitions[i].num_sectors || + !sunlabel->infos[i].id)) +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + || (sgi_label && (!sgi_get_num_sectors(i))) +#endif + ) + fprintf(stderr, + _("Warning: partition %d has empty type\n"), + i+1); + } + return i; +} + +static int +get_existing_partition(int warn, int max) { + int pno = -1; + int i; + + for (i = 0; i < max; i++) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + + if (p && !is_cleared_partition(p)) { + if (pno >= 0) + goto not_unique; + pno = i; + } + } + if (pno >= 0) { + printf(_("Selected partition %d\n"), pno+1); + return pno; + } + printf(_("No partition is defined yet!\n")); + return -1; + + not_unique: + return get_partition(warn, max); +} + +static int +get_nonexisting_partition(int warn, int max) { + int pno = -1; + int i; + + for (i = 0; i < max; i++) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + + if (p && is_cleared_partition(p)) { + if (pno >= 0) + goto not_unique; + pno = i; + } + } + if (pno >= 0) { + printf(_("Selected partition %d\n"), pno+1); + return pno; + } + printf(_("All primary partitions have been defined already!\n")); + return -1; + + not_unique: + return get_partition(warn, max); +} + + +void change_units(void) +{ + display_in_cyl_units = !display_in_cyl_units; + update_units(); + printf(_("Changing display/entry units to %s\n"), + str_units(PLURAL)); +} + +static void +toggle_active(int i) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + + if (IS_EXTENDED (p->sys_ind) && !p->boot_ind) + fprintf(stderr, + _("WARNING: Partition %d is an extended partition\n"), + i + 1); + p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG); + pe->changed = 1; +} + +static void +toggle_dos_compatibility_flag(void) { + dos_compatible_flag = ~dos_compatible_flag; + if (dos_compatible_flag) { + sector_offset = sectors; + printf(_("DOS Compatibility flag is set\n")); + } + else { + sector_offset = 1; + printf(_("DOS Compatibility flag is not set\n")); + } +} + +static void +delete_partition(int i) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + struct partition *q = pe->ext_pointer; + +/* Note that for the fifth partition (i == 4) we don't actually + * decrement partitions. + */ + + if (warn_geometry()) + return; /* C/H/S not set */ + pe->changed = 1; + +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) { + sun_delete_partition(i); + return; + } +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) { + sgi_delete_partition(i); + return; + } +#endif + + if (i < 4) { + if (IS_EXTENDED (p->sys_ind) && i == ext_index) { + partitions = 4; + ptes[ext_index].ext_pointer = NULL; + extended_offset = 0; + } + clear_partition(p); + return; + } + + if (!q->sys_ind && i > 4) { + /* the last one in the chain - just delete */ + --partitions; + --i; + clear_partition(ptes[i].ext_pointer); + ptes[i].changed = 1; + } else { + /* not the last one - further ones will be moved down */ + if (i > 4) { + /* delete this link in the chain */ + p = ptes[i-1].ext_pointer; + *p = *q; + set_start_sect(p, get_start_sect(q)); + set_nr_sects(p, get_nr_sects(q)); + ptes[i-1].changed = 1; + } else if (partitions > 5) { /* 5 will be moved to 4 */ + /* the first logical in a longer chain */ + pe = &ptes[5]; + + if (pe->part_table) /* prevent SEGFAULT */ + set_start_sect(pe->part_table, + get_partition_start(pe) - + extended_offset); + pe->offset = extended_offset; + pe->changed = 1; + } + + if (partitions > 5) { + partitions--; + while (i < partitions) { + ptes[i] = ptes[i+1]; + i++; + } + } else + /* the only logical: clear only */ + clear_partition(ptes[i].part_table); + } +} + +static void +change_sysid(void) { + int i, sys, origsys; + struct partition *p; + +#ifdef CONFIG_FEATURE_SGI_LABEL + /* If sgi_label then don't use get_existing_partition, + let the user select a partition, since get_existing_partition() + only works for Linux like partition tables. */ + if (!sgi_label) { + i = get_existing_partition(0, partitions); + } else { + i = get_partition(0, partitions); + } +#else + i = get_existing_partition(0, partitions); +#endif + if (i == -1) + return; + p = ptes[i].part_table; + origsys = sys = get_sysid(i); + + /* if changing types T to 0 is allowed, then + the reverse change must be allowed, too */ + if (!sys && !sgi_label && !sun_label && !get_nr_sects(p)) + printf(_("Partition %d does not exist yet!\n"), i + 1); + else while (1) { + sys = read_hex (get_sys_types()); + + if (!sys && !sgi_label && !sun_label) { + printf(_("Type 0 means free space to many systems\n" + "(but not to Linux). Having partitions of\n" + "type 0 is probably unwise. You can delete\n" + "a partition using the `d' command.\n")); + /* break; */ + } + + if (!sun_label && !sgi_label) { + if (IS_EXTENDED (sys) != IS_EXTENDED (p->sys_ind)) { + printf(_("You cannot change a partition into" + " an extended one or vice versa\n" + "Delete it first.\n")); + break; + } + } + + if (sys < 256) { +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label && i == 2 && sys != WHOLE_DISK) + printf(_("Consider leaving partition 3 " + "as Whole disk (5),\n" + "as SunOS/Solaris expects it and " + "even Linux likes it.\n\n")); +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label && ((i == 10 && sys != ENTIRE_DISK) + || (i == 8 && sys != 0))) + printf(_("Consider leaving partition 9 " + "as volume header (0),\nand " + "partition 11 as entire volume (6)" + "as IRIX expects it.\n\n")); +#endif + if (sys == origsys) + break; +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) { + sun_change_sysid(i, sys); + } else +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) { + sgi_change_sysid(i, sys); + } else +#endif + p->sys_ind = sys; + printf (_("Changed system type of partition %d " + "to %x (%s)\n"), i + 1, sys, + partition_type(sys)); + ptes[i].changed = 1; + if (is_dos_partition(origsys) || + is_dos_partition(sys)) + dos_changed = 1; + break; + } + } +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + + +/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993, + * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross, + * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S. + * Lubkin Oct. 1991). */ + +static void long2chs(ulong ls, uint *c, uint *h, uint *s) { + int spc = heads * sectors; + + *c = ls / spc; + ls = ls % spc; + *h = ls / sectors; + *s = ls % sectors + 1; /* sectors count from 1 */ +} + +static void check_consistency(const struct partition *p, int partition) { + uint pbc, pbh, pbs; /* physical beginning c, h, s */ + uint pec, peh, pes; /* physical ending c, h, s */ + uint lbc, lbh, lbs; /* logical beginning c, h, s */ + uint lec, leh, les; /* logical ending c, h, s */ + + if (!heads || !sectors || (partition >= 4)) + return; /* do not check extended partitions */ + +/* physical beginning c, h, s */ + pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300); + pbh = p->head; + pbs = p->sector & 0x3f; + +/* physical ending c, h, s */ + pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300); + peh = p->end_head; + pes = p->end_sector & 0x3f; + +/* compute logical beginning (c, h, s) */ + long2chs(get_start_sect(p), &lbc, &lbh, &lbs); + +/* compute logical ending (c, h, s) */ + long2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les); + +/* Same physical / logical beginning? */ + if (cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) { + printf(_("Partition %d has different physical/logical " + "beginnings (non-Linux?):\n"), partition + 1); + printf(_(" phys=(%d, %d, %d) "), pbc, pbh, pbs); + printf(_("logical=(%d, %d, %d)\n"),lbc, lbh, lbs); + } + +/* Same physical / logical ending? */ + if (cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) { + printf(_("Partition %d has different physical/logical " + "endings:\n"), partition + 1); + printf(_(" phys=(%d, %d, %d) "), pec, peh, pes); + printf(_("logical=(%d, %d, %d)\n"),lec, leh, les); + } + +#if 0 +/* Beginning on cylinder boundary? */ + if (pbh != !pbc || pbs != 1) { + printf(_("Partition %i does not start on cylinder " + "boundary:\n"), partition + 1); + printf(_(" phys=(%d, %d, %d) "), pbc, pbh, pbs); + printf(_("should be (%d, %d, 1)\n"), pbc, !pbc); + } +#endif + +/* Ending on cylinder boundary? */ + if (peh != (heads - 1) || pes != sectors) { + printf(_("Partition %i does not end on cylinder boundary.\n"), + partition + 1); +#if 0 + printf(_(" phys=(%d, %d, %d) "), pec, peh, pes); + printf(_("should be (%d, %d, %d)\n"), + pec, heads - 1, sectors); +#endif + } +} + +static void +list_disk_geometry(void) { + long long bytes = (total_number_of_sectors << 9); + long megabytes = bytes/1000000; + + if (megabytes < 10000) + printf(_("\nDisk %s: %ld MB, %lld bytes\n"), + disk_device, megabytes, bytes); + else + printf(_("\nDisk %s: %ld.%ld GB, %lld bytes\n"), + disk_device, megabytes/1000, (megabytes/100)%10, bytes); + printf(_("%d heads, %d sectors/track, %d cylinders"), + heads, sectors, cylinders); + if (units_per_sector == 1) + printf(_(", total %llu sectors"), + total_number_of_sectors / (sector_size/512)); + printf(_("\nUnits = %s of %d * %d = %d bytes\n\n"), + str_units(PLURAL), + units_per_sector, sector_size, units_per_sector * sector_size); +} + +/* + * Check whether partition entries are ordered by their starting positions. + * Return 0 if OK. Return i if partition i should have been earlier. + * Two separate checks: primary and logical partitions. + */ +static int +wrong_p_order(int *prev) { + const struct pte *pe; + const struct partition *p; + off_t last_p_start_pos = 0, p_start_pos; + int i, last_i = 0; + + for (i = 0 ; i < partitions; i++) { + if (i == 4) { + last_i = 4; + last_p_start_pos = 0; + } + pe = &ptes[i]; + if ((p = pe->part_table)->sys_ind) { + p_start_pos = get_partition_start(pe); + + if (last_p_start_pos > p_start_pos) { + if (prev) + *prev = last_i; + return i; + } + + last_p_start_pos = p_start_pos; + last_i = i; + } + } + return 0; +} + +#ifdef CONFIG_FEATURE_FDISK_ADVANCED +/* + * Fix the chain of logicals. + * extended_offset is unchanged, the set of sectors used is unchanged + * The chain is sorted so that sectors increase, and so that + * starting sectors increase. + * + * After this it may still be that cfdisk doesnt like the table. + * (This is because cfdisk considers expanded parts, from link to + * end of partition, and these may still overlap.) + * Now + * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda + * may help. + */ +static void +fix_chain_of_logicals(void) { + int j, oj, ojj, sj, sjj; + struct partition *pj,*pjj,tmp; + + /* Stage 1: sort sectors but leave sector of part 4 */ + /* (Its sector is the global extended_offset.) */ + stage1: + for (j = 5; j < partitions-1; j++) { + oj = ptes[j].offset; + ojj = ptes[j+1].offset; + if (oj > ojj) { + ptes[j].offset = ojj; + ptes[j+1].offset = oj; + pj = ptes[j].part_table; + set_start_sect(pj, get_start_sect(pj)+oj-ojj); + pjj = ptes[j+1].part_table; + set_start_sect(pjj, get_start_sect(pjj)+ojj-oj); + set_start_sect(ptes[j-1].ext_pointer, + ojj-extended_offset); + set_start_sect(ptes[j].ext_pointer, + oj-extended_offset); + goto stage1; + } + } + + /* Stage 2: sort starting sectors */ + stage2: + for (j = 4; j < partitions-1; j++) { + pj = ptes[j].part_table; + pjj = ptes[j+1].part_table; + sj = get_start_sect(pj); + sjj = get_start_sect(pjj); + oj = ptes[j].offset; + ojj = ptes[j+1].offset; + if (oj+sj > ojj+sjj) { + tmp = *pj; + *pj = *pjj; + *pjj = tmp; + set_start_sect(pj, ojj+sjj-oj); + set_start_sect(pjj, oj+sj-ojj); + goto stage2; + } + } + + /* Probably something was changed */ + for (j = 4; j < partitions; j++) + ptes[j].changed = 1; +} + + +static void +fix_partition_table_order(void) { + struct pte *pei, *pek; + int i,k; + + if (!wrong_p_order(NULL)) { + printf(_("Nothing to do. Ordering is correct already.\n\n")); + return; + } + + while ((i = wrong_p_order(&k)) != 0 && i < 4) { + /* partition i should have come earlier, move it */ + /* We have to move data in the MBR */ + struct partition *pi, *pk, *pe, pbuf; + pei = &ptes[i]; + pek = &ptes[k]; + + pe = pei->ext_pointer; + pei->ext_pointer = pek->ext_pointer; + pek->ext_pointer = pe; + + pi = pei->part_table; + pk = pek->part_table; + + memmove(&pbuf, pi, sizeof(struct partition)); + memmove(pi, pk, sizeof(struct partition)); + memmove(pk, &pbuf, sizeof(struct partition)); + + pei->changed = pek->changed = 1; + } + + if (i) + fix_chain_of_logicals(); + + printf("Done.\n"); + +} +#endif + +static void +list_table(int xtra) { + const struct partition *p; + int i, w; + +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) { + sun_list_table(xtra); + return; + } +#endif + +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) { + sgi_list_table(xtra); + return; + } +#endif + + list_disk_geometry(); + +#ifdef CONFIG_FEATURE_OSF_LABEL + if (osf_label) { + xbsd_print_disklabel(xtra); + return; + } +#endif + + /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3, + but if the device name ends in a digit, say /dev/foo1, + then the partition is called /dev/foo1p3. */ + w = strlen(disk_device); + if (w && isdigit(disk_device[w-1])) + w++; + if (w < 5) + w = 5; + + printf(_("%*s Boot Start End Blocks Id System\n"), + w+1, _("Device")); + + for (i = 0; i < partitions; i++) { + const struct pte *pe = &ptes[i]; + + p = pe->part_table; + if (p && !is_cleared_partition(p)) { + off_t psects = get_nr_sects(p); + off_t pblocks = psects; + unsigned int podd = 0; + + if (sector_size < 1024) { + pblocks /= (1024 / sector_size); + podd = psects % (1024 / sector_size); + } + if (sector_size > 1024) + pblocks *= (sector_size / 1024); + printf( + "%s %c %11llu %11llu %11llu%c %2x %s\n", + partname(disk_device, i+1, w+2), +/* boot flag */ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG + ? '*' : '?', +/* start */ (unsigned long long) cround(get_partition_start(pe)), +/* end */ (unsigned long long) cround(get_partition_start(pe) + psects + - (psects ? 1 : 0)), +/* odd flag on end */ (unsigned long long) pblocks, podd ? '+' : ' ', +/* type id */ p->sys_ind, +/* type name */ partition_type(p->sys_ind)); + check_consistency(p, i); + } + } + + /* Is partition table in disk order? It need not be, but... */ + /* partition table entries are not checked for correct order if this + is a sgi, sun or aix labeled disk... */ + if (dos_label && wrong_p_order(NULL)) { + printf(_("\nPartition table entries are not in disk order\n")); + } +} + +#ifdef CONFIG_FEATURE_FDISK_ADVANCED +static void +x_list_table(int extend) { + const struct pte *pe; + const struct partition *p; + int i; + + printf(_("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n"), + disk_device, heads, sectors, cylinders); + printf(_("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n")); + for (i = 0 ; i < partitions; i++) { + pe = &ptes[i]; + p = (extend ? pe->ext_pointer : pe->part_table); + if (p != NULL) { + printf("%2d %02x%4d%4d%5d%4d%4d%5d%11u%11u %02x\n", + i + 1, p->boot_ind, p->head, + sector(p->sector), + cylinder(p->sector, p->cyl), p->end_head, + sector(p->end_sector), + cylinder(p->end_sector, p->end_cyl), + get_start_sect(p), get_nr_sects(p), p->sys_ind); + if (p->sys_ind) + check_consistency(p, i); + } + } +} +#endif + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +fill_bounds(off_t *first, off_t *last) { + int i; + const struct pte *pe = &ptes[0]; + const struct partition *p; + + for (i = 0; i < partitions; pe++,i++) { + p = pe->part_table; + if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) { + first[i] = 0xffffffff; + last[i] = 0; + } else { + first[i] = get_partition_start(pe); + last[i] = first[i] + get_nr_sects(p) - 1; + } + } +} + +static void +check(int n, uint h, uint s, uint c, off_t start) { + off_t total, real_s, real_c; + + real_s = sector(s) - 1; + real_c = cylinder(s, c); + total = (real_c * sectors + real_s) * heads + h; + if (!total) + fprintf(stderr, _("Warning: partition %d contains sector 0\n"), n); + if (h >= heads) + fprintf(stderr, + _("Partition %d: head %d greater than maximum %d\n"), + n, h + 1, heads); + if (real_s >= sectors) + fprintf(stderr, _("Partition %d: sector %d greater than " + "maximum %d\n"), n, s, sectors); + if (real_c >= cylinders) + fprintf(stderr, _("Partitions %d: cylinder %llu greater than " + "maximum %d\n"), n, (unsigned long long)real_c + 1, cylinders); + if (cylinders <= 1024 && start != total) + fprintf(stderr, + _("Partition %d: previous sectors %llu disagrees with " + "total %llu\n"), n, (unsigned long long)start, (unsigned long long)total); +} + +static void +verify(void) { + int i, j; + uint total = 1; + off_t first[partitions], last[partitions]; + struct partition *p; + + if (warn_geometry()) + return; + +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) { + verify_sun(); + return; + } +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) { + verify_sgi(1); + return; + } +#endif + + fill_bounds(first, last); + for (i = 0; i < partitions; i++) { + struct pte *pe = &ptes[i]; + + p = pe->part_table; + if (p->sys_ind && !IS_EXTENDED (p->sys_ind)) { + check_consistency(p, i); + if (get_partition_start(pe) < first[i]) + printf(_("Warning: bad start-of-data in " + "partition %d\n"), i + 1); + check(i + 1, p->end_head, p->end_sector, p->end_cyl, + last[i]); + total += last[i] + 1 - first[i]; + for (j = 0; j < i; j++) + if ((first[i] >= first[j] && first[i] <= last[j]) + || ((last[i] <= last[j] && last[i] >= first[j]))) { + printf(_("Warning: partition %d overlaps " + "partition %d.\n"), j + 1, i + 1); + total += first[i] >= first[j] ? + first[i] : first[j]; + total -= last[i] <= last[j] ? + last[i] : last[j]; + } + } + } + + if (extended_offset) { + struct pte *pex = &ptes[ext_index]; + off_t e_last = get_start_sect(pex->part_table) + + get_nr_sects(pex->part_table) - 1; + + for (i = 4; i < partitions; i++) { + total++; + p = ptes[i].part_table; + if (!p->sys_ind) { + if (i != 4 || i + 1 < partitions) + printf(_("Warning: partition %d " + "is empty\n"), i + 1); + } + else if (first[i] < extended_offset || + last[i] > e_last) + printf(_("Logical partition %d not entirely in " + "partition %d\n"), i + 1, ext_index + 1); + } + } + + if (total > heads * sectors * cylinders) + printf(_("Total allocated sectors %d greater than the maximum " + "%d\n"), total, heads * sectors * cylinders); + else if ((total = heads * sectors * cylinders - total) != 0) + printf(_("%d unallocated sectors\n"), total); +} + +static void +add_partition(int n, int sys) { + char mesg[256]; /* 48 does not suffice in Japanese */ + int i, readed = 0; + struct partition *p = ptes[n].part_table; + struct partition *q = ptes[ext_index].part_table; + long long llimit; + off_t start, stop = 0, limit, temp, + first[partitions], last[partitions]; + + if (p && p->sys_ind) { + printf(_("Partition %d is already defined. Delete " + "it before re-adding it.\n"), n + 1); + return; + } + fill_bounds(first, last); + if (n < 4) { + start = sector_offset; + if (display_in_cyl_units || !total_number_of_sectors) + llimit = heads * sectors * cylinders - 1; + else + llimit = total_number_of_sectors - 1; + limit = llimit; + if (limit != llimit) + limit = 0x7fffffff; + if (extended_offset) { + first[ext_index] = extended_offset; + last[ext_index] = get_start_sect(q) + + get_nr_sects(q) - 1; + } + } else { + start = extended_offset + sector_offset; + limit = get_start_sect(q) + get_nr_sects(q) - 1; + } + if (display_in_cyl_units) + for (i = 0; i < partitions; i++) + first[i] = (cround(first[i]) - 1) * units_per_sector; + + snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); + do { + temp = start; + for (i = 0; i < partitions; i++) { + int lastplusoff; + + if (start == ptes[i].offset) + start += sector_offset; + lastplusoff = last[i] + ((n<4) ? 0 : sector_offset); + if (start >= first[i] && start <= lastplusoff) + start = lastplusoff + 1; + } + if (start > limit) + break; + if (start >= temp+units_per_sector && readed) { + printf(_("Sector %llu is already allocated\n"), (unsigned long long)temp); + temp = start; + readed = 0; + } + if (!readed && start == temp) { + off_t saved_start; + + saved_start = start; + start = read_int(cround(saved_start), cround(saved_start), cround(limit), + 0, mesg); + if (display_in_cyl_units) { + start = (start - 1) * units_per_sector; + if (start < saved_start) start = saved_start; + } + readed = 1; + } + } while (start != temp || !readed); + if (n > 4) { /* NOT for fifth partition */ + struct pte *pe = &ptes[n]; + + pe->offset = start - sector_offset; + if (pe->offset == extended_offset) { /* must be corrected */ + pe->offset++; + if (sector_offset == 1) + start++; + } + } + + for (i = 0; i < partitions; i++) { + struct pte *pe = &ptes[i]; + + if (start < pe->offset && limit >= pe->offset) + limit = pe->offset - 1; + if (start < first[i] && limit >= first[i]) + limit = first[i] - 1; + } + if (start > limit) { + printf(_("No free sectors available\n")); + if (n > 4) + partitions--; + return; + } + if (cround(start) == cround(limit)) { + stop = limit; + } else { + snprintf(mesg, sizeof(mesg), + _("Last %s or +size or +sizeM or +sizeK"), + str_units(SINGULAR)); + stop = read_int(cround(start), cround(limit), cround(limit), + cround(start), mesg); + if (display_in_cyl_units) { + stop = stop * units_per_sector - 1; + if (stop >limit) + stop = limit; + } + } + + set_partition(n, 0, start, stop, sys); + if (n > 4) + set_partition(n - 1, 1, ptes[n].offset, stop, EXTENDED); + + if (IS_EXTENDED (sys)) { + struct pte *pe4 = &ptes[4]; + struct pte *pen = &ptes[n]; + + ext_index = n; + pen->ext_pointer = p; + pe4->offset = extended_offset = start; + pe4->sectorbuffer = xcalloc(1, sector_size); + pe4->part_table = pt_offset(pe4->sectorbuffer, 0); + pe4->ext_pointer = pe4->part_table + 1; + pe4->changed = 1; + partitions = 5; + } +} + +static void +add_logical(void) { + if (partitions > 5 || ptes[4].part_table->sys_ind) { + struct pte *pe = &ptes[partitions]; + + pe->sectorbuffer = xcalloc(1, sector_size); + pe->part_table = pt_offset(pe->sectorbuffer, 0); + pe->ext_pointer = pe->part_table + 1; + pe->offset = 0; + pe->changed = 1; + partitions++; + } + add_partition(partitions - 1, LINUX_NATIVE); +} + +static void +new_partition(void) { + int i, free_primary = 0; + + if (warn_geometry()) + return; + +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) { + add_sun_partition(get_partition(0, partitions), LINUX_NATIVE); + return; + } +#endif +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) { + sgi_add_partition(get_partition(0, partitions), LINUX_NATIVE); + return; + } +#endif +#ifdef CONFIG_FEATURE_AIX_LABEL + if (aix_label) { + printf(_("\tSorry - this fdisk cannot handle AIX disk labels." + "\n\tIf you want to add DOS-type partitions, create" + "\n\ta new empty DOS partition table first. (Use o.)" + "\n\tWARNING: " + "This will destroy the present disk contents.\n")); + return; + } +#endif + + for (i = 0; i < 4; i++) + free_primary += !ptes[i].part_table->sys_ind; + + if (!free_primary && partitions >= MAXIMUM_PARTS) { + printf(_("The maximum number of partitions has been created\n")); + return; + } + + if (!free_primary) { + if (extended_offset) + add_logical(); + else + printf(_("You must delete some partition and add " + "an extended partition first\n")); + } else { + char c, line[LINE_LENGTH]; + snprintf(line, sizeof(line), "%s\n %s\n p primary " + "partition (1-4)\n", + "Command action", (extended_offset ? + "l logical (5 or over)" : "e extended")); + while (1) { + if ((c = read_char(line)) == 'p' || c == 'P') { + i = get_nonexisting_partition(0, 4); + if (i >= 0) + add_partition(i, LINUX_NATIVE); + return; + } + else if (c == 'l' && extended_offset) { + add_logical(); + return; + } + else if (c == 'e' && !extended_offset) { + i = get_nonexisting_partition(0, 4); + if (i >= 0) + add_partition(i, EXTENDED); + return; + } + else + printf(_("Invalid partition number " + "for type `%c'\n"), c); + } + } +} + +static void +write_table(void) { + int i; + + if (dos_label) { + for (i=0; i<3; i++) + if (ptes[i].changed) + ptes[3].changed = 1; + for (i = 3; i < partitions; i++) { + struct pte *pe = &ptes[i]; + + if (pe->changed) { + write_part_table_flag(pe->sectorbuffer); + write_sector(pe->offset, pe->sectorbuffer); + } + } + } +#ifdef CONFIG_FEATURE_SGI_LABEL + else if (sgi_label) { + /* no test on change? the printf below might be mistaken */ + sgi_write_table(); + } +#endif +#ifdef CONFIG_FEATURE_SUN_LABEL + else if (sun_label) { + int needw = 0; + + for (i=0; i<8; i++) + if (ptes[i].changed) + needw = 1; + if (needw) + sun_write_table(); + } +#endif + + printf(_("The partition table has been altered!\n\n")); + reread_partition_table(1); +} + +void +reread_partition_table(int leave) { + int error = 0; + int i; + + printf(_("Calling ioctl() to re-read partition table.\n")); + sync(); + sleep(2); + if ((i = ioctl(fd, BLKRRPART)) != 0) { + error = errno; + } else { + /* some kernel versions (1.2.x) seem to have trouble + rereading the partition table, but if asked to do it + twice, the second time works. - biro@yggdrasil.com */ + sync(); + sleep(2); + if ((i = ioctl(fd, BLKRRPART)) != 0) + error = errno; + } + + if (i) { + printf(_("\nWARNING: Re-reading the partition table " + "failed with error %d: %s.\n" + "The kernel still uses the old table.\n" + "The new table will be used " + "at the next reboot.\n"), + error, strerror(error)); + } + + if (dos_changed) + printf( + _("\nWARNING: If you have created or modified any DOS 6.x\n" + "partitions, please see the fdisk manual page for additional\n" + "information.\n")); + + if (leave) { + close(fd); + + printf(_("Syncing disks.\n")); + sync(); + sleep(4); /* for sync() */ + exit(!!i); + } +} +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ + +#ifdef CONFIG_FEATURE_FDISK_ADVANCED +#define MAX_PER_LINE 16 +static void +print_buffer(char pbuffer[]) { + int i, + l; + + for (i = 0, l = 0; i < sector_size; i++, l++) { + if (l == 0) + printf("0x%03X:", i); + printf(" %02X", (unsigned char) pbuffer[i]); + if (l == MAX_PER_LINE - 1) { + printf("\n"); + l = -1; + } + } + if (l > 0) + printf("\n"); + printf("\n"); +} + + +static void +print_raw(void) { + int i; + + printf(_("Device: %s\n"), disk_device); +#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_SUN_LABEL) + if (sun_label || sgi_label) + print_buffer(MBRbuffer); + else +#endif + for (i = 3; i < partitions; i++) + print_buffer(ptes[i].sectorbuffer); +} + +static void +move_begin(int i) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + off_t new, first; + + if (warn_geometry()) + return; + if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED (p->sys_ind)) { + printf(_("Partition %d has no data area\n"), i + 1); + return; + } + first = get_partition_start(pe); + new = read_int(first, first, first + get_nr_sects(p) - 1, first, + _("New beginning of data")) - pe->offset; + + if (new != get_nr_sects(p)) { + first = get_nr_sects(p) + get_start_sect(p) - new; + set_nr_sects(p, first); + set_start_sect(p, new); + pe->changed = 1; + } +} + +static void +xselect(void) { + char c; + + while(1) { + putchar('\n'); + c = tolower(read_char(_("Expert command (m for help): "))); + switch (c) { + case 'a': +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) + sun_set_alt_cyl(); +#endif + break; + case 'b': + if (dos_label) + move_begin(get_partition(0, partitions)); + break; + case 'c': + user_cylinders = cylinders = + read_int(1, cylinders, 1048576, 0, + _("Number of cylinders")); +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) + sun_set_ncyl(cylinders); +#endif + if (dos_label) + warn_cylinders(); + break; + case 'd': + print_raw(); + break; + case 'e': +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) + sgi_set_xcyl(); + else +#endif +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) + sun_set_xcyl(); + else +#endif + if (dos_label) + x_list_table(1); + break; + case 'f': + if (dos_label) + fix_partition_table_order(); + break; + case 'g': +#ifdef CONFIG_FEATURE_SGI_LABEL + create_sgilabel(); +#endif + break; + case 'h': + user_heads = heads = read_int(1, heads, 256, 0, + _("Number of heads")); + update_units(); + break; + case 'i': +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) + sun_set_ilfact(); +#endif + break; + case 'o': +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) + sun_set_rspeed(); +#endif + break; + case 'p': +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) + list_table(1); + else +#endif + x_list_table(0); + break; + case 'q': + close(fd); + printf("\n"); + exit(0); + case 'r': + return; + case 's': + user_sectors = sectors = read_int(1, sectors, 63, 0, + _("Number of sectors")); + if (dos_compatible_flag) { + sector_offset = sectors; + fprintf(stderr, _("Warning: setting " + "sector offset for DOS " + "compatiblity\n")); + } + update_units(); + break; + case 'v': + verify(); + break; + case 'w': + write_table(); /* does not return */ + break; + case 'y': +#ifdef CONFIG_FEATURE_SUN_LABEL + if (sun_label) + sun_set_pcylcount(); +#endif + break; + default: + xmenu(); + } + } +} +#endif /* ADVANCED mode */ + +static int +is_ide_cdrom_or_tape(const char *device) { + FILE *procf; + char buf[100]; + struct stat statbuf; + int is_ide = 0; + + /* No device was given explicitly, and we are trying some + likely things. But opening /dev/hdc may produce errors like + "hdc: tray open or drive not ready" + if it happens to be a CD-ROM drive. It even happens that + the process hangs on the attempt to read a music CD. + So try to be careful. This only works since 2.1.73. */ + + if (strncmp("/dev/hd", device, 7)) + return 0; + + snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5); + procf = fopen(buf, "r"); + if (procf != NULL && fgets(buf, sizeof(buf), procf)) + is_ide = (!strncmp(buf, "cdrom", 5) || + !strncmp(buf, "tape", 4)); + else + /* Now when this proc file does not exist, skip the + device when it is read-only. */ + if (stat(device, &statbuf) == 0) + is_ide = ((statbuf.st_mode & 0222) == 0); + + if (procf) + fclose(procf); + return is_ide; +} + +static void +try(const char *device, int user_specified) { + int gb; + + disk_device = device; + if (setjmp(listingbuf)) + return; + if (!user_specified) + if (is_ide_cdrom_or_tape(device)) + return; + if ((fd = open(disk_device, type_open)) >= 0) { + gb = get_boot(try_only); + if (gb > 0) { /* I/O error */ + close(fd); + } else if (gb < 0) { /* no DOS signature */ + list_disk_geometry(); + if (aix_label) + return; +#ifdef CONFIG_FEATURE_OSF_LABEL + if (btrydev(device) < 0) +#endif + fprintf(stderr, + _("Disk %s doesn't contain a valid " + "partition table\n"), device); + close(fd); + } else { + close(fd); + list_table(0); +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + if (!sun_label && partitions > 4) + delete_partition(ext_index); +#endif + } + } else { + /* Ignore other errors, since we try IDE + and SCSI hard disks which may not be + installed on the system. */ + if (errno == EACCES) { + fprintf(stderr, _("Cannot open %s\n"), device); + return; + } + } +} + +/* for fdisk -l: try all things in /proc/partitions + that look like a partition name (do not end in a digit) */ +static void +tryprocpt(void) { + FILE *procpt; + char line[100], ptname[100], devname[120], *s; + int ma, mi, sz; + + procpt = bb_wfopen(PROC_PARTITIONS, "r"); + + while (fgets(line, sizeof(line), procpt)) { + if (sscanf (line, " %d %d %d %[^\n ]", + &ma, &mi, &sz, ptname) != 4) + continue; + for (s = ptname; *s; s++); + if (isdigit(s[-1])) + continue; + sprintf(devname, "/dev/%s", ptname); + try(devname, 0); + } +#ifdef CONFIG_FEATURE_CLEAN_UP + fclose(procpt); +#endif +} + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE +static void +unknown_command(int c) { + printf(_("%c: unknown command\n"), c); +} +#endif + +int fdisk_main(int argc, char **argv) { + int c; +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + int optl = 0; +#endif +#ifdef CONFIG_FEATURE_FDISK_BLKSIZE + int opts = 0; +#endif + /* + * Calls: + * fdisk -v + * fdisk -l [-b sectorsize] [-u] device ... + * fdisk -s [partition] ... + * fdisk [-b sectorsize] [-u] device + * + * Options -C, -H, -S set the geometry. + * + */ + while ((c = getopt(argc, argv, "b:C:H:lS:uvV" +#ifdef CONFIG_FEATURE_FDISK_BLKSIZE + "s" +#endif + )) != -1) { + switch (c) { + case 'b': + /* Ugly: this sector size is really per device, + so cannot be combined with multiple disks, + and te same goes for the C/H/S options. + */ + sector_size = atoi(optarg); + if (sector_size != 512 && sector_size != 1024 && + sector_size != 2048) + bb_show_usage(); + sector_offset = 2; + user_set_sector_size = 1; + break; + case 'C': + user_cylinders = atoi(optarg); + break; + case 'H': + user_heads = atoi(optarg); + if (user_heads <= 0 || user_heads >= 256) + user_heads = 0; + break; + case 'S': + user_sectors = atoi(optarg); + if (user_sectors <= 0 || user_sectors >= 64) + user_sectors = 0; + break; + case 'l': +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + optl = 1; +#endif + break; +#ifdef CONFIG_FEATURE_FDISK_BLKSIZE + case 's': + opts = 1; + break; +#endif + case 'u': + display_in_cyl_units = 0; + break; + case 'V': + case 'v': + printf("fdisk v" UTIL_LINUX_VERSION "\n"); + return 0; + default: + bb_show_usage(); + } + } + +#if 0 + printf(_("This kernel finds the sector size itself - " + "-b option ignored\n")); +#else + if (user_set_sector_size && argc-optind != 1) + printf(_("Warning: the -b (set sector size) option should" + " be used with one specified device\n")); +#endif + +#ifdef CONFIG_FEATURE_FDISK_WRITABLE + if (optl) { + nowarn = 1; +#endif + type_open = O_RDONLY; + if (argc > optind) { + int k; +#if __GNUC__ + /* avoid gcc warning: + variable `k' might be clobbered by `longjmp' */ + (void)&k; +#endif + listing = 1; + for (k=optind; k= 0) + delete_partition(j); + } + break; + case 'i': +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) + create_sgiinfo(); + else +#endif + unknown_command(c); + case 'l': + list_types(get_sys_types()); + break; + case 'm': + menu(); + break; + case 'n': + new_partition(); + break; + case 'o': + create_doslabel(); + break; + case 'p': + list_table(0); + break; + case 'q': + close(fd); + printf("\n"); + return 0; + case 's': +#ifdef CONFIG_FEATURE_SUN_LABEL + create_sunlabel(); +#endif + break; + case 't': + change_sysid(); + break; + case 'u': + change_units(); + break; + case 'v': + verify(); + break; + case 'w': + write_table(); /* does not return */ + break; +#ifdef CONFIG_FEATURE_FDISK_ADVANCED + case 'x': +#ifdef CONFIG_FEATURE_SGI_LABEL + if (sgi_label) { + fprintf(stderr, + _("\n\tSorry, no experts menu for SGI " + "partition tables available.\n\n")); + } else +#endif + + xselect(); + break; +#endif + default: + unknown_command(c); + menu(); + } + } + return 0; +#endif /* CONFIG_FEATURE_FDISK_WRITABLE */ +} diff --git a/busybox/util-linux/freeramdisk.c b/busybox/util-linux/freeramdisk.c new file mode 100644 index 000000000..e5061dc34 --- /dev/null +++ b/busybox/util-linux/freeramdisk.c @@ -0,0 +1,69 @@ +/* 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 result; + int fd; + + if (argc != 2) { + bb_show_usage(); + } + + fd = bb_xopen(argv[1], O_RDWR); + + result = ioctl(fd, BLKFLSBUF); +#ifdef CONFIG_FEATURE_CLEAN_UP + close(fd); +#endif + if (result < 0) { + bb_perror_msg_and_die("failed ioctl on %s", argv[1]); + } + + /* Don't bother closing. Exit does + * that, so we can save a few bytes */ + return EXIT_SUCCESS; +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ + diff --git a/busybox/util-linux/fsck_minix.c b/busybox/util-linux/fsck_minix.c new file mode 100644 index 000000000..f6d7deaab --- /dev/null +++ b/busybox/util-linux/fsck_minix.c @@ -0,0 +1,1476 @@ +/* vi: set sw=4 ts=4: */ +/* + * fsck.c - a file system consistency checker for Linux. + * + * (C) 1991, 1992 Linus Torvalds. This file may be redistributed + * as per the GNU copyleft. + */ + +/* + * 09.11.91 - made the first rudimentary functions + * + * 10.11.91 - updated, does checking, no repairs yet. + * Sent out to the mailing-list for testing. + * + * 14.11.91 - Testing seems to have gone well. Added some + * correction-code, and changed some functions. + * + * 15.11.91 - More correction code. Hopefully it notices most + * cases now, and tries to do something about them. + * + * 16.11.91 - More corrections (thanks to Mika Jalava). Most + * things seem to work now. Yeah, sure. + * + * + * 19.04.92 - Had to start over again from this old version, as a + * kernel bug ate my enhanced fsck in february. + * + * 28.02.93 - added support for different directory entry sizes.. + * + * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with + * super-block information + * + * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform + * to that required by fsutil + * + * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu) + * Added support for file system valid flag. Also + * added program_version variable and output of + * program name and version number when program + * is executed. + * + * 30.10.94 - added support for v2 filesystem + * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) + * + * 10.12.94 - added test to prevent checking of mounted fs adapted + * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck + * program. (Daniel Quinlan, quinlan@yggdrasil.com) + * + * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such + * for modern libcs (janl@math.uio.no, Nicolai Langfeldt) + * + * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk + * (Russell King). He made them for ARM. It would seem + * that the ARM is powerful enough to do this in C whereas + * i386 and m64k must use assembly to get it fast >:-) + * This should make minix fsck system-independent. + * (janl@math.uio.no, Nicolai Langfeldt) + * + * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler + * warnings. Added mc68k bitops from + * Joerg Dorchain . + * + * 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). + * + * Usage: fsck [-larvsm] device + * -l for a listing of all the filenames + * -a for automatic repairs (not implemented) + * -r for repairs (interactive) (not implemented) + * -v for verbose (tells how many files) + * -s for super-block info + * -m for minix-like "mode not cleared" warnings + * -f force filesystem check even if filesystem marked as valid + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +static const int MINIX_ROOT_INO = 1; +static const int MINIX_LINK_MAX = 250; +static const int MINIX2_LINK_MAX = 65530; + +static const int MINIX_I_MAP_SLOTS = 8; +static const int MINIX_Z_MAP_SLOTS = 64; +static const int MINIX_SUPER_MAGIC = 0x137F; /* original minix fs */ +static const int MINIX_SUPER_MAGIC2 = 0x138F; /* minix fs, 30 char names */ +static const int MINIX2_SUPER_MAGIC = 0x2468; /* minix V2 fs */ +static const int MINIX2_SUPER_MAGIC2 = 0x2478; /* minix V2 fs, 30 char names */ +static const int MINIX_VALID_FS = 0x0001; /* Clean fs. */ +static const int MINIX_ERROR_FS = 0x0002; /* fs has errors. */ + +#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) +#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode))) + +static const int MINIX_V1 = 0x0001; /* original minix fs */ +static const int MINIX_V2 = 0x0002; /* minix V2 fs */ + +#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version + +/* + * This is the original minix inode layout on disk. + * Note the 8-bit gid and atime and ctime. + */ +struct minix_inode { + uint16_t i_mode; + uint16_t i_uid; + uint32_t i_size; + uint32_t i_time; + uint8_t i_gid; + uint8_t i_nlinks; + uint16_t i_zone[9]; +}; + +/* + * The new minix inode has all the time entries, as well as + * long block numbers and a third indirect block (7+1+1+1 + * instead of 7+1+1). Also, some previously 8-bit values are + * now 16-bit. The inode is now 64 bytes instead of 32. + */ +struct minix2_inode { + uint16_t i_mode; + uint16_t i_nlinks; + uint16_t i_uid; + uint16_t i_gid; + uint32_t i_size; + uint32_t i_atime; + uint32_t i_mtime; + uint32_t i_ctime; + uint32_t i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + uint16_t s_ninodes; + uint16_t s_nzones; + uint16_t s_imap_blocks; + uint16_t s_zmap_blocks; + uint16_t s_firstdatazone; + uint16_t s_log_zone_size; + uint32_t s_max_size; + uint16_t s_magic; + uint16_t s_state; + uint32_t s_zones; +}; + +struct minix_dir_entry { + uint16_t inode; + char name[0]; +}; + +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<> 3] & (1<<(i & 7))) != 0; +} +#define inode_in_use(x) (bit(inode_map,(x))) +#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1)) + +#define mark_inode(x) (setbit(inode_map,(x)),changed=1) +#define unmark_inode(x) (clrbit(inode_map,(x)),changed=1) + +#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1) +#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1) + +static void leave(int) __attribute__ ((noreturn)); +static void leave(int status) +{ + if (termios_set) + tcsetattr(0, TCSANOW, &termios); + exit(status); +} + +static void die(const char *str) +{ + bb_error_msg("%s", str); + leave(8); +} + +/* + * This simply goes through the file-name data and prints out the + * current file. + */ +static void print_current_name(void) +{ + int i = 0; + + while (i < name_depth) + printf("/%.*s", namelen, name_list[i++]); + if (i == 0) + printf("/"); +} + +static int ask(const char *string, int def) +{ + int c; + + if (!repair) { + printf("\n"); + errors_uncorrected = 1; + return 0; + } + if (automatic) { + printf("\n"); + if (!def) + errors_uncorrected = 1; + return def; + } + printf(def ? "%s (y/n)? " : "%s (n/y)? ", string); + for (;;) { + fflush(stdout); + if ((c = getchar()) == EOF) { + if (!def) + errors_uncorrected = 1; + return def; + } + c = toupper(c); + if (c == 'Y') { + def = 1; + break; + } else if (c == 'N') { + def = 0; + break; + } else if (c == ' ' || c == '\n') + break; + } + if (def) + printf("y\n"); + else { + printf("n\n"); + errors_uncorrected = 1; + } + return def; +} + +/* + * Make certain that we aren't checking a filesystem that is on a + * mounted partition. Code adapted from e2fsck, Copyright (C) 1993, + * 1994 Theodore Ts'o. Also licensed under GPL. + */ +static void check_mount(void) +{ + FILE *f; + struct mntent *mnt; + int cont; + int fd; + + if ((f = setmntent(MOUNTED, "r")) == NULL) + return; + while ((mnt = getmntent(f)) != NULL) + if (strcmp(device_name, mnt->mnt_fsname) == 0) + break; + endmntent(f); + if (!mnt) + return; + + /* + * If the root is mounted read-only, then /etc/mtab is + * probably not correct; so we won't issue a warning based on + * it. + */ + fd = open(MOUNTED, O_RDWR); + if (fd < 0 && errno == EROFS) + return; + else + close(fd); + + printf("%s is mounted. ", device_name); + if (isatty(0) && isatty(1)) + cont = ask("Do you really want to continue", 0); + else + cont = 0; + if (!cont) { + printf("check aborted.\n"); + exit(0); + } + return; +} + +/* + * check_zone_nr checks to see that *nr is a valid zone nr. If it + * isn't, it will possibly be repaired. Check_zone_nr sets *corrected + * if an error was corrected, and returns the zone (0 for no zone + * or a bad zone-number). + */ +static int check_zone_nr(unsigned short *nr, int *corrected) +{ + if (!*nr) + return 0; + if (*nr < FIRSTZONE) + printf("Zone nr < FIRSTZONE in file `"); + else if (*nr >= ZONES) + printf("Zone nr >= ZONES in file `"); + else + return *nr; + print_current_name(); + printf("'."); + if (ask("Remove block", 1)) { + *nr = 0; + *corrected = 1; + } + return 0; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static int check_zone_nr2(unsigned int *nr, int *corrected) +{ + if (!*nr) + return 0; + if (*nr < FIRSTZONE) + printf("Zone nr < FIRSTZONE in file `"); + else if (*nr >= ZONES) + printf("Zone nr >= ZONES in file `"); + else + return *nr; + print_current_name(); + printf("'."); + if (ask("Remove block", 1)) { + *nr = 0; + *corrected = 1; + } + return 0; +} +#endif + +/* + * read-block reads block nr into the buffer at addr. + */ +static void read_block(unsigned int nr, char *addr) +{ + if (!nr) { + memset(addr, 0, BLOCK_SIZE); + return; + } + if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) { + printf("Read error: unable to seek to block in file '"); + print_current_name(); + printf("'\n"); + memset(addr, 0, BLOCK_SIZE); + errors_uncorrected = 1; + } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) { + printf("Read error: bad block in file '"); + print_current_name(); + printf("'\n"); + memset(addr, 0, BLOCK_SIZE); + errors_uncorrected = 1; + } +} + +/* + * write_block writes block nr to disk. + */ +static void write_block(unsigned int nr, char *addr) +{ + if (!nr) + return; + if (nr < FIRSTZONE || nr >= ZONES) { + printf("Internal error: trying to write bad block\n" + "Write request ignored\n"); + errors_uncorrected = 1; + return; + } + if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) + die("seek failed in write_block"); + if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) { + printf("Write error: bad block in file '"); + print_current_name(); + printf("'\n"); + errors_uncorrected = 1; + } +} + +/* + * map-block calculates the absolute block nr of a block in a file. + * It sets 'changed' if the inode has needed changing, and re-writes + * any indirect blocks with errors. + */ +static int map_block(struct minix_inode *inode, unsigned int blknr) +{ + unsigned short ind[BLOCK_SIZE >> 1]; + unsigned short dind[BLOCK_SIZE >> 1]; + int blk_chg, block, result; + + if (blknr < 7) + return check_zone_nr(inode->i_zone + blknr, &changed); + blknr -= 7; + if (blknr < 512) { + block = check_zone_nr(inode->i_zone + 7, &changed); + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr(blknr + ind, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; + } + blknr -= 512; + block = check_zone_nr(inode->i_zone + 8, &changed); + read_block(block, (char *) dind); + blk_chg = 0; + result = check_zone_nr(dind + (blknr / 512), &blk_chg); + if (blk_chg) + write_block(block, (char *) dind); + block = result; + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr(ind + (blknr % 512), &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static int map_block2(struct minix2_inode *inode, unsigned int blknr) +{ + unsigned int ind[BLOCK_SIZE >> 2]; + unsigned int dind[BLOCK_SIZE >> 2]; + unsigned int tind[BLOCK_SIZE >> 2]; + int blk_chg, block, result; + + if (blknr < 7) + return check_zone_nr2(inode->i_zone + blknr, &changed); + blknr -= 7; + if (blknr < 256) { + block = check_zone_nr2(inode->i_zone + 7, &changed); + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr2(blknr + ind, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; + } + blknr -= 256; + if (blknr >= 256 * 256) { + block = check_zone_nr2(inode->i_zone + 8, &changed); + read_block(block, (char *) dind); + blk_chg = 0; + result = check_zone_nr2(dind + blknr / 256, &blk_chg); + if (blk_chg) + write_block(block, (char *) dind); + block = result; + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr2(ind + blknr % 256, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; + } + blknr -= 256 * 256; + block = check_zone_nr2(inode->i_zone + 9, &changed); + read_block(block, (char *) tind); + blk_chg = 0; + result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg); + if (blk_chg) + write_block(block, (char *) tind); + block = result; + read_block(block, (char *) dind); + blk_chg = 0; + result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg); + if (blk_chg) + write_block(block, (char *) dind); + block = result; + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr2(ind + blknr % 256, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; +} +#endif + +static void write_super_block(void) +{ + /* + * Set the state of the filesystem based on whether or not there + * are uncorrected errors. The filesystem valid flag is + * unconditionally set if we get this far. + */ + Super.s_state |= MINIX_VALID_FS; + if (errors_uncorrected) + Super.s_state |= MINIX_ERROR_FS; + else + Super.s_state &= ~MINIX_ERROR_FS; + + if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET)) + die("seek failed in write_super_block"); + if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE)) + die("unable to write super-block"); + + return; +} + +static void write_tables(void) +{ + write_super_block(); + + if (IMAPS * BLOCK_SIZE != write(IN, inode_map, IMAPS * BLOCK_SIZE)) + die("Unable to write inode map"); + if (ZMAPS * BLOCK_SIZE != write(IN, zone_map, ZMAPS * BLOCK_SIZE)) + die("Unable to write zone map"); + if (INODE_BUFFER_SIZE != write(IN, inode_buffer, INODE_BUFFER_SIZE)) + die("Unable to write inodes"); +} + +static void get_dirsize(void) +{ + int block; + char blk[BLOCK_SIZE]; + int size; + +#ifdef CONFIG_FEATURE_MINIX2 + if (version2) + block = Inode2[ROOT_INO].i_zone[0]; + else +#endif + block = Inode[ROOT_INO].i_zone[0]; + read_block(block, blk); + for (size = 16; size < BLOCK_SIZE; size <<= 1) { + if (strcmp(blk + size + 2, "..") == 0) { + dirsize = size; + namelen = size - 2; + return; + } + } + /* use defaults */ +} + +static void read_superblock(void) +{ + if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET)) + die("seek failed"); + if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE)) + die("unable to read super block"); + if (MAGIC == MINIX_SUPER_MAGIC) { + namelen = 14; + dirsize = 16; + version2 = 0; + } else if (MAGIC == MINIX_SUPER_MAGIC2) { + namelen = 30; + dirsize = 32; + version2 = 0; +#ifdef CONFIG_FEATURE_MINIX2 + } else if (MAGIC == MINIX2_SUPER_MAGIC) { + namelen = 14; + dirsize = 16; + version2 = 1; + } else if (MAGIC == MINIX2_SUPER_MAGIC2) { + namelen = 30; + dirsize = 32; + version2 = 1; +#endif + } else + die("bad magic number in super-block"); + if (ZONESIZE != 0 || BLOCK_SIZE != 1024) + die("Only 1k blocks/zones supported"); + if (IMAPS * BLOCK_SIZE * 8 < INODES + 1) + die("bad s_imap_blocks field in super-block"); + if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1) + die("bad s_zmap_blocks field in super-block"); +} + +static void read_tables(void) +{ + inode_map = xmalloc(IMAPS * BLOCK_SIZE); + zone_map = xmalloc(ZMAPS * BLOCK_SIZE); + memset(inode_map, 0, sizeof(inode_map)); + memset(zone_map, 0, sizeof(zone_map)); + inode_buffer = xmalloc(INODE_BUFFER_SIZE); + inode_count = xmalloc(INODES + 1); + zone_count = xmalloc(ZONES); + if (IMAPS * BLOCK_SIZE != read(IN, inode_map, IMAPS * BLOCK_SIZE)) + die("Unable to read inode map"); + if (ZMAPS * BLOCK_SIZE != read(IN, zone_map, ZMAPS * BLOCK_SIZE)) + die("Unable to read zone map"); + if (INODE_BUFFER_SIZE != read(IN, inode_buffer, INODE_BUFFER_SIZE)) + die("Unable to read inodes"); + if (NORM_FIRSTZONE != FIRSTZONE) { + printf("Warning: Firstzone != Norm_firstzone\n"); + errors_uncorrected = 1; + } + get_dirsize(); + if (show) { + printf("%ld inodes\n", INODES); + printf("%ld blocks\n", ZONES); + printf("Firstdatazone=%ld (%ld)\n", FIRSTZONE, NORM_FIRSTZONE); + printf("Zonesize=%d\n", BLOCK_SIZE << ZONESIZE); + printf("Maxsize=%ld\n", MAXSIZE); + printf("Filesystem state=%d\n", Super.s_state); + printf("namelen=%d\n\n", namelen); + } +} + +static struct minix_inode *get_inode(unsigned int nr) +{ + struct minix_inode *inode; + + if (!nr || nr > INODES) + return NULL; + total++; + inode = Inode + nr; + if (!inode_count[nr]) { + if (!inode_in_use(nr)) { + printf("Inode %d marked not used, but used for file '", nr); + print_current_name(); + printf("'\n"); + if (repair) { + if (ask("Mark in use", 1)) + mark_inode(nr); + } else { + errors_uncorrected = 1; + } + } + if (S_ISDIR(inode->i_mode)) + directory++; + else if (S_ISREG(inode->i_mode)) + regular++; + else if (S_ISCHR(inode->i_mode)) + chardev++; + else if (S_ISBLK(inode->i_mode)) + blockdev++; + else if (S_ISLNK(inode->i_mode)) + symlinks++; + else if (S_ISSOCK(inode->i_mode)); + else if (S_ISFIFO(inode->i_mode)); + else { + print_current_name(); + printf(" has mode %05o\n", inode->i_mode); + } + + } else + links++; + if (!++inode_count[nr]) { + printf("Warning: inode count too big.\n"); + inode_count[nr]--; + errors_uncorrected = 1; + } + return inode; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static struct minix2_inode *get_inode2(unsigned int nr) +{ + struct minix2_inode *inode; + + if (!nr || nr > INODES) + return NULL; + total++; + inode = Inode2 + nr; + if (!inode_count[nr]) { + if (!inode_in_use(nr)) { + printf("Inode %d marked not used, but used for file '", nr); + print_current_name(); + printf("'\n"); + if (repair) { + if (ask("Mark in use", 1)) + mark_inode(nr); + else + errors_uncorrected = 1; + } + } + if (S_ISDIR(inode->i_mode)) + directory++; + else if (S_ISREG(inode->i_mode)) + regular++; + else if (S_ISCHR(inode->i_mode)) + chardev++; + else if (S_ISBLK(inode->i_mode)) + blockdev++; + else if (S_ISLNK(inode->i_mode)) + symlinks++; + else if (S_ISSOCK(inode->i_mode)); + else if (S_ISFIFO(inode->i_mode)); + else { + print_current_name(); + printf(" has mode %05o\n", inode->i_mode); + } + } else + links++; + if (!++inode_count[nr]) { + printf("Warning: inode count too big.\n"); + inode_count[nr]--; + errors_uncorrected = 1; + } + return inode; +} +#endif + +static void check_root(void) +{ + struct minix_inode *inode = Inode + ROOT_INO; + + if (!inode || !S_ISDIR(inode->i_mode)) + die("root inode isn't a directory"); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check_root2(void) +{ + struct minix2_inode *inode = Inode2 + ROOT_INO; + + if (!inode || !S_ISDIR(inode->i_mode)) + die("root inode isn't a directory"); +} +#endif + +static int add_zone(unsigned short *znr, int *corrected) +{ + int result; + int block; + + result = 0; + block = check_zone_nr(znr, corrected); + if (!block) + return 0; + if (zone_count[block]) { + printf("Block has been used before. Now in file `"); + print_current_name(); + printf("'."); + if (ask("Clear", 1)) { + *znr = 0; + block = 0; + *corrected = 1; + } + } + if (!block) + return 0; + if (!zone_in_use(block)) { + printf("Block %d in file `", block); + print_current_name(); + printf("' is marked not in use."); + if (ask("Correct", 1)) + mark_zone(block); + } + if (!++zone_count[block]) + zone_count[block]--; + return block; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static int add_zone2(unsigned int *znr, int *corrected) +{ + int result; + int block; + + result = 0; + block = check_zone_nr2(znr, corrected); + if (!block) + return 0; + if (zone_count[block]) { + printf("Block has been used before. Now in file `"); + print_current_name(); + printf("'."); + if (ask("Clear", 1)) { + *znr = 0; + block = 0; + *corrected = 1; + } + } + if (!block) + return 0; + if (!zone_in_use(block)) { + printf("Block %d in file `", block); + print_current_name(); + printf("' is marked not in use."); + if (ask("Correct", 1)) + mark_zone(block); + } + if (!++zone_count[block]) + zone_count[block]--; + return block; +} +#endif + +static void add_zone_ind(unsigned short *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, chg_blk = 0; + int block; + + block = add_zone(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < (BLOCK_SIZE >> 1); i++) + add_zone(i + (unsigned short *) blk, &chg_blk); + if (chg_blk) + write_block(block, blk); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void add_zone_ind2(unsigned int *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, chg_blk = 0; + int block; + + block = add_zone2(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < BLOCK_SIZE >> 2; i++) + add_zone2(i + (unsigned int *) blk, &chg_blk); + if (chg_blk) + write_block(block, blk); +} +#endif + +static void add_zone_dind(unsigned short *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, blk_chg = 0; + int block; + + block = add_zone(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < (BLOCK_SIZE >> 1); i++) + add_zone_ind(i + (unsigned short *) blk, &blk_chg); + if (blk_chg) + write_block(block, blk); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void add_zone_dind2(unsigned int *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, blk_chg = 0; + int block; + + block = add_zone2(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < BLOCK_SIZE >> 2; i++) + add_zone_ind2(i + (unsigned int *) blk, &blk_chg); + if (blk_chg) + write_block(block, blk); +} + +static void add_zone_tind2(unsigned int *znr, int *corrected) +{ + static char blk[BLOCK_SIZE]; + int i, blk_chg = 0; + int block; + + block = add_zone2(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i = 0; i < BLOCK_SIZE >> 2; i++) + add_zone_dind2(i + (unsigned int *) blk, &blk_chg); + if (blk_chg) + write_block(block, blk); +} +#endif + +static void check_zones(unsigned int i) +{ + struct minix_inode *inode; + + if (!i || i > INODES) + return; + if (inode_count[i] > 1) /* have we counted this file already? */ + return; + inode = Inode + i; + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && + !S_ISLNK(inode->i_mode)) return; + for (i = 0; i < 7; i++) + add_zone(i + inode->i_zone, &changed); + add_zone_ind(7 + inode->i_zone, &changed); + add_zone_dind(8 + inode->i_zone, &changed); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check_zones2(unsigned int i) +{ + struct minix2_inode *inode; + + if (!i || i > INODES) + return; + if (inode_count[i] > 1) /* have we counted this file already? */ + return; + inode = Inode2 + i; + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) + && !S_ISLNK(inode->i_mode)) + return; + for (i = 0; i < 7; i++) + add_zone2(i + inode->i_zone, &changed); + add_zone_ind2(7 + inode->i_zone, &changed); + add_zone_dind2(8 + inode->i_zone, &changed); + add_zone_tind2(9 + inode->i_zone, &changed); +} +#endif + +static void check_file(struct minix_inode *dir, unsigned int offset) +{ + static char blk[BLOCK_SIZE]; + struct minix_inode *inode; + int ino; + char *name; + int block; + + block = map_block(dir, offset / BLOCK_SIZE); + read_block(block, blk); + name = blk + (offset % BLOCK_SIZE) + 2; + ino = *(unsigned short *) (name - 2); + if (ino > INODES) { + print_current_name(); + printf(" contains a bad inode number for file '"); + printf("%.*s'.", namelen, name); + if (ask(" Remove", 1)) { + *(unsigned short *) (name - 2) = 0; + write_block(block, blk); + } + ino = 0; + } + if (name_depth < MAX_DEPTH) + strncpy(name_list[name_depth], name, namelen); + name_depth++; + inode = get_inode(ino); + name_depth--; + if (!offset) { + if (!inode || strcmp(".", name)) { + print_current_name(); + printf(": bad directory: '.' isn't first\n"); + errors_uncorrected = 1; + } else + return; + } + if (offset == dirsize) { + if (!inode || strcmp("..", name)) { + print_current_name(); + printf(": bad directory: '..' isn't second\n"); + errors_uncorrected = 1; + } else + return; + } + if (!inode) + return; + if (name_depth < MAX_DEPTH) + strncpy(name_list[name_depth], name, namelen); + name_depth++; + if (list) { + if (verbose) + printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks); + print_current_name(); + if (S_ISDIR(inode->i_mode)) + printf(":\n"); + else + printf("\n"); + } + check_zones(ino); + if (inode && S_ISDIR(inode->i_mode)) + recursive_check(ino); + name_depth--; + return; +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check_file2(struct minix2_inode *dir, unsigned int offset) +{ + static char blk[BLOCK_SIZE]; + struct minix2_inode *inode; + int ino; + char *name; + int block; + + block = map_block2(dir, offset / BLOCK_SIZE); + read_block(block, blk); + name = blk + (offset % BLOCK_SIZE) + 2; + ino = *(unsigned short *) (name - 2); + if (ino > INODES) { + print_current_name(); + printf(" contains a bad inode number for file '"); + printf("%.*s'.", namelen, name); + if (ask(" Remove", 1)) { + *(unsigned short *) (name - 2) = 0; + write_block(block, blk); + } + ino = 0; + } + if (name_depth < MAX_DEPTH) + strncpy(name_list[name_depth], name, namelen); + name_depth++; + inode = get_inode2(ino); + name_depth--; + if (!offset) { + if (!inode || strcmp(".", name)) { + print_current_name(); + printf(": bad directory: '.' isn't first\n"); + errors_uncorrected = 1; + } else + return; + } + if (offset == dirsize) { + if (!inode || strcmp("..", name)) { + print_current_name(); + printf(": bad directory: '..' isn't second\n"); + errors_uncorrected = 1; + } else + return; + } + if (!inode) + return; + name_depth++; + if (list) { + if (verbose) + printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks); + print_current_name(); + if (S_ISDIR(inode->i_mode)) + printf(":\n"); + else + printf("\n"); + } + check_zones2(ino); + if (inode && S_ISDIR(inode->i_mode)) + recursive_check2(ino); + name_depth--; + return; +} +#endif + +static void recursive_check(unsigned int ino) +{ + struct minix_inode *dir; + unsigned int offset; + + dir = Inode + ino; + if (!S_ISDIR(dir->i_mode)) + die("internal error"); + if (dir->i_size < 2 * dirsize) { + print_current_name(); + printf(": bad directory: size<32"); + errors_uncorrected = 1; + } + for (offset = 0; offset < dir->i_size; offset += dirsize) + check_file(dir, offset); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void recursive_check2(unsigned int ino) +{ + struct minix2_inode *dir; + unsigned int offset; + + dir = Inode2 + ino; + if (!S_ISDIR(dir->i_mode)) + die("internal error"); + if (dir->i_size < 2 * dirsize) { + print_current_name(); + printf(": bad directory: size < 32"); + errors_uncorrected = 1; + } + for (offset = 0; offset < dir->i_size; offset += dirsize) + check_file2(dir, offset); +} +#endif + +static int bad_zone(int i) +{ + char buffer[1024]; + + if (BLOCK_SIZE * i != lseek(IN, BLOCK_SIZE * i, SEEK_SET)) + die("seek failed in bad_zone"); + return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE)); +} + +static void check_counts(void) +{ + int i; + + for (i = 1; i <= INODES; i++) { + if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) { + printf("Inode %d mode not cleared.", i); + if (ask("Clear", 1)) { + Inode[i].i_mode = 0; + changed = 1; + } + } + if (!inode_count[i]) { + if (!inode_in_use(i)) + continue; + printf("Inode %d not used, marked used in the bitmap.", i); + if (ask("Clear", 1)) + unmark_inode(i); + continue; + } + if (!inode_in_use(i)) { + printf("Inode %d used, marked unused in the bitmap.", i); + if (ask("Set", 1)) + mark_inode(i); + } + if (Inode[i].i_nlinks != inode_count[i]) { + printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.", + i, Inode[i].i_mode, Inode[i].i_nlinks, inode_count[i]); + if (ask("Set i_nlinks to count", 1)) { + Inode[i].i_nlinks = inode_count[i]; + changed = 1; + } + } + } + for (i = FIRSTZONE; i < ZONES; i++) { + if (zone_in_use(i) == zone_count[i]) + continue; + if (!zone_count[i]) { + if (bad_zone(i)) + continue; + printf("Zone %d: marked in use, no file uses it.", i); + if (ask("Unmark", 1)) + unmark_zone(i); + continue; + } + printf("Zone %d: %sin use, counted=%d\n", + i, zone_in_use(i) ? "" : "not ", zone_count[i]); + } +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check_counts2(void) +{ + int i; + + for (i = 1; i <= INODES; i++) { + if (!inode_in_use(i) && Inode2[i].i_mode && warn_mode) { + printf("Inode %d mode not cleared.", i); + if (ask("Clear", 1)) { + Inode2[i].i_mode = 0; + changed = 1; + } + } + if (!inode_count[i]) { + if (!inode_in_use(i)) + continue; + printf("Inode %d not used, marked used in the bitmap.", i); + if (ask("Clear", 1)) + unmark_inode(i); + continue; + } + if (!inode_in_use(i)) { + printf("Inode %d used, marked unused in the bitmap.", i); + if (ask("Set", 1)) + mark_inode(i); + } + if (Inode2[i].i_nlinks != inode_count[i]) { + printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.", + i, Inode2[i].i_mode, Inode2[i].i_nlinks, + inode_count[i]); + if (ask("Set i_nlinks to count", 1)) { + Inode2[i].i_nlinks = inode_count[i]; + changed = 1; + } + } + } + for (i = FIRSTZONE; i < ZONES; i++) { + if (zone_in_use(i) == zone_count[i]) + continue; + if (!zone_count[i]) { + if (bad_zone(i)) + continue; + printf("Zone %d: marked in use, no file uses it.", i); + if (ask("Unmark", 1)) + unmark_zone(i); + continue; + } + printf("Zone %d: %sin use, counted=%d\n", + i, zone_in_use(i) ? "" : "not ", zone_count[i]); + } +} +#endif + +static void check(void) +{ + memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count)); + memset(zone_count, 0, ZONES * sizeof(*zone_count)); + check_zones(ROOT_INO); + recursive_check(ROOT_INO); + check_counts(); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static void check2(void) +{ + memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count)); + memset(zone_count, 0, ZONES * sizeof(*zone_count)); + check_zones2(ROOT_INO); + recursive_check2(ROOT_INO); + check_counts2(); +} +#endif + +/* Wed Feb 9 15:17:06 MST 2000 */ +/* dynamically allocate name_list (instead of making it static) */ +static void alloc_name_list(void) +{ + int i; + + name_list = xmalloc(sizeof(char *) * MAX_DEPTH); + for (i = 0; i < MAX_DEPTH; i++) + name_list[i] = xmalloc(sizeof(char) * BUFSIZ + 1); +} + +#ifdef CONFIG_FEATURE_CLEAN_UP +/* execute this atexit() to deallocate name_list[] */ +/* piptigger was here */ +static void free_name_list(void) +{ + int i; + + if (name_list) { + for (i = 0; i < MAX_DEPTH; i++) { + free(name_list[i]); + } + free(name_list); + } +} +#endif + +extern int fsck_minix_main(int argc, char **argv) +{ + struct termios tmp; + int count; + int retcode = 0; + + alloc_name_list(); +#ifdef CONFIG_FEATURE_CLEAN_UP + /* Don't bother to free memory. Exit does + * that automagically, so we can save a few bytes */ + atexit(free_name_list); +#endif + + if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE) + die("bad inode size"); +#ifdef CONFIG_FEATURE_MINIX2 + if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) + die("bad v2 inode size"); +#endif + while (argc-- > 1) { + argv++; + if (argv[0][0] != '-') { + if (device_name) + bb_show_usage(); + else + device_name = argv[0]; + } else + while (*++argv[0]) + switch (argv[0][0]) { + case 'l': + list = 1; + break; + case 'a': + automatic = 1; + repair = 1; + break; + case 'r': + automatic = 0; + repair = 1; + break; + case 'v': + verbose = 1; + break; + case 's': + show = 1; + break; + case 'm': + warn_mode = 1; + break; + case 'f': + force = 1; + break; + default: + bb_show_usage(); + } + } + if (!device_name) + bb_show_usage(); + check_mount(); /* trying to check a mounted filesystem? */ + if (repair && !automatic) { + if (!isatty(0) || !isatty(1)) + die("need terminal for interactive repairs"); + } + IN = open(device_name, repair ? O_RDWR : O_RDONLY); + if (IN < 0){ + fprintf(stderr,"unable to open device '%s'.\n",device_name); + leave(8); + } + for (count = 0; count < 3; count++) + sync(); + read_superblock(); + + /* + * Determine whether or not we should continue with the checking. + * This is based on the status of the filesystem valid and error + * flags and whether or not the -f switch was specified on the + * command line. + */ + printf("%s, %s\n", bb_applet_name, program_version); + if (!(Super.s_state & MINIX_ERROR_FS) && + (Super.s_state & MINIX_VALID_FS) && !force) { + if (repair) + printf("%s is clean, no check.\n", device_name); + return retcode; + } else if (force) + printf("Forcing filesystem check on %s.\n", device_name); + else if (repair) + printf("Filesystem on %s is dirty, needs checking.\n", + device_name); + + read_tables(); + + if (repair && !automatic) { + tcgetattr(0, &termios); + tmp = termios; + tmp.c_lflag &= ~(ICANON | ECHO); + tcsetattr(0, TCSANOW, &tmp); + termios_set = 1; + } +#ifdef CONFIG_FEATURE_MINIX2 + if (version2) { + check_root2(); + check2(); + } else +#endif + { + check_root(); + check(); + } + if (verbose) { + int i, free_cnt; + + for (i = 1, free_cnt = 0; i <= INODES; i++) + if (!inode_in_use(i)) + free_cnt++; + printf("\n%6ld inodes used (%ld%%)\n", (INODES - free_cnt), + 100 * (INODES - free_cnt) / INODES); + for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++) + if (!zone_in_use(i)) + free_cnt++; + printf("%6ld zones used (%ld%%)\n", (ZONES - free_cnt), + 100 * (ZONES - free_cnt) / ZONES); + printf("\n%6d regular files\n" + "%6d directories\n" + "%6d character device files\n" + "%6d block device files\n" + "%6d links\n" + "%6d symbolic links\n" + "------\n" + "%6d files\n", + regular, directory, chardev, blockdev, + links - 2 * directory + 1, symlinks, + total - 2 * directory + 1); + } + if (changed) { + write_tables(); + printf("----------------------------\n" + "FILE SYSTEM HAS BEEN CHANGED\n" + "----------------------------\n"); + for (count = 0; count < 3; count++) + sync(); + } else if (repair) + write_super_block(); + + if (repair && !automatic) + tcsetattr(0, TCSANOW, &termios); + + if (changed) + retcode += 3; + if (errors_uncorrected) + retcode += 4; + return retcode; +} diff --git a/busybox/util-linux/getopt.c b/busybox/util-linux/getopt.c new file mode 100644 index 000000000..032d0dc6b --- /dev/null +++ b/busybox/util-linux/getopt.c @@ -0,0 +1,391 @@ +/* + * 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 parse_error(), using bb_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; + + free(BUFFER); + + if (!quote) { /* Just copy arg */ + BUFFER=bb_xstrdup(arg); + return BUFFER; + } + + /* Each character in arg may take up to four characters in the result: + For a quote we need a closing quote, a backslash, a quote and an + opening quote! We need also the global opening and closing quote, + and one extra character for '\0'. */ + BUFFER=xmalloc(strlen(arg)*4+3); + + bufptr=BUFFER; + *bufptr++='\''; + + while (*argptr) { + if (*argptr == '\'') { + /* Quote: replace it with: '\'' */ + *bufptr++='\''; + *bufptr++='\\'; + *bufptr++='\''; + *bufptr++='\''; + } else if (shell==TCSH && *argptr=='!') { + /* Exclamation mark: replace it with: \! */ + *bufptr++='\''; + *bufptr++='\\'; + *bufptr++='!'; + *bufptr++='\''; + } else if (shell==TCSH && *argptr=='\n') { + /* Newline: replace it with: \n */ + *bufptr++='\\'; + *bufptr++='n'; + } else if (shell==TCSH && isspace(*argptr)) { + /* Non-newline whitespace: replace it with \ */ + *bufptr++='\''; + *bufptr++='\\'; + *bufptr++=*argptr; + *bufptr++='\''; + } else + /* Just copy */ + *bufptr++=*argptr; + argptr++; + } + *bufptr++='\''; + *bufptr++='\0'; + return BUFFER; +} + +/* + * Generate the output. argv[0] is the program name (used for reporting errors). + * argv[1..] contains the options to be parsed. argc must be the number of + * elements in argv (ie. 1 if there are no options, only the program name), + * optstr must contain the short options, and longopts the long options. + * Other settings are found in global variables. + */ +int generate_output(char * argv[],int argc,const char *optstr, + const struct option *longopts) +{ + int exit_code = 0; /* We assume everything will be OK */ + int opt; + int longindex; + const char *charptr; + + if (quiet_errors) /* No error reporting from getopt(3) */ + opterr=0; + optind=0; /* Reset getopt(3) */ + + while ((opt = (alternative? + getopt_long_only(argc,argv,optstr,longopts,&longindex): + getopt_long(argc,argv,optstr,longopts,&longindex))) + != EOF) + if (opt == '?' || opt == ':' ) + exit_code = 1; + else if (!quiet_output) { + if (opt == LONG_OPT) { + printf(" --%s",longopts[longindex].name); + if (longopts[longindex].has_arg) + printf(" %s", + normalize(optarg?optarg:"")); + } else if (opt == NON_OPT) + printf(" %s",normalize(optarg)); + else { + printf(" -%c",opt); + charptr = strchr(optstr,opt); + if (charptr != NULL && *++charptr == ':') + printf(" %s", + normalize(optarg?optarg:"")); + } + } + + if (! quiet_output) { + printf(" --"); + while (optind < argc) + printf(" %s",normalize(argv[optind++])); + printf("\n"); + } + return exit_code; +} + +static struct option *long_options=NULL; +static int long_options_length=0; /* Length of array */ +static int long_options_nr=0; /* Nr of used elements in array */ +static const int LONG_OPTIONS_INCR = 10; +#define init_longopt() add_longopt(NULL,0) + +/* Register a long option. The contents of name is copied. */ +void add_longopt(const char *name,int has_arg) +{ + if (!name) { /* init */ + free(long_options); + long_options=NULL; + long_options_length=0; + long_options_nr=0; + } + + if (long_options_nr == long_options_length) { + long_options_length += LONG_OPTIONS_INCR; + long_options=xrealloc(long_options, + sizeof(struct option) * + long_options_length); + } + + long_options[long_options_nr].name=NULL; + long_options[long_options_nr].has_arg=0; + long_options[long_options_nr].flag=NULL; + long_options[long_options_nr].val=0; + + if (long_options_nr) { /* Not for init! */ + long_options[long_options_nr-1].has_arg=has_arg; + long_options[long_options_nr-1].flag=NULL; + long_options[long_options_nr-1].val=LONG_OPT; + long_options[long_options_nr-1].name=bb_xstrdup(name); + } + long_options_nr++; +} + + +/* + * Register several long options. options is a string of long options, + * separated by commas or whitespace. + * This nukes options! + */ +void add_long_options(char *options) +{ + int arg_opt, tlen; + char *tokptr=strtok(options,", \t\n"); + while (tokptr) { + arg_opt=no_argument; + tlen=strlen(tokptr); + if (tlen > 0) { + if (tokptr[tlen-1] == ':') { + if (tlen > 1 && tokptr[tlen-2] == ':') { + tokptr[tlen-2]='\0'; + tlen -= 2; + arg_opt=optional_argument; + } else { + tokptr[tlen-1]='\0'; + tlen -= 1; + arg_opt=required_argument; + } + if (tlen == 0) + bb_error_msg("empty long option after -l or --long argument"); + } + add_longopt(tokptr,arg_opt); + } + tokptr=strtok(NULL,", \t\n"); + } +} + +void set_shell(const char *new_shell) +{ + if (!strcmp(new_shell,"bash")) + shell=BASH; + else if (!strcmp(new_shell,"tcsh")) + shell=TCSH; + else if (!strcmp(new_shell,"sh")) + shell=BASH; + else if (!strcmp(new_shell,"csh")) + shell=TCSH; + else + bb_error_msg("unknown shell after -s or --shell argument"); +} + + +/* Exit codes: + * 0) No errors, successful operation. + * 1) getopt(3) returned an error. + * 2) A problem with parameter parsing for getopt(1). + * 3) Internal error, out of memory + * 4) Returned for -T + */ + +static struct option longopts[]= +{ + {"options",required_argument,NULL,'o'}, + {"longoptions",required_argument,NULL,'l'}, + {"quiet",no_argument,NULL,'q'}, + {"quiet-output",no_argument,NULL,'Q'}, + {"shell",required_argument,NULL,'s'}, + {"test",no_argument,NULL,'T'}, + {"unquoted",no_argument,NULL,'u'}, + {"alternative",no_argument,NULL,'a'}, + {"name",required_argument,NULL,'n'}, + {NULL,0,NULL,0} +}; + +/* Stop scanning as soon as a non-option argument is found! */ +static const char *shortopts="+ao:l:n:qQs:Tu"; + + +int getopt_main(int argc, char *argv[]) +{ + const char *optstr = NULL; + const char *name = NULL; + int opt; + int compatible=0; + + init_longopt(); + + if (getenv("GETOPT_COMPATIBLE")) + compatible=1; + + if (argc == 1) { + if (compatible) { + /* For some reason, the original getopt gave no error + when there were no arguments. */ + printf(" --\n"); + return 0; + } else + bb_error_msg_and_die("missing optstring argument"); + } + + if (argv[1][0] != '-' || compatible) { + quote=0; + optstr=xmalloc(strlen(argv[1])+1); + strcpy(optstr,argv[1]+strspn(argv[1],"-+")); + argv[1]=argv[0]; + return (generate_output(argv+1,argc-1,optstr,long_options)); + } + + while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF) + switch (opt) { + case 'a': + alternative=1; + break; + case 'o': + free(optstr); + optstr = optarg; + break; + case 'l': + add_long_options(optarg); + break; + case 'n': + free(name); + name = optarg; + break; + case 'q': + quiet_errors=1; + break; + case 'Q': + quiet_output=1; + break; + case 's': + set_shell(optarg); + break; + case 'T': + return 4; + case 'u': + quote=0; + break; + default: + bb_show_usage(); + } + + if (!optstr) { + if (optind >= argc) + bb_error_msg_and_die("missing optstring argument"); + else { + optstr=bb_xstrdup(argv[optind]); + optind++; + } + } + if (name) + argv[optind-1]=name; + else + argv[optind-1]=argv[0]; + return (generate_output(argv+optind-1,argc-optind+1,optstr,long_options)); +} + +/* + Local Variables: + c-file-style: "linux" + c-basic-offset: 4 + tab-width: 4 + End: +*/ diff --git a/busybox/util-linux/hexdump.c b/busybox/util-linux/hexdump.c new file mode 100644 index 000000000..1858b08d4 --- /dev/null +++ b/busybox/util-linux/hexdump.c @@ -0,0 +1,142 @@ +/* + * hexdump implementation for busybox + * Based on code from util-linux v 2.11l + * + * Copyright (c) 1989 + * The Regents of the University of California. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Original copyright notice is retained at the end of this file. + */ + +#include +#include +#include +#include "busybox.h" +#include "dump.h" + +static void bb_dump_addfile(char *name) +{ + register char *p; + FILE *fp; + char *buf; + + fp = bb_xfopen(name, "r"); + + while ((buf = bb_get_chomped_line_from_file(fp)) != NULL) { + p = (char *) bb_skip_whitespace(buf); + + if (*p && (*p != '#')) { + bb_dump_add(p); + } + free(buf); + } + fclose(fp); +} + +static const char * const add_strings[] = { + "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"", /* b */ + "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"", /* c */ + "\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"", /* d */ + "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"", /* o */ + "\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"", /* x */ +}; + +static const char add_first[] = "\"%07.7_Ax\n\""; + +static const char hexdump_opts[] = "bcdoxe:f:n:s:v"; + +static const struct suffix_mult suffixes[] = { + {"b", 512 }, + {"k", 1024 }, + {"m", 1024*1024 }, + {NULL, 0 } +}; + +int hexdump_main(int argc, char **argv) +{ +// register FS *tfs; + const char *p; + int ch; + + bb_dump_vflag = FIRST; + bb_dump_length = -1; + + while ((ch = getopt(argc, argv, hexdump_opts)) > 0) { + if ((p = strchr(hexdump_opts, ch)) != NULL) { + if ((p - hexdump_opts) < 5) { + bb_dump_add(add_first); + bb_dump_add(add_strings[(int)(p - hexdump_opts)]); + } else { + /* Sae a little bit of space below by omitting the 'else's. */ + if (ch == 'e') { + bb_dump_add(optarg); + } /* else */ + if (ch == 'f') { + bb_dump_addfile(optarg); + } /* else */ + if (ch == 'n') { + bb_dump_length = bb_xgetularg10_bnd(optarg, 0, INT_MAX); + } /* else */ + if (ch == 's') { + bb_dump_skip = bb_xgetularg_bnd_sfx(optarg, 10, 0, LONG_MAX, suffixes); + } /* else */ + if (ch == 'v') { + bb_dump_vflag = ALL; + } + } + } else { + bb_show_usage(); + } + } + + if (!bb_dump_fshead) { + bb_dump_add(add_first); + bb_dump_add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\""); + } + + argv += optind; + + return(bb_dump_dump(argv)); +} +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/busybox/util-linux/hwclock.c b/busybox/util-linux/hwclock.c new file mode 100644 index 000000000..a260d7448 --- /dev/null +++ b/busybox/util-linux/hwclock.c @@ -0,0 +1,230 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini hwclock implementation for busybox + * + * Copyright (C) 2002 Robert Griebl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + +/* Copied from linux/rtc.h to eliminate the kernel dependency */ +struct linux_rtc_time { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */ +#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */ + +#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif +#endif + +static time_t read_rtc(int utc) +{ + int rtc; + struct tm tm; + char *oldtz = 0; + time_t t = 0; + + if (( rtc = open ( "/dev/rtc", O_RDONLY )) < 0 ) { + if (( rtc = open ( "/dev/misc/rtc", O_RDONLY )) < 0 ) + bb_perror_msg_and_die ( "Could not access RTC" ); + } + memset ( &tm, 0, sizeof( struct tm )); + if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 ) + bb_perror_msg_and_die ( "Could not read time from RTC" ); + tm. tm_isdst = -1; // not known + + close ( rtc ); + + if ( utc ) { + oldtz = getenv ( "TZ" ); + setenv ( "TZ", "UTC 0", 1 ); + tzset ( ); + } + + t = mktime ( &tm ); + + if ( utc ) { + if ( oldtz ) + setenv ( "TZ", oldtz, 1 ); + else + unsetenv ( "TZ" ); + tzset ( ); + } + return t; +} + +static void write_rtc(time_t t, int utc) +{ + int rtc; + struct tm tm; + + if (( rtc = open ( "/dev/rtc", O_WRONLY )) < 0 ) { + if (( rtc = open ( "/dev/misc/rtc", O_WRONLY )) < 0 ) + bb_perror_msg_and_die ( "Could not access RTC" ); + } + + tm = *( utc ? gmtime ( &t ) : localtime ( &t )); + tm. tm_isdst = 0; + + if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 ) + bb_perror_msg_and_die ( "Could not set the RTC time" ); + + close ( rtc ); +} + +static int show_clock(int utc) +{ + struct tm *ptm; + time_t t; + char buffer [64]; + + t = read_rtc ( utc ); + ptm = localtime ( &t ); /* Sets 'tzname[]' */ + + safe_strncpy ( buffer, ctime ( &t ), sizeof( buffer )); + if ( buffer [0] ) + buffer [bb_strlen ( buffer ) - 1] = 0; + + //printf ( "%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : ( ptm-> tm_isdst ? tzname [1] : tzname [0] )); + printf ( "%s %.6f seconds\n", buffer, 0.0 ); + + return 0; +} + +static int to_sys_clock(int utc) +{ + struct timeval tv = { 0, 0 }; + const struct timezone tz = { timezone/60 - 60*daylight, 0 }; + + tv. tv_sec = read_rtc ( utc ); + + if ( settimeofday ( &tv, &tz )) + bb_perror_msg_and_die ( "settimeofday() failed" ); + + return 0; +} + +static int from_sys_clock(int utc) +{ + struct timeval tv = { 0, 0 }; + struct timezone tz = { 0, 0 }; + + if ( gettimeofday ( &tv, &tz )) + bb_perror_msg_and_die ( "gettimeofday() failed" ); + + write_rtc ( tv. tv_sec, utc ); + return 0; +} + + +static int check_utc(void) +{ + int utc = 0; + FILE *f = fopen ( "/var/lib/hwclock/adjtime", "r" ); + + if ( f ) { + char buffer [128]; + + while ( fgets ( buffer, sizeof( buffer ), f )) { + int len = bb_strlen ( buffer ); + + while ( len && isspace ( buffer [len - 1] )) + len--; + + buffer [len] = 0; + + if ( strncmp ( buffer, "UTC", 3 ) == 0 ) { + utc = 1; + break; + } + } + fclose ( f ); + } + return utc; +} + +#define HWCLOCK_OPT_LOCALTIME 1 +#define HWCLOCK_OPT_UTC 2 +#define HWCLOCK_OPT_SHOW 4 +#define HWCLOCK_OPT_HCTOSYS 8 +#define HWCLOCK_OPT_SYSTOHC 16 + +extern int hwclock_main ( int argc, char **argv ) +{ + unsigned long opt; + int utc; + +#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS +static const struct option hwclock_long_options[] = { + { "localtime", 0, 0, 'l' }, + { "utc", 0, 0, 'u' }, + { "show", 0, 0, 'r' }, + { "hctosys", 0, 0, 's' }, + { "systohc", 0, 0, 'w' }, + { 0, 0, 0, 0 } + }; + bb_applet_long_options = hwclock_long_options; +#endif + + bb_opt_complementaly = "r~ws:w~rs:s~wr:l~u:u~l"; + opt = bb_getopt_ulflags(argc, argv, "lursw"); + /* Check only one mode was given */ + if(opt & 0x80000000UL) { + bb_show_usage(); + } + + /* If -u or -l wasn't given check if we are using utc */ + if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) + utc = opt & HWCLOCK_OPT_UTC; + else + utc = check_utc(); + + if (opt & HWCLOCK_OPT_HCTOSYS) { + return to_sys_clock ( utc ); + } + else if (opt & HWCLOCK_OPT_SYSTOHC) { + return from_sys_clock ( utc ); + } else { + /* default HWCLOCK_OPT_SHOW */ + return show_clock ( utc ); + } +} diff --git a/busybox/util-linux/losetup.c b/busybox/util-linux/losetup.c new file mode 100644 index 000000000..c94456522 --- /dev/null +++ b/busybox/util-linux/losetup.c @@ -0,0 +1,59 @@ +/* + * Mini losetup implementation for busybox + * + * Copyright (C) 2002 Matt Kraai. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include "busybox.h" + +int +losetup_main (int argc, char **argv) +{ + int delete = 0; + int offset = 0; + int opt; + + while ((opt = getopt (argc, argv, "do:")) != -1) + switch (opt) + { + case 'd': + delete = 1; + break; + + case 'o': + offset = bb_xparse_number (optarg, NULL); + break; + + default: + bb_show_usage(); + } + + if ((delete && (offset || optind + 1 != argc)) + || (!delete && optind + 2 != argc)) + bb_show_usage(); + + opt = 0; + if (delete) + return del_loop (argv[optind]) ? EXIT_SUCCESS : EXIT_FAILURE; + else + return set_loop (argv[optind], argv[optind + 1], offset, &opt) + ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/busybox/util-linux/mkfs_minix.c b/busybox/util-linux/mkfs_minix.c new file mode 100644 index 000000000..264569a94 --- /dev/null +++ b/busybox/util-linux/mkfs_minix.c @@ -0,0 +1,852 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkfs.c - make a linux (minix) file-system. + * + * (C) 1991 Linus Torvalds. This file may be redistributed as per + * the Linux copyright. + */ + +/* + * DD.MM.YY + * + * 24.11.91 - Time began. Used the fsck sources to get started. + * + * 25.11.91 - Corrected some bugs. Added support for ".badblocks" + * The algorithm for ".badblocks" is a bit weird, but + * it should work. Oh, well. + * + * 25.01.92 - Added the -l option for getting the list of bad blocks + * out of a named file. (Dave Rivers, rivers@ponds.uucp) + * + * 28.02.92 - Added %-information when using -c. + * + * 28.02.93 - Added support for other namelengths than the original + * 14 characters so that I can test the new kernel routines.. + * + * 09.10.93 - Make exit status conform to that required by fsutil + * (Rik Faith, faith@cs.unc.edu) + * + * 31.10.93 - Added inode request feature, for backup floppies: use + * 32 inodes, for a news partition use more. + * (Scott Heavner, sdh@po.cwru.edu) + * + * 03.01.94 - Added support for file system valid flag. + * (Dr. Wettstein, greg%wind.uucp@plains.nodak.edu) + * + * 30.10.94 - added support for v2 filesystem + * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) + * + * 09.11.94 - Added test to prevent overwrite of mounted fs adapted + * from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs + * program. (Daniel Quinlan, quinlan@yggdrasil.com) + * + * 03.20.95 - Clear first 512 bytes of filesystem to make certain that + * the filesystem is not misidentified as a MS-DOS FAT filesystem. + * (Daniel Quinlan, quinlan@yggdrasil.com) + * + * 02.07.96 - Added small patch from Russell King to make the program a + * good deal more portable (janl@math.uio.no) + * + * Usage: mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks] + * + * -c for readability checking (SLOW!) + * -l for getting a list of bad blocks from a file. + * -n for namelength (currently the kernel only uses 14 or 30) + * -i for number of inodes + * -v for v2 filesystem + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + * + * Modified for BusyBox by Erik Andersen -- + * removed getopt based parser and added a hand rolled one. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define MINIX_ROOT_INO 1 +#define MINIX_LINK_MAX 250 +#define MINIX2_LINK_MAX 65530 + +#define MINIX_I_MAP_SLOTS 8 +#define MINIX_Z_MAP_SLOTS 64 +#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ +#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */ +#define MINIX_VALID_FS 0x0001 /* Clean fs. */ +#define MINIX_ERROR_FS 0x0002 /* fs has errors. */ + +#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) +#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode))) + +#define MINIX_V1 0x0001 /* original minix fs */ +#define MINIX_V2 0x0002 /* minix V2 fs */ + +#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version + +/* + * This is the original minix inode layout on disk. + * Note the 8-bit gid and atime and ctime. + */ +struct minix_inode { + uint16_t i_mode; + uint16_t i_uid; + uint32_t i_size; + uint32_t i_time; + uint8_t i_gid; + uint8_t i_nlinks; + uint16_t i_zone[9]; +}; + +/* + * The new minix inode has all the time entries, as well as + * long block numbers and a third indirect block (7+1+1+1 + * instead of 7+1+1). Also, some previously 8-bit values are + * now 16-bit. The inode is now 64 bytes instead of 32. + */ +struct minix2_inode { + uint16_t i_mode; + uint16_t i_nlinks; + uint16_t i_uid; + uint16_t i_gid; + uint32_t i_size; + uint32_t i_atime; + uint32_t i_mtime; + uint32_t i_ctime; + uint32_t i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + uint16_t s_ninodes; + uint16_t s_nzones; + uint16_t s_imap_blocks; + uint16_t s_zmap_blocks; + uint16_t s_firstdatazone; + uint16_t s_log_zone_size; + uint32_t s_max_size; + uint16_t s_magic; + uint16_t s_state; + uint32_t s_zones; +}; + +struct minix_dir_entry { + uint16_t inode; + char name[0]; +}; + +#define BLOCK_SIZE_BITS 10 +#define BLOCK_SIZE (1<> 3] & (1<<(i & 7))) != 0; +} +#define inode_in_use(x) (bit(inode_map,(x))) +#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1)) + +#define mark_inode(x) (setbit(inode_map,(x))) +#define unmark_inode(x) (clrbit(inode_map,(x))) + +#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1)) +#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1)) + +/* + * Check to make certain that our new filesystem won't be created on + * an already mounted partition. Code adapted from mke2fs, Copyright + * (C) 1994 Theodore Ts'o. Also licensed under GPL. + */ +static inline void check_mount(void) +{ + FILE *f; + struct mntent *mnt; + + if ((f = setmntent(MOUNTED, "r")) == NULL) + return; + while ((mnt = getmntent(f)) != NULL) + if (strcmp(device_name, mnt->mnt_fsname) == 0) + break; + endmntent(f); + if (!mnt) + return; + + bb_error_msg_and_die("%s is mounted; will not make a filesystem here!", device_name); +} + +static long valid_offset(int fd, int offset) +{ + char ch; + + if (lseek(fd, offset, 0) < 0) + return 0; + if (read(fd, &ch, 1) < 1) + return 0; + return 1; +} + +static inline int count_blocks(int fd) +{ + int high, low; + + low = 0; + for (high = 1; valid_offset(fd, high); high *= 2) + low = high; + while (low < high - 1) { + const int mid = (low + high) / 2; + + if (valid_offset(fd, mid)) + low = mid; + else + high = mid; + } + valid_offset(fd, 0); + return (low + 1); +} + +static inline int get_size(const char *file) +{ + int fd; + long size; + + if ((fd = open(file, O_RDWR)) < 0) + bb_perror_msg_and_die("%s", file); + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + close(fd); + return (size * 512); + } + + size = count_blocks(fd); + close(fd); + return size; +} + +static inline void write_tables(void) +{ + /* Mark the super block valid. */ + Super.s_state |= MINIX_VALID_FS; + Super.s_state &= ~MINIX_ERROR_FS; + + if (lseek(DEV, 0, SEEK_SET)) + bb_error_msg_and_die("seek to boot block failed in write_tables"); + if (512 != write(DEV, boot_block_buffer, 512)) + bb_error_msg_and_die("unable to clear boot sector"); + if (BLOCK_SIZE != lseek(DEV, BLOCK_SIZE, SEEK_SET)) + bb_error_msg_and_die("seek failed in write_tables"); + if (BLOCK_SIZE != write(DEV, super_block_buffer, BLOCK_SIZE)) + bb_error_msg_and_die("unable to write super-block"); + if (IMAPS * BLOCK_SIZE != write(DEV, inode_map, IMAPS * BLOCK_SIZE)) + bb_error_msg_and_die("unable to write inode map"); + if (ZMAPS * BLOCK_SIZE != write(DEV, zone_map, ZMAPS * BLOCK_SIZE)) + bb_error_msg_and_die("unable to write zone map"); + if (INODE_BUFFER_SIZE != write(DEV, inode_buffer, INODE_BUFFER_SIZE)) + bb_error_msg_and_die("unable to write inodes"); + +} + +static void write_block(int blk, char *buffer) +{ + if (blk * BLOCK_SIZE != lseek(DEV, blk * BLOCK_SIZE, SEEK_SET)) + bb_error_msg_and_die("seek failed in write_block"); + if (BLOCK_SIZE != write(DEV, buffer, BLOCK_SIZE)) + bb_error_msg_and_die("write failed in write_block"); +} + +static int get_free_block(void) +{ + int blk; + + if (used_good_blocks + 1 >= MAX_GOOD_BLOCKS) + bb_error_msg_and_die("too many bad blocks"); + if (used_good_blocks) + blk = good_blocks_table[used_good_blocks - 1] + 1; + else + blk = FIRSTZONE; + while (blk < ZONES && zone_in_use(blk)) + blk++; + if (blk >= ZONES) + bb_error_msg_and_die("not enough good blocks"); + good_blocks_table[used_good_blocks] = blk; + used_good_blocks++; + return blk; +} + +static inline void mark_good_blocks(void) +{ + int blk; + + for (blk = 0; blk < used_good_blocks; blk++) + mark_zone(good_blocks_table[blk]); +} + +static int next(int zone) +{ + if (!zone) + zone = FIRSTZONE - 1; + while (++zone < ZONES) + if (zone_in_use(zone)) + return zone; + return 0; +} + +static inline void make_bad_inode(void) +{ + struct minix_inode *inode = &Inode[MINIX_BAD_INO]; + int i, j, zone; + int ind = 0, dind = 0; + unsigned short ind_block[BLOCK_SIZE >> 1]; + unsigned short dind_block[BLOCK_SIZE >> 1]; + +#define NEXT_BAD (zone = next(zone)) + + if (!badblocks) + return; + mark_inode(MINIX_BAD_INO); + inode->i_nlinks = 1; + inode->i_time = time(NULL); + inode->i_mode = S_IFREG + 0000; + inode->i_size = badblocks * BLOCK_SIZE; + zone = next(0); + for (i = 0; i < 7; i++) { + inode->i_zone[i] = zone; + if (!NEXT_BAD) + goto end_bad; + } + inode->i_zone[7] = ind = get_free_block(); + memset(ind_block, 0, BLOCK_SIZE); + for (i = 0; i < 512; i++) { + ind_block[i] = zone; + if (!NEXT_BAD) + goto end_bad; + } + inode->i_zone[8] = dind = get_free_block(); + memset(dind_block, 0, BLOCK_SIZE); + for (i = 0; i < 512; i++) { + write_block(ind, (char *) ind_block); + dind_block[i] = ind = get_free_block(); + memset(ind_block, 0, BLOCK_SIZE); + for (j = 0; j < 512; j++) { + ind_block[j] = zone; + if (!NEXT_BAD) + goto end_bad; + } + } + bb_error_msg_and_die("too many bad blocks"); + end_bad: + if (ind) + write_block(ind, (char *) ind_block); + if (dind) + write_block(dind, (char *) dind_block); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static inline void make_bad_inode2(void) +{ + struct minix2_inode *inode = &Inode2[MINIX_BAD_INO]; + int i, j, zone; + int ind = 0, dind = 0; + unsigned long ind_block[BLOCK_SIZE >> 2]; + unsigned long dind_block[BLOCK_SIZE >> 2]; + + if (!badblocks) + return; + mark_inode(MINIX_BAD_INO); + inode->i_nlinks = 1; + inode->i_atime = inode->i_mtime = inode->i_ctime = time(NULL); + inode->i_mode = S_IFREG + 0000; + inode->i_size = badblocks * BLOCK_SIZE; + zone = next(0); + for (i = 0; i < 7; i++) { + inode->i_zone[i] = zone; + if (!NEXT_BAD) + goto end_bad; + } + inode->i_zone[7] = ind = get_free_block(); + memset(ind_block, 0, BLOCK_SIZE); + for (i = 0; i < 256; i++) { + ind_block[i] = zone; + if (!NEXT_BAD) + goto end_bad; + } + inode->i_zone[8] = dind = get_free_block(); + memset(dind_block, 0, BLOCK_SIZE); + for (i = 0; i < 256; i++) { + write_block(ind, (char *) ind_block); + dind_block[i] = ind = get_free_block(); + memset(ind_block, 0, BLOCK_SIZE); + for (j = 0; j < 256; j++) { + ind_block[j] = zone; + if (!NEXT_BAD) + goto end_bad; + } + } + /* Could make triple indirect block here */ + bb_error_msg_and_die("too many bad blocks"); + end_bad: + if (ind) + write_block(ind, (char *) ind_block); + if (dind) + write_block(dind, (char *) dind_block); +} +#endif + +static inline void make_root_inode(void) +{ + struct minix_inode *inode = &Inode[MINIX_ROOT_INO]; + + mark_inode(MINIX_ROOT_INO); + inode->i_zone[0] = get_free_block(); + inode->i_nlinks = 2; + inode->i_time = time(NULL); + if (badblocks) + inode->i_size = 3 * dirsize; + else { + root_block[2 * dirsize] = '\0'; + root_block[2 * dirsize + 1] = '\0'; + inode->i_size = 2 * dirsize; + } + inode->i_mode = S_IFDIR + 0755; + inode->i_uid = getuid(); + if (inode->i_uid) + inode->i_gid = getgid(); + write_block(inode->i_zone[0], root_block); +} + +#ifdef CONFIG_FEATURE_MINIX2 +static inline void make_root_inode2(void) +{ + struct minix2_inode *inode = &Inode2[MINIX_ROOT_INO]; + + mark_inode(MINIX_ROOT_INO); + inode->i_zone[0] = get_free_block(); + inode->i_nlinks = 2; + inode->i_atime = inode->i_mtime = inode->i_ctime = time(NULL); + if (badblocks) + inode->i_size = 3 * dirsize; + else { + root_block[2 * dirsize] = '\0'; + root_block[2 * dirsize + 1] = '\0'; + inode->i_size = 2 * dirsize; + } + inode->i_mode = S_IFDIR + 0755; + inode->i_uid = getuid(); + if (inode->i_uid) + inode->i_gid = getgid(); + write_block(inode->i_zone[0], root_block); +} +#endif + +static inline void setup_tables(void) +{ + int i; + unsigned long inodes; + + memset(super_block_buffer, 0, BLOCK_SIZE); + memset(boot_block_buffer, 0, 512); + MAGIC = magic; + ZONESIZE = 0; + MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024; +#ifdef CONFIG_FEATURE_MINIX2 + if (version2) { + Super.s_zones = BLOCKS; + } else +#endif + Super.s_nzones = BLOCKS; + +/* some magic nrs: 1 inode / 3 blocks */ + if (req_nr_inodes == 0) + inodes = BLOCKS / 3; + else + inodes = req_nr_inodes; + /* Round up inode count to fill block size */ +#ifdef CONFIG_FEATURE_MINIX2 + if (version2) + inodes = ((inodes + MINIX2_INODES_PER_BLOCK - 1) & + ~(MINIX2_INODES_PER_BLOCK - 1)); + else +#endif + inodes = ((inodes + MINIX_INODES_PER_BLOCK - 1) & + ~(MINIX_INODES_PER_BLOCK - 1)); + if (inodes > 65535) + inodes = 65535; + INODES = inodes; + IMAPS = UPPER(INODES + 1, BITS_PER_BLOCK); + ZMAPS = 0; + i = 0; + while (ZMAPS != + UPPER(BLOCKS - (2 + IMAPS + ZMAPS + INODE_BLOCKS) + 1, + BITS_PER_BLOCK) && i < 1000) { + ZMAPS = + UPPER(BLOCKS - (2 + IMAPS + ZMAPS + INODE_BLOCKS) + 1, + BITS_PER_BLOCK); + i++; + } + /* Real bad hack but overwise mkfs.minix can be thrown + * in infinite loop... + * try: + * dd if=/dev/zero of=test.fs count=10 bs=1024 + * /sbin/mkfs.minix -i 200 test.fs + * */ + if (i >= 999) { + bb_error_msg_and_die("unable to allocate buffers for maps"); + } + FIRSTZONE = NORM_FIRSTZONE; + inode_map = xmalloc(IMAPS * BLOCK_SIZE); + zone_map = xmalloc(ZMAPS * BLOCK_SIZE); + memset(inode_map, 0xff, IMAPS * BLOCK_SIZE); + memset(zone_map, 0xff, ZMAPS * BLOCK_SIZE); + for (i = FIRSTZONE; i < ZONES; i++) + unmark_zone(i); + for (i = MINIX_ROOT_INO; i <= INODES; i++) + unmark_inode(i); + inode_buffer = xmalloc(INODE_BUFFER_SIZE); + memset(inode_buffer, 0, INODE_BUFFER_SIZE); + printf("%ld inodes\n", (long)INODES); + printf("%ld blocks\n", (long)ZONES); + printf("Firstdatazone=%ld (%ld)\n", (long)FIRSTZONE, (long)NORM_FIRSTZONE); + printf("Zonesize=%d\n", BLOCK_SIZE << ZONESIZE); + printf("Maxsize=%ld\n\n", (long)MAXSIZE); +} + +/* + * Perform a test of a block; return the number of + * blocks readable/writable. + */ +static inline long do_check(char *buffer, int try, unsigned int current_block) +{ + long got; + + /* Seek to the correct loc. */ + if (lseek(DEV, current_block * BLOCK_SIZE, SEEK_SET) != + current_block * BLOCK_SIZE) { + bb_error_msg_and_die("seek failed during testing of blocks"); + } + + + /* Try the read */ + got = read(DEV, buffer, try * BLOCK_SIZE); + if (got < 0) + got = 0; + if (got & (BLOCK_SIZE - 1)) { + printf("Weird values in do_check: probably bugs\n"); + } + got /= BLOCK_SIZE; + return got; +} + +static unsigned int currently_testing = 0; + +static void alarm_intr(int alnum) +{ + if (currently_testing >= ZONES) + return; + signal(SIGALRM, alarm_intr); + alarm(5); + if (!currently_testing) + return; + printf("%d ...", currently_testing); + fflush(stdout); +} + +static void check_blocks(void) +{ + int try, got; + static char buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS]; + + currently_testing = 0; + signal(SIGALRM, alarm_intr); + alarm(5); + while (currently_testing < ZONES) { + if (lseek(DEV, currently_testing * BLOCK_SIZE, SEEK_SET) != + currently_testing * BLOCK_SIZE) + bb_error_msg_and_die("seek failed in check_blocks"); + try = TEST_BUFFER_BLOCKS; + if (currently_testing + try > ZONES) + try = ZONES - currently_testing; + got = do_check(buffer, try, currently_testing); + currently_testing += got; + if (got == try) + continue; + if (currently_testing < FIRSTZONE) + bb_error_msg_and_die("bad blocks before data-area: cannot make fs"); + mark_zone(currently_testing); + badblocks++; + currently_testing++; + } + if (badblocks > 1) + printf("%d bad blocks\n", badblocks); + else if (badblocks == 1) + printf("one bad block\n"); +} + +static void get_list_blocks(char *filename) +{ + FILE *listfile; + unsigned long blockno; + + listfile = bb_xfopen(filename, "r"); + while (!feof(listfile)) { + fscanf(listfile, "%ld\n", &blockno); + mark_zone(blockno); + badblocks++; + } + if (badblocks > 1) + printf("%d bad blocks\n", badblocks); + else if (badblocks == 1) + printf("one bad block\n"); +} + +extern int mkfs_minix_main(int argc, char **argv) +{ + int i=1; + char *tmp; + struct stat statbuf; + char *listfile = NULL; + int stopIt=FALSE; + + if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE) + bb_error_msg_and_die("bad inode size"); +#ifdef CONFIG_FEATURE_MINIX2 + if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) + bb_error_msg_and_die("bad inode size"); +#endif + + /* Parse options */ + argv++; + while (--argc >= 0 && *argv && **argv) { + if (**argv == '-') { + stopIt=FALSE; + while (i > 0 && *++(*argv) && stopIt==FALSE) { + switch (**argv) { + case 'c': + check = 1; + break; + case 'i': + { + char *cp=NULL; + if (*(*argv+1) != 0) { + cp = ++(*argv); + } else { + if (--argc == 0) { + goto goodbye; + } + cp = *(++argv); + } + req_nr_inodes = strtoul(cp, &tmp, 0); + if (*tmp) + bb_show_usage(); + stopIt=TRUE; + break; + } + case 'l': + if (--argc == 0) { + goto goodbye; + } + listfile = *(++argv); + break; + case 'n': + { + char *cp=NULL; + + if (*(*argv+1) != 0) { + cp = ++(*argv); + } else { + if (--argc == 0) { + goto goodbye; + } + cp = *(++argv); + } + i = strtoul(cp, &tmp, 0); + if (*tmp) + bb_show_usage(); + if (i == 14) + magic = MINIX_SUPER_MAGIC; + else if (i == 30) + magic = MINIX_SUPER_MAGIC2; + else + bb_show_usage(); + namelen = i; + dirsize = i + 2; + stopIt=TRUE; + break; + } + case 'v': +#ifdef CONFIG_FEATURE_MINIX2 + version2 = 1; +#else + bb_error_msg("%s: not compiled with minix v2 support", + device_name); + exit(-1); +#endif + break; + case '-': + case 'h': + default: +goodbye: + bb_show_usage(); + } + } + } else { + if (device_name == NULL) + device_name = *argv; + else if (BLOCKS == 0) + BLOCKS = strtol(*argv, &tmp, 0); + else { + goto goodbye; + } + } + argv++; + } + + if (device_name && !BLOCKS) + BLOCKS = get_size(device_name) / 1024; + if (!device_name || BLOCKS < 10) { + bb_show_usage(); + } +#ifdef CONFIG_FEATURE_MINIX2 + if (version2) { + if (namelen == 14) + magic = MINIX2_SUPER_MAGIC; + else + magic = MINIX2_SUPER_MAGIC2; + } else +#endif + if (BLOCKS > 65535) + BLOCKS = 65535; + check_mount(); /* is it already mounted? */ + tmp = root_block; + *(short *) tmp = 1; + strcpy(tmp + 2, "."); + tmp += dirsize; + *(short *) tmp = 1; + strcpy(tmp + 2, ".."); + tmp += dirsize; + *(short *) tmp = 2; + strcpy(tmp + 2, ".badblocks"); + DEV = open(device_name, O_RDWR); + if (DEV < 0) + bb_error_msg_and_die("unable to open %s", device_name); + if (fstat(DEV, &statbuf) < 0) + bb_error_msg_and_die("unable to stat %s", device_name); + if (!S_ISBLK(statbuf.st_mode)) + check = 0; + else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) + bb_error_msg_and_die("will not try to make filesystem on '%s'", device_name); + setup_tables(); + if (check) + check_blocks(); + else if (listfile) + get_list_blocks(listfile); +#ifdef CONFIG_FEATURE_MINIX2 + if (version2) { + make_root_inode2(); + make_bad_inode2(); + } else +#endif + { + make_root_inode(); + make_bad_inode(); + } + mark_good_blocks(); + write_tables(); + return( 0); + +} diff --git a/busybox/util-linux/mkswap.c b/busybox/util-linux/mkswap.c new file mode 100644 index 000000000..1fc648f3a --- /dev/null +++ b/busybox/util-linux/mkswap.c @@ -0,0 +1,418 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkswap.c - set up a linux swap device + * + * (C) 1991 Linus Torvalds. This file may be redistributed as per + * the Linux copyright. + */ + +/* + * 20.12.91 - time began. Got VM working yesterday by doing this by hand. + * + * Usage: mkswap [-c] [-vN] [-f] device [size-in-blocks] + * + * -c for readability checking. (Use it unless you are SURE!) + * -vN for swap areas version N. (Only N=0,1 known today.) + * -f for forcing swap creation even if it would smash partition table. + * + * The device may be a block device or an image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + * + * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the + * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995. + * + * Version 1 swap area code (for kernel 2.1.117), aeb, 981010. + * + * Sparc fixes, jj@ultra.linux.cz (Jakub Jelinek), 981201 - mangled by aeb. + * V1_MAX_PAGES fixes, jj, 990325. + * + * 1999-02-22 Arkadiusz Mi¶kiewicz + * - 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 inline void init_signature_page(void) +{ + pagesize = getpagesize(); + +#ifdef PAGE_SIZE + if (pagesize != PAGE_SIZE) + bb_error_msg("Assuming pages of size %d", pagesize); +#endif + signature_page = (int *) xmalloc(pagesize); + memset(signature_page, 0, pagesize); + p = (struct swap_header_v1 *) signature_page; +} + +static inline void write_signature(char *sig) +{ + char *sp = (char *) signature_page; + + strncpy(sp + pagesize - 10, sig, 10); +} + +#define V0_MAX_PAGES (8 * (pagesize - 10)) +/* Before 2.2.0pre9 */ +#define V1_OLD_MAX_PAGES ((0x7fffffff / pagesize) - 1) +/* Since 2.2.0pre9: + error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL)) + with variations on + #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) + #define SWP_OFFSET(entry) ((entry) >> 8) + on the various architectures. Below the result - yuk. + + Machine pagesize SWP_ENTRY SWP_OFFSET bound+1 oldbound+2 + i386 2^12 o<<8 e>>8 1<<24 1<<19 + mips 2^12 o<<15 e>>15 1<<17 1<<19 + alpha 2^13 o<<40 e>>40 1<<24 1<<18 + m68k 2^12 o<<12 e>>12 1<<20 1<<19 + sparc 2^{12,13} (o&0x3ffff)<<9 (e>>9)&0x3ffff 1<<18 1<<{19,18} + sparc64 2^13 o<<13 e>>13 1<<51 1<<18 + ppc 2^12 o<<8 e>>8 1<<24 1<<19 + armo 2^{13,14,15} o<<8 e>>8 1<<24 1<<{18,17,16} + armv 2^12 o<<9 e>>9 1<<23 1<<19 + + assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere. + + The bad part is that we need to know this since the kernel will + refuse a swap space if it is too large. +*/ +/* patch from jj - why does this differ from the above? */ +#if defined(__alpha__) +#define V1_MAX_PAGES ((1 << 24) - 1) +#elif defined(__mips__) +#define V1_MAX_PAGES ((1 << 17) - 1) +#elif defined(__sparc_v9__) +#define V1_MAX_PAGES ((3 << 29) - 1) +#elif defined(__sparc__) +#define V1_MAX_PAGES (pagesize == 8192 ? ((3 << 29) - 1) : ((1 << 18) - 1)) +#else +#define V1_MAX_PAGES V1_OLD_MAX_PAGES +#endif +/* man page now says: +The maximum useful size of a swap area now depends on the architecture. +It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips, +128GB on alpha and 3TB on sparc64. +*/ + +#define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int)) + +static inline void bit_set(unsigned int *addr, unsigned int nr) +{ + unsigned int r, m; + + addr += nr / (8 * sizeof(int)); + + r = *addr; + m = 1 << (nr & (8 * sizeof(int) - 1)); + + *addr = r | m; +} + +static int bit_test_and_clear(unsigned int *addr, unsigned int nr) +{ + unsigned int r, m; + + addr += nr / (8 * sizeof(int)); + + r = *addr; + m = 1 << (nr & (8 * sizeof(int) - 1)); + + *addr = r & ~m; + return (r & m) != 0; +} + + +static void page_ok(int page) +{ + if (version == 0) + bit_set(signature_page, page); +} + +static inline void page_bad(int page) +{ + if (version == 0) + bit_test_and_clear(signature_page, page); + else { + if (badpages == MAX_BADPAGES) + bb_error_msg_and_die("too many bad pages"); + p->badpages[badpages] = page; + } + badpages++; +} + +static void check_blocks(void) +{ + unsigned int current_page; + int do_seek = 1; + char *buffer; + + buffer = xmalloc(pagesize); + current_page = 0; + while (current_page < PAGES) { + if (!check) { + page_ok(current_page++); + continue; + } + if (do_seek && lseek(DEV, current_page * pagesize, SEEK_SET) != + current_page * pagesize) + bb_error_msg_and_die("seek failed in check_blocks"); + if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) { + page_bad(current_page++); + continue; + } + page_ok(current_page++); + } + if (badpages == 1) + printf("one bad page\n"); + else if (badpages > 1) + printf("%d bad pages\n", badpages); +} + +static long valid_offset(int fd, int offset) +{ + char ch; + + if (lseek(fd, offset, 0) < 0) + return 0; + if (read(fd, &ch, 1) < 1) + return 0; + return 1; +} + +static int find_size(int fd) +{ + unsigned int high, low; + + low = 0; + for (high = 1; high > 0 && valid_offset(fd, high); high *= 2) + low = high; + while (low < high - 1) { + const int mid = (low + high) / 2; + + if (valid_offset(fd, mid)) + low = mid; + else + high = mid; + } + return (low + 1); +} + +/* return size in pages, to avoid integer overflow */ +static long get_size(const char *file) +{ + int fd; + long size; + + if ((fd = open(file, O_RDONLY)) < 0) + bb_perror_msg_and_die("%s", file); + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + int sectors_per_page = pagesize / 512; + + size /= sectors_per_page; + } else { + size = find_size(fd) / pagesize; + } + close(fd); + return size; +} + +int mkswap_main(int argc, char **argv) +{ + char *tmp; + struct stat statbuf; + int sz; + int maxpages; + int goodpages; + int offset; + int force = 0; + + init_signature_page(); /* get pagesize */ + + while (argc-- > 1) { + argv++; + if (argv[0][0] != '-') { + if (device_name) { + int blocks_per_page = pagesize / 1024; + + PAGES = strtol(argv[0], &tmp, 0) / blocks_per_page; + if (*tmp) + bb_show_usage(); + } else + device_name = argv[0]; + } else { + switch (argv[0][1]) { + case 'c': + check = 1; + break; + case 'f': + force = 1; + break; + case 'v': + version = atoi(argv[0] + 2); + break; + default: + bb_show_usage(); + } + } + } + if (!device_name) { + bb_error_msg("error: Nowhere to set up swap on?"); + bb_show_usage(); + } + sz = get_size(device_name); + if (!PAGES) { + PAGES = sz; + } else if (PAGES > sz && !force) { + bb_error_msg("error: size %ld is larger than device size %d", + PAGES * (pagesize / 1024), sz * (pagesize / 1024)); + return EXIT_FAILURE; + } + + if (version == -1) { + if (get_kernel_revision() < MAKE_VERSION(2, 1, 117)) + version = 0; + else + version = 1; + } + if (version != 0 && version != 1) { + bb_error_msg("error: unknown version %d", version); + bb_show_usage(); + } + if (PAGES < 10) { + bb_error_msg("error: swap area needs to be at least %ldkB", + (long) (10 * pagesize / 1024)); + bb_show_usage(); + } +#if 0 + maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES); +#else + if (!version) + maxpages = V0_MAX_PAGES; + else if (get_kernel_revision() >= MAKE_VERSION(2, 2, 1)) + maxpages = V1_MAX_PAGES; + else { + maxpages = V1_OLD_MAX_PAGES; + if (maxpages > V1_MAX_PAGES) + maxpages = V1_MAX_PAGES; + } +#endif + if (PAGES > maxpages) { + PAGES = maxpages; + bb_error_msg("warning: truncating swap area to %ldkB", + PAGES * pagesize / 1024); + } + + DEV = open(device_name, O_RDWR); + if (DEV < 0 || fstat(DEV, &statbuf) < 0) + bb_perror_msg_and_die("%s", device_name); + if (!S_ISBLK(statbuf.st_mode)) + check = 0; + else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) + bb_error_msg_and_die("Will not try to make swapdevice on '%s'", device_name); + +#ifdef __sparc__ + if (!force && version == 0) { + /* Don't overwrite partition table unless forced */ + unsigned char *buffer = (unsigned char *) signature_page; + unsigned short *q, sum; + + if (read(DEV, buffer, 512) != 512) + bb_error_msg_and_die("fatal: first page unreadable"); + if (buffer[508] == 0xDA && buffer[509] == 0xBE) { + q = (unsigned short *) (buffer + 510); + for (sum = 0; q >= (unsigned short *) buffer;) + sum ^= *q--; + if (!sum) { + bb_error_msg("Device '%s' contains a valid Sun disklabel.\n" +"This probably means creating v0 swap would destroy your partition table\n" +"No swap created. If you really want to create swap v0 on that device, use\n" +"the -f option to force it.", device_name); + return EXIT_FAILURE; + } + } + } +#endif + + if (version == 0 || check) + check_blocks(); + if (version == 0 && !bit_test_and_clear(signature_page, 0)) + bb_error_msg_and_die("fatal: first page unreadable"); + if (version == 1) { + p->version = version; + p->last_page = PAGES - 1; + p->nr_badpages = badpages; + } + + goodpages = PAGES - badpages - 1; + if (goodpages <= 0) + bb_error_msg_and_die("Unable to set up swap-space: unreadable"); + printf("Setting up swapspace version %d, size = %ld bytes\n", + version, (long) (goodpages * pagesize)); + write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2"); + + offset = ((version == 0) ? 0 : 1024); + if (lseek(DEV, offset, SEEK_SET) != offset) + bb_error_msg_and_die("unable to rewind swap-device"); + if (write(DEV, (char *) signature_page + offset, pagesize - offset) + != pagesize - offset) + bb_error_msg_and_die("unable to write signature page"); + + /* + * A subsequent swapon() will fail if the signature + * is not actually on disk. (This is a kernel bug.) + */ + if (fsync(DEV)) + bb_error_msg_and_die("fsync failed"); + return EXIT_SUCCESS; +} diff --git a/busybox/util-linux/more.c b/busybox/util-linux/more.c new file mode 100644 index 000000000..e91038883 --- /dev/null +++ b/busybox/util-linux/more.c @@ -0,0 +1,211 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini more implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * Copyright (C) 1999-2004 by Erik Andersen + * + * 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" + + +#ifdef CONFIG_FEATURE_USE_TERMIOS +static int cin_fileno; +#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(cin_fileno, &initial_settings); +} + +static void gotsig(int sig) +{ + putchar('\n'); + exit(EXIT_FAILURE); +} +#endif /* CONFIG_FEATURE_USE_TERMIOS */ + + +extern int more_main(int argc, char **argv) +{ + int c, lines, input = 0; + int please_display_more_prompt = 0; + struct stat st; + FILE *file; + FILE *cin; + int len, page_height; + int terminal_width; + int terminal_height; +#ifndef CONFIG_FEATURE_USE_TERMIOS + int cin_fileno; +#endif + + argc--; + argv++; + + + /* not use inputing from terminal if usage: more > outfile */ + if(isatty(STDOUT_FILENO)) { + cin = fopen(CURRENT_TTY, "r"); + if (!cin) + cin = bb_xfopen(CONSOLE_DEV, "r"); + cin_fileno = fileno(cin); + please_display_more_prompt = 2; +#ifdef CONFIG_FEATURE_USE_TERMIOS + getTermSettings(cin_fileno, &initial_settings); + new_settings = initial_settings; + new_settings.c_lflag &= ~ICANON; + new_settings.c_lflag &= ~ECHO; + new_settings.c_cc[VMIN] = 1; + new_settings.c_cc[VTIME] = 0; + setTermSettings(cin_fileno, &new_settings); + atexit(set_tty_to_initial_mode); + (void) signal(SIGINT, gotsig); + (void) signal(SIGQUIT, gotsig); + (void) signal(SIGTERM, gotsig); +#endif + } else { + cin = stdin; + } + + do { + if (argc == 0) { + file = stdin; + } else + file = bb_wfopen(*argv, "r"); + if(file==0) + goto loop; + + st.st_size = 0; + fstat(fileno(file), &st); + + please_display_more_prompt &= ~1; + + get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height); + if (terminal_height > 4) + terminal_height -= 2; + if (terminal_width > 0) + terminal_width -= 1; + + len=0; + lines = 0; + page_height = terminal_height; + while ((c = getc(file)) != EOF) { + + if ((please_display_more_prompt & 3) == 3) { + len = printf("--More-- "); + if (file != stdin && st.st_size > 0) { +#if _FILE_OFFSET_BITS == 64 + len += printf("(%d%% of %lld bytes)", + (int) (100 * ((double) ftell(file) / + (double) st.st_size)), (long long)st.st_size); +#else + len += printf("(%d%% of %ld bytes)", + (int) (100 * ((double) ftell(file) / + (double) st.st_size)), (long)st.st_size); +#endif + } + + fflush(stdout); + + /* + * We've just displayed the "--More--" prompt, so now we need + * to get input from the user. + */ + input = getc(cin); +#ifndef CONFIG_FEATURE_USE_TERMIOS + printf("\033[A"); /* up cursor */ +#endif + /* Erase the "More" message */ + putc('\r', stdout); + while (--len >= 0) + putc(' ', stdout); + putc('\r', stdout); + len=0; + lines = 0; + page_height = terminal_height; + please_display_more_prompt &= ~1; + + if (input == 'q') + goto end; + } + + /* + * There are two input streams to worry about here: + * + * c : the character we are reading from the file being "mored" + * input : a character received from the keyboard + * + * If we hit a newline in the _file_ stream, we want to test and + * see if any characters have been hit in the _input_ stream. This + * allows the user to quit while in the middle of a file. + */ + if (c == '\n') { + /* increment by just one line if we are at + * the end of this line */ + if (input == '\n') + please_display_more_prompt |= 1; + /* Adjust the terminal height for any overlap, so that + * no lines get lost off the top. */ + if (len >= terminal_width) { + int quot, rem; + quot = len / terminal_width; + rem = len - (quot * terminal_width); + if (quot) { + if (rem) + page_height-=quot; + else + page_height-=(quot-1); + } + } + if (++lines >= page_height) { + please_display_more_prompt |= 1; + } + len=0; + } + /* + * If we just read a newline from the file being 'mored' and any + * key other than a return is hit, scroll by one page + */ + putc(c, stdout); + len++; + } + fclose(file); + fflush(stdout); +loop: + argv++; + } while (--argc > 0); + end: + return 0; +} diff --git a/busybox/util-linux/mount.c b/busybox/util-linux/mount.c new file mode 100644 index 000000000..b059d7094 --- /dev/null +++ b/busybox/util-linux/mount.c @@ -0,0 +1,497 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mount implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * Copyright (C) 1999-2004 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 + * + * 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 support 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" + +#ifdef CONFIG_NFSMOUNT +#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) +#error "You need to build uClibc with UCLIBC_HAS_RPC for busybox mount with NFS support to compile." +#endif +#endif + +enum { + MS_MGC_VAL = 0xc0ed0000, /* Magic number indicatng "new" flags */ + MS_RDONLY = 1, /* Mount read-only */ + MS_NOSUID = 2, /* Ignore suid and sgid bits */ + MS_NODEV = 4, /* Disallow access to device special files */ + MS_NOEXEC = 8, /* Disallow program execution */ + MS_SYNCHRONOUS = 16, /* Writes are synced at once */ + MS_REMOUNT = 32, /* Alter flags of a mounted FS */ + MS_MANDLOCK = 64, /* Allow mandatory locks on an FS */ + S_QUOTA = 128, /* Quota initialized for file/directory/symlink */ + S_APPEND = 256, /* Append-only file */ + S_IMMUTABLE = 512, /* Immutable file */ + MS_NOATIME = 1024, /* Do not update access times. */ + MS_NODIRATIME = 2048, /* Do not update directory access times */ + MS_BIND = 4096, /* Use the new linux 2.4.x "mount --bind" feature */ + MS_MOVE = 8192, /* Use the new linux 2.4.x "mount --move" feature */ +}; + + +#if defined CONFIG_FEATURE_MOUNT_LOOP +#include +#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); + +struct mount_options { + const char *name; + unsigned long and; + unsigned long or; +}; + +static const struct mount_options mount_options[] = { + {"async", ~MS_SYNCHRONOUS, 0}, + {"atime", ~0, ~MS_NOATIME}, + {"defaults", ~0, 0}, + {"noauto", ~0, 0}, + {"dev", ~MS_NODEV, 0}, + {"diratime", ~0, ~MS_NODIRATIME}, + {"exec", ~MS_NOEXEC, 0}, + {"noatime", ~0, MS_NOATIME}, + {"nodev", ~0, MS_NODEV}, + {"nodiratime", ~0, MS_NODIRATIME}, + {"noexec", ~0, MS_NOEXEC}, + {"nosuid", ~0, MS_NOSUID}, + {"remount", ~0, MS_REMOUNT}, + {"ro", ~0, MS_RDONLY}, + {"rw", ~MS_RDONLY, 0}, + {"suid", ~MS_NOSUID, 0}, + {"sync", ~0, MS_SYNCHRONOUS}, + {"bind", ~0, MS_BIND}, + {"move", ~0, MS_MOVE}, + {0, 0, 0} +}; + +static int +do_mount(char *specialfile, char *dir, char *filesystemtype, long flags, + void *string_flags, int useMtab, int fakeIt, char *mtab_opts, + int mount_all) +{ + int status = 0; + +#if defined CONFIG_FEATURE_MOUNT_LOOP + char *lofile = NULL; +#endif + + if (!fakeIt) { +#if defined CONFIG_FEATURE_MOUNT_LOOP + if (use_loop == TRUE) { + int loro = flags & MS_RDONLY; + + lofile = specialfile; + + specialfile = find_unused_loop_device(); + if (specialfile == NULL) { + bb_error_msg_and_die("Could not find a spare loop device"); + } + if (set_loop(specialfile, lofile, 0, &loro)) { + bb_error_msg_and_die("Could not setup loop device"); + } + if (!(flags & MS_RDONLY) && loro) { /* loop is ro, but wanted rw */ + bb_error_msg("WARNING: loop device is read-only"); + flags |= MS_RDONLY; + } + } +#endif + status = mount(specialfile, dir, filesystemtype, flags, string_flags); + if (status < 0 && errno == EROFS) { + bb_error_msg("%s is write-protected, mounting read-only", + specialfile); + status = mount(specialfile, dir, filesystemtype, flags |= + MS_RDONLY, string_flags); + } + /* Don't whine about already mounted filesystems when mounting all. */ + if (status < 0 && errno == EBUSY && mount_all) { + return TRUE; + } + } + + + /* If the mount was sucessful, do anything needed, then return TRUE */ + if (status == 0 || fakeIt == TRUE) { + +#if defined CONFIG_FEATURE_MTAB_SUPPORT + if (useMtab) { + erase_mtab(specialfile); /* Clean any stale entries */ + write_mtab(specialfile, dir, filesystemtype, flags, mtab_opts); + } +#endif + return (TRUE); + } + + /* Bummer. mount failed. Clean up */ +#if defined CONFIG_FEATURE_MOUNT_LOOP + if (lofile != NULL) { + del_loop(specialfile); + } +#endif + + if (errno == EPERM) { + bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); + } + + return (FALSE); +} + + +static void paste_str(char **s1, const char *s2) +{ + *s1 = xrealloc(*s1, strlen(*s1) + strlen(s2) + 1); + strcat(*s1, s2); +} + +/* Seperate standard mount options from the nonstandard string options */ +static void parse_mount_options(char *options, int *flags, char **strflags) +{ + while (options) { + int gotone = FALSE; + char *comma = strchr(options, ','); + const struct mount_options *f = mount_options; + + if (comma) { + *comma = '\0'; + } + + while (f->name != 0) { + if (strcasecmp(f->name, options) == 0) { + + *flags &= f->and; + *flags |= f->or; + gotone = TRUE; + break; + } + f++; + } +#if defined CONFIG_FEATURE_MOUNT_LOOP + if (!strcasecmp("loop", options)) { /* loop device support */ + use_loop = TRUE; + gotone = TRUE; + } +#endif + if (!gotone) { + if (**strflags) { + /* have previous parsed options */ + paste_str(strflags, ","); + } + paste_str(strflags, options); + } + if (comma) { + *comma = ','; + options = ++comma; + } else { + break; + } + } +} + +static int mount_one(char *blockDevice, char *directory, char *filesystemType, + unsigned long flags, char *string_flags, int useMtab, + int fakeIt, char *mtab_opts, int whineOnErrors, + int mount_all) +{ + int status = 0; + if (strcmp(filesystemType, "auto") == 0) { + char buf[255]; + FILE *f; + int read_proc = 0; + + f = fopen("/etc/filesystems", "r"); + + if (f) { + while (fgets(buf, sizeof(buf), f)) { + if (*buf == '*') { + read_proc = 1; + } else if (*buf == '#') { + continue; + } else { + filesystemType = buf; + + /* Add NULL termination to each line */ + while (*filesystemType && !isspace(*filesystemType)) { + filesystemType++; + } + *filesystemType = '\0'; + + filesystemType = buf; + + if (bb_strlen(filesystemType)) { + status = do_mount(blockDevice, directory, filesystemType, + flags | MS_MGC_VAL, string_flags, + useMtab, fakeIt, mtab_opts, mount_all); + if (status) { + break; + } + } + + } + } + fclose(f); + } else { + read_proc = 1; + } + + if (read_proc && !status) { + + f = bb_xfopen("/proc/filesystems", "r"); + + while (fgets(buf, sizeof(buf), f) != NULL) { + filesystemType = buf; + if (*filesystemType == '\t') { /* Not a nodev filesystem */ + + /* Add NULL termination to each line */ + while (*filesystemType && *filesystemType != '\n') { + filesystemType++; + } + *filesystemType = '\0'; + + filesystemType = buf; + filesystemType++; /* hop past tab */ + + status = do_mount(blockDevice, directory, filesystemType, + flags | MS_MGC_VAL, string_flags, useMtab, + fakeIt, mtab_opts, mount_all); + if (status) { + break; + } + } + } + fclose(f); + } + } else { + status = do_mount(blockDevice, directory, filesystemType, + flags | MS_MGC_VAL, string_flags, useMtab, fakeIt, + mtab_opts, mount_all); + } + + if (!status) { + if (whineOnErrors) { + bb_perror_msg("Mounting %s on %s failed", blockDevice, directory); + } + return (FALSE); + } + return (TRUE); +} + +static void show_mounts(char *onlytype) +{ + FILE *mountTable = setmntent(bb_path_mtab_file, "r"); + + if (mountTable) { + struct mntent *m; + + while ((m = getmntent(mountTable)) != 0) { + char *blockDevice = m->mnt_fsname; + + if (strcmp(blockDevice, "rootfs") == 0) { + continue; + } else if (strcmp(blockDevice, "/dev/root") == 0) { + blockDevice = find_real_root_device_name(); + } + if (!onlytype || (strcmp(m->mnt_type, onlytype) == 0)) { + printf("%s on %s type %s (%s)\n", blockDevice, m->mnt_dir, + m->mnt_type, m->mnt_opts); + } +#ifdef CONFIG_FEATURE_CLEAN_UP + if (blockDevice != m->mnt_fsname) { + free(blockDevice); + } +#endif + } + endmntent(mountTable); + } else { + bb_perror_msg_and_die(bb_path_mtab_file); + } + exit(EXIT_SUCCESS); +} + +extern int mount_main(int argc, char **argv) +{ + struct stat statbuf; + char *string_flags = bb_xstrdup(""); + char *extra_opts; + int flags = 0; + char *filesystemType = "auto"; + int got_filesystemType = 0; + char *device = xmalloc(PATH_MAX); + char *directory = xmalloc(PATH_MAX); + struct mntent *m = NULL; + int all = FALSE; + int fakeIt = FALSE; + int useMtab = TRUE; + int rc = EXIT_FAILURE; + FILE *f = 0; + int opt; + + /* Parse options */ + while ((opt = getopt(argc, argv, "o:rt:wafnv")) > 0) { + switch (opt) { + case 'o': + parse_mount_options(optarg, &flags, &string_flags); + break; + case 'r': + flags |= MS_RDONLY; + break; + case 't': + filesystemType = optarg; + got_filesystemType = 1; + break; + case 'w': + flags &= ~MS_RDONLY; + break; + case 'a': + all = TRUE; + break; + case 'f': + fakeIt = TRUE; + break; + case 'n': +#ifdef CONFIG_FEATURE_MTAB_SUPPORT + useMtab = FALSE; +#endif + break; + case 'v': + break; /* ignore -v */ + } + } + + if (!all && (optind == argc)) { + show_mounts(got_filesystemType ? filesystemType : NULL); + } + + if (optind < argc) { + /* if device is a filename get its real path */ + if (stat(argv[optind], &statbuf) == 0) { + char *tmp = bb_simplify_path(argv[optind]); + + safe_strncpy(device, tmp, PATH_MAX); + } else { + safe_strncpy(device, argv[optind], PATH_MAX); + } + } + + if (optind + 1 < argc) + directory = bb_simplify_path(argv[optind + 1]); + + if (all || optind + 1 == argc) { + f = setmntent("/etc/fstab", "r"); + + if (f == NULL) + bb_perror_msg_and_die("\nCannot read /etc/fstab"); + + while ((m = getmntent(f)) != NULL) { + if (!all && (optind + 1 == argc) + && ((strcmp(device, m->mnt_fsname) != 0) + && (strcmp(device, m->mnt_dir) != 0))) { + continue; + } + + if (all && ( /* If we're mounting 'all' */ + (strstr(m->mnt_opts, "noauto")) || /* and the file system isn't noauto, */ + (strstr(m->mnt_type, "swap")))) /* and isn't swap, then mount it */ + { + continue; + } + + if (all || flags == 0) { /* Allow single mount to override fstab flags */ + flags = 0; + string_flags[0] = 0; + parse_mount_options(m->mnt_opts, &flags, &string_flags); + } + + strcpy(device, m->mnt_fsname); + strcpy(directory, m->mnt_dir); + filesystemType = bb_xstrdup(m->mnt_type); + singlemount: + extra_opts = string_flags; + rc = EXIT_SUCCESS; +#ifdef CONFIG_NFSMOUNT + if (strchr(device, ':') != NULL) { + filesystemType = "nfs"; + if (nfsmount + (device, directory, &flags, &extra_opts, &string_flags, + 1)) { + bb_perror_msg("nfsmount failed"); + rc = EXIT_FAILURE; + } + } +#endif + if (!mount_one + (device, directory, filesystemType, flags, string_flags, + useMtab, fakeIt, extra_opts, TRUE, all)) { + rc = EXIT_FAILURE; + } + if (!all) { + break; + } + } + if (f) { + endmntent(f); + } + if (!all && f && m == NULL) { + fprintf(stderr, "Can't find %s in /etc/fstab\n", device); + } + return rc; + } + + goto singlemount; +} diff --git a/busybox/util-linux/nfsmount.c b/busybox/util-linux/nfsmount.c new file mode 100644 index 000000000..0ebab80f6 --- /dev/null +++ b/busybox/util-linux/nfsmount.c @@ -0,0 +1,1026 @@ +/* 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 "nfsmount.h" + + +/* + * NFS stats. The good thing with these values is that NFSv3 errors are + * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which + * no-one uses anyway), so we can happily mix code as long as we make sure + * no NFSv3 errors are returned to NFSv2 clients. + * Error codes that have a `--' in the v2 column are not part of the + * standard, but seem to be widely used nevertheless. + */ +enum nfs_stat { + NFS_OK = 0, /* v2 v3 */ + NFSERR_PERM = 1, /* v2 v3 */ + NFSERR_NOENT = 2, /* v2 v3 */ + NFSERR_IO = 5, /* v2 v3 */ + NFSERR_NXIO = 6, /* v2 v3 */ + NFSERR_EAGAIN = 11, /* v2 v3 */ + NFSERR_ACCES = 13, /* v2 v3 */ + NFSERR_EXIST = 17, /* v2 v3 */ + NFSERR_XDEV = 18, /* v3 */ + NFSERR_NODEV = 19, /* v2 v3 */ + NFSERR_NOTDIR = 20, /* v2 v3 */ + NFSERR_ISDIR = 21, /* v2 v3 */ + NFSERR_INVAL = 22, /* v2 v3 that Sun forgot */ + NFSERR_FBIG = 27, /* v2 v3 */ + NFSERR_NOSPC = 28, /* v2 v3 */ + NFSERR_ROFS = 30, /* v2 v3 */ + NFSERR_MLINK = 31, /* v3 */ + NFSERR_OPNOTSUPP = 45, /* v2 v3 */ + NFSERR_NAMETOOLONG = 63, /* v2 v3 */ + NFSERR_NOTEMPTY = 66, /* v2 v3 */ + NFSERR_DQUOT = 69, /* v2 v3 */ + NFSERR_STALE = 70, /* v2 v3 */ + NFSERR_REMOTE = 71, /* v2 v3 */ + NFSERR_WFLUSH = 99, /* v2 */ + NFSERR_BADHANDLE = 10001, /* v3 */ + NFSERR_NOT_SYNC = 10002, /* v3 */ + NFSERR_BAD_COOKIE = 10003, /* v3 */ + NFSERR_NOTSUPP = 10004, /* v3 */ + NFSERR_TOOSMALL = 10005, /* v3 */ + NFSERR_SERVERFAULT = 10006, /* v3 */ + NFSERR_BADTYPE = 10007, /* v3 */ + NFSERR_JUKEBOX = 10008 /* v3 */ +}; + +#define NFS_PROGRAM 100003 + + + +#ifndef NFS_FHSIZE +static const int NFS_FHSIZE = 32; +#endif +#ifndef NFS_PORT +static const int NFS_PORT = 2049; +#endif + +/* Disable the nls stuff */ +# undef bindtextdomain +# define bindtextdomain(Domain, Directory) /* empty */ +# undef textdomain +# define textdomain(Domain) /* empty */ +# define _(Text) (Text) +# define N_(Text) (Text) + +static const int MS_MGC_VAL = 0xc0ed0000; /* Magic number indicatng "new" flags */ +static const int MS_RDONLY = 1; /* Mount read-only */ +static const int MS_NOSUID = 2; /* Ignore suid and sgid bits */ +static const int MS_NODEV = 4; /* Disallow access to device special files */ +static const int MS_NOEXEC = 8; /* Disallow program execution */ +static const int MS_SYNCHRONOUS = 16; /* Writes are synced at once */ +static const int MS_REMOUNT = 32; /* Alter flags of a mounted FS */ +static const int MS_MANDLOCK = 64; /* Allow mandatory locks on an FS */ +static const int S_QUOTA = 128; /* Quota initialized for file/directory/symlink */ +static const int S_APPEND = 256; /* Append-only file */ +static const int S_IMMUTABLE = 512; /* Immutable file */ +static const int MS_NOATIME = 1024; /* Do not update access times. */ +static const int MS_NODIRATIME = 2048; /* Do not update directory access times */ + + +/* + * We want to be able to compile mount on old kernels in such a way + * that the binary will work well on more recent kernels. + * Thus, if necessary we teach nfsmount.c the structure of new fields + * that will come later. + * + * Moreover, the new kernel includes conflict with glibc includes + * so it is easiest to ignore the kernel altogether (at compile time). + */ + +/* NOTE: Do not make this into a 'static const int' because the pre-processor + * needs to test this value in some #if statements. */ +#define NFS_MOUNT_VERSION 4 + +struct nfs2_fh { + char data[32]; +}; +struct nfs3_fh { + unsigned short size; + unsigned char data[64]; +}; + +struct nfs_mount_data { + int version; /* 1 */ + int fd; /* 1 */ + struct nfs2_fh old_root; /* 1 */ + int flags; /* 1 */ + int rsize; /* 1 */ + int wsize; /* 1 */ + int timeo; /* 1 */ + int retrans; /* 1 */ + int acregmin; /* 1 */ + int acregmax; /* 1 */ + int acdirmin; /* 1 */ + int acdirmax; /* 1 */ + struct sockaddr_in addr; /* 1 */ + char hostname[256]; /* 1 */ + int namlen; /* 2 */ + unsigned int bsize; /* 3 */ + struct nfs3_fh root; /* 4 */ +}; + +/* bits in the flags field */ + +static const int NFS_MOUNT_SOFT = 0x0001; /* 1 */ +static const int NFS_MOUNT_INTR = 0x0002; /* 1 */ +static const int NFS_MOUNT_SECURE = 0x0004; /* 1 */ +static const int NFS_MOUNT_POSIX = 0x0008; /* 1 */ +static const int NFS_MOUNT_NOCTO = 0x0010; /* 1 */ +static const int NFS_MOUNT_NOAC = 0x0020; /* 1 */ +static const int NFS_MOUNT_TCP = 0x0040; /* 2 */ +static const int NFS_MOUNT_VER3 = 0x0080; /* 3 */ +static const int NFS_MOUNT_KERBEROS = 0x0100; /* 3 */ +static const int NFS_MOUNT_NONLM = 0x0200; /* 3 */ + + +#define UTIL_LINUX_VERSION "2.10m" +#define util_linux_version "util-linux-2.10m" + +#define HAVE_inet_aton +#define HAVE_scsi_h +#define HAVE_blkpg_h +#define HAVE_kd_h +#define HAVE_termcap +#define HAVE_locale_h +#define HAVE_libintl_h +#define ENABLE_NLS +#define HAVE_langinfo_h +#define HAVE_progname +#define HAVE_openpty +#define HAVE_nanosleep +#define HAVE_personality +#define HAVE_tm_gmtoff + +static char *nfs_strerror(int status); + +#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r)) +#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) + +static const int EX_FAIL = 32; /* mount failure */ +static const int EX_BG = 256; /* retry in background (internal only) */ + + +/* + * nfs_mount_version according to the sources seen at compile time. + */ +static int nfs_mount_version; + +/* + * Unfortunately, the kernel prints annoying console messages + * in case of an unexpected nfs mount version (instead of + * just returning some error). Therefore we'll have to try + * and figure out what version the kernel expects. + * + * Variables: + * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time + * NFS_MOUNT_VERSION: these nfsmount sources at compile time + * nfs_mount_version: version this source and running kernel can handle + */ +static void +find_kernel_nfs_mount_version(void) +{ + static int kernel_version = 0; + + if (kernel_version) + return; + + nfs_mount_version = NFS_MOUNT_VERSION; /* default */ + + kernel_version = get_kernel_revision(); + if (kernel_version) { + if (kernel_version < MAKE_VERSION(2,1,32)) + nfs_mount_version = 1; + else if (kernel_version < MAKE_VERSION(2,2,18) || + (kernel_version >= MAKE_VERSION(2,3,0) && + kernel_version < MAKE_VERSION(2,3,99))) + nfs_mount_version = 3; + else + nfs_mount_version = 4; /* since 2.3.99pre4 */ + } + if (nfs_mount_version > NFS_MOUNT_VERSION) + nfs_mount_version = NFS_MOUNT_VERSION; +} + +static struct pmap * +get_mountport(struct sockaddr_in *server_addr, + long unsigned prog, + long unsigned version, + long unsigned proto, + long unsigned port) +{ +struct pmaplist *pmap; +static struct pmap p = {0, 0, 0, 0}; + +server_addr->sin_port = PMAPPORT; +pmap = pmap_getmaps(server_addr); + +if (version > MAX_NFSPROT) + version = MAX_NFSPROT; +if (!prog) + prog = MOUNTPROG; +p.pm_prog = prog; +p.pm_vers = version; +p.pm_prot = proto; +p.pm_port = port; + +while (pmap) { + if (pmap->pml_map.pm_prog != prog) + goto next; + if (!version && p.pm_vers > pmap->pml_map.pm_vers) + goto next; + if (version > 2 && pmap->pml_map.pm_vers != version) + goto next; + if (version && version <= 2 && pmap->pml_map.pm_vers > 2) + goto next; + if (pmap->pml_map.pm_vers > MAX_NFSPROT || + (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) || + (port && pmap->pml_map.pm_port != port)) + goto next; + memcpy(&p, &pmap->pml_map, sizeof(p)); +next: + pmap = pmap->pml_next; +} +if (!p.pm_vers) + p.pm_vers = MOUNTVERS; +if (!p.pm_port) + p.pm_port = MOUNTPORT; +if (!p.pm_prot) + p.pm_prot = IPPROTO_TCP; +return &p; +} + +int nfsmount(const char *spec, const char *node, int *flags, + char **extra_opts, char **mount_opts, int running_bg) +{ + static char *prev_bg_host; + char hostdir[1024]; + CLIENT *mclient; + char *hostname; + char *pathname; + char *old_opts; + char *mounthost=NULL; + char new_opts[1024]; + struct timeval total_timeout; + enum clnt_stat clnt_stat; + struct nfs_mount_data data; + char *opt, *opteq; + int val; + struct hostent *hp; + struct sockaddr_in server_addr; + struct sockaddr_in mount_server_addr; + struct pmap* pm_mnt; + int msock, fsock; + struct timeval retry_timeout; + union { + struct fhstatus nfsv2; + struct mountres3 nfsv3; + } status; + struct stat statbuf; + char *s; + int port; + int mountport; + int proto; + int bg; + int soft; + int intr; + int posix; + int nocto; + int noac; + int nolock; + int retry; + int tcp; + int mountprog; + int mountvers; + int nfsprog; + int nfsvers; + int retval; + time_t t; + time_t prevt; + time_t timeout; + + find_kernel_nfs_mount_version(); + + retval = EX_FAIL; + msock = fsock = -1; + mclient = NULL; + if (strlen(spec) >= sizeof(hostdir)) { + bb_error_msg("excessively long host:dir argument"); + goto fail; + } + strcpy(hostdir, spec); + if ((s = strchr(hostdir, ':'))) { + hostname = hostdir; + pathname = s + 1; + *s = '\0'; + /* Ignore all but first hostname in replicated mounts + until they can be fully supported. (mack@sgi.com) */ + if ((s = strchr(hostdir, ','))) { + *s = '\0'; + bb_error_msg("warning: multiple hostnames not supported"); + } + } else { + bb_error_msg("directory to mount not in host:dir format"); + goto fail; + } + + server_addr.sin_family = AF_INET; +#ifdef HAVE_inet_aton + if (!inet_aton(hostname, &server_addr.sin_addr)) +#endif + { + if ((hp = gethostbyname(hostname)) == NULL) { + bb_herror_msg("%s", hostname); + goto fail; + } else { + if (hp->h_length > sizeof(struct in_addr)) { + bb_error_msg("got bad hp->h_length"); + hp->h_length = sizeof(struct in_addr); + } + memcpy(&server_addr.sin_addr, + hp->h_addr, hp->h_length); + } + } + + memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr)); + + /* add IP address to mtab options for use when unmounting */ + + s = inet_ntoa(server_addr.sin_addr); + old_opts = *extra_opts; + if (!old_opts) + old_opts = ""; + if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) { + bb_error_msg("excessively long option argument"); + goto fail; + } + sprintf(new_opts, "%s%saddr=%s", + old_opts, *old_opts ? "," : "", s); + *extra_opts = bb_xstrdup(new_opts); + + /* Set default options. + * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to + * let the kernel decide. + * timeo is filled in after we know whether it'll be TCP or UDP. */ + memset(&data, 0, sizeof(data)); + data.retrans = 3; + data.acregmin = 3; + data.acregmax = 60; + data.acdirmin = 30; + data.acdirmax = 60; +#if NFS_MOUNT_VERSION >= 2 + data.namlen = NAME_MAX; +#endif + + bg = 0; + soft = 0; + intr = 0; + posix = 0; + nocto = 0; + nolock = 0; + noac = 0; + retry = 10000; /* 10000 minutes ~ 1 week */ + tcp = 0; + + mountprog = MOUNTPROG; + mountvers = 0; + port = 0; + mountport = 0; + nfsprog = NFS_PROGRAM; + nfsvers = 0; + + /* parse options */ + + for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) { + if ((opteq = strchr(opt, '='))) { + val = atoi(opteq + 1); + *opteq = '\0'; + if (!strcmp(opt, "rsize")) + data.rsize = val; + else if (!strcmp(opt, "wsize")) + data.wsize = val; + else if (!strcmp(opt, "timeo")) + data.timeo = val; + else if (!strcmp(opt, "retrans")) + data.retrans = val; + else if (!strcmp(opt, "acregmin")) + data.acregmin = val; + else if (!strcmp(opt, "acregmax")) + data.acregmax = val; + else if (!strcmp(opt, "acdirmin")) + data.acdirmin = val; + else if (!strcmp(opt, "acdirmax")) + data.acdirmax = val; + else if (!strcmp(opt, "actimeo")) { + data.acregmin = val; + data.acregmax = val; + data.acdirmin = val; + data.acdirmax = val; + } + else if (!strcmp(opt, "retry")) + retry = val; + else if (!strcmp(opt, "port")) + port = val; + else if (!strcmp(opt, "mountport")) + mountport = val; + else if (!strcmp(opt, "mounthost")) + mounthost=bb_xstrndup(opteq+1, + strcspn(opteq+1," \t\n\r,")); + else if (!strcmp(opt, "mountprog")) + mountprog = val; + else if (!strcmp(opt, "mountvers")) + mountvers = val; + else if (!strcmp(opt, "nfsprog")) + nfsprog = val; + else if (!strcmp(opt, "nfsvers") || + !strcmp(opt, "vers")) + nfsvers = val; + else if (!strcmp(opt, "proto")) { + if (!strncmp(opteq+1, "tcp", 3)) + tcp = 1; + else if (!strncmp(opteq+1, "udp", 3)) + tcp = 0; + else + printf(_("Warning: Unrecognized proto= option.\n")); + } else if (!strcmp(opt, "namlen")) { +#if NFS_MOUNT_VERSION >= 2 + if (nfs_mount_version >= 2) + data.namlen = val; + else +#endif + printf(_("Warning: Option namlen is not supported.\n")); + } else if (!strcmp(opt, "addr")) + /* ignore */; + else { + printf(_("unknown nfs mount parameter: " + "%s=%d\n"), opt, val); + goto fail; + } + } + else { + val = 1; + if (!strncmp(opt, "no", 2)) { + val = 0; + opt += 2; + } + if (!strcmp(opt, "bg")) + bg = val; + else if (!strcmp(opt, "fg")) + bg = !val; + else if (!strcmp(opt, "soft")) + soft = val; + else if (!strcmp(opt, "hard")) + soft = !val; + else if (!strcmp(opt, "intr")) + intr = val; + else if (!strcmp(opt, "posix")) + posix = val; + else if (!strcmp(opt, "cto")) + nocto = !val; + else if (!strcmp(opt, "ac")) + noac = !val; + else if (!strcmp(opt, "tcp")) + tcp = val; + else if (!strcmp(opt, "udp")) + tcp = !val; + else if (!strcmp(opt, "lock")) { + if (nfs_mount_version >= 3) + nolock = !val; + else + printf(_("Warning: option nolock is not supported.\n")); + } else { + printf(_("unknown nfs mount option: " + "%s%s\n"), val ? "" : "no", opt); + goto fail; + } + } + } + proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; + + data.flags = (soft ? NFS_MOUNT_SOFT : 0) + | (intr ? NFS_MOUNT_INTR : 0) + | (posix ? NFS_MOUNT_POSIX : 0) + | (nocto ? NFS_MOUNT_NOCTO : 0) + | (noac ? NFS_MOUNT_NOAC : 0); +#if NFS_MOUNT_VERSION >= 2 + if (nfs_mount_version >= 2) + data.flags |= (tcp ? NFS_MOUNT_TCP : 0); +#endif +#if NFS_MOUNT_VERSION >= 3 + if (nfs_mount_version >= 3) + data.flags |= (nolock ? NFS_MOUNT_NONLM : 0); +#endif + if (nfsvers > MAX_NFSPROT) { + bb_error_msg("NFSv%d not supported!", nfsvers); + return 0; + } + if (mountvers > MAX_NFSPROT) { + bb_error_msg("NFSv%d not supported!", nfsvers); + return 0; + } + if (nfsvers && !mountvers) + mountvers = (nfsvers < 3) ? 1 : nfsvers; + if (nfsvers && nfsvers < mountvers) { + mountvers = nfsvers; + } + + /* Adjust options if none specified */ + if (!data.timeo) + data.timeo = tcp ? 70 : 7; + +#ifdef NFS_MOUNT_DEBUG + printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", + data.rsize, data.wsize, data.timeo, data.retrans); + printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n", + data.acregmin, data.acregmax, data.acdirmin, data.acdirmax); + printf("port = %d, bg = %d, retry = %d, flags = %.8x\n", + port, bg, retry, data.flags); + printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n", + mountprog, mountvers, nfsprog, nfsvers); + printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n", + (data.flags & NFS_MOUNT_SOFT) != 0, + (data.flags & NFS_MOUNT_INTR) != 0, + (data.flags & NFS_MOUNT_POSIX) != 0, + (data.flags & NFS_MOUNT_NOCTO) != 0, + (data.flags & NFS_MOUNT_NOAC) != 0); +#if NFS_MOUNT_VERSION >= 2 + printf("tcp = %d\n", + (data.flags & NFS_MOUNT_TCP) != 0); +#endif +#endif + + data.version = nfs_mount_version; + + if (*flags & MS_REMOUNT) + goto copy_data_and_return; + + /* + * If the previous mount operation on the same host was + * backgrounded, and the "bg" for this mount is also set, + * give up immediately, to avoid the initial timeout. + */ + if (bg && !running_bg && + prev_bg_host && strcmp(hostname, prev_bg_host) == 0) { + if (retry > 0) + retval = EX_BG; + return retval; + } + + /* create mount deamon client */ + /* See if the nfs host = mount host. */ + if (mounthost) { + if (mounthost[0] >= '0' && mounthost[0] <= '9') { + mount_server_addr.sin_family = AF_INET; + mount_server_addr.sin_addr.s_addr = inet_addr(hostname); + } else { + if ((hp = gethostbyname(mounthost)) == NULL) { + bb_herror_msg("%s", mounthost); + goto fail; + } else { + if (hp->h_length > sizeof(struct in_addr)) { + bb_error_msg("got bad hp->h_length?"); + hp->h_length = sizeof(struct in_addr); + } + mount_server_addr.sin_family = AF_INET; + memcpy(&mount_server_addr.sin_addr, + hp->h_addr, hp->h_length); + } + } + } + + /* + * The following loop implements the mount retries. On the first + * call, "running_bg" is 0. When the mount times out, and the + * "bg" option is set, the exit status EX_BG will be returned. + * For a backgrounded mount, there will be a second call by the + * child process with "running_bg" set to 1. + * + * The case where the mount point is not present and the "bg" + * option is set, is treated as a timeout. This is done to + * support nested mounts. + * + * The "retry" count specified by the user is the number of + * minutes to retry before giving up. + * + * Only the first error message will be displayed. + */ + retry_timeout.tv_sec = 3; + retry_timeout.tv_usec = 0; + total_timeout.tv_sec = 20; + total_timeout.tv_usec = 0; + timeout = time(NULL) + 60 * retry; + prevt = 0; + t = 30; + val = 1; + for (;;) { + if (bg && stat(node, &statbuf) == -1) { + if (running_bg) { + sleep(val); /* 1, 2, 4, 8, 16, 30, ... */ + val *= 2; + if (val > 30) + val = 30; + } + } else { + /* be careful not to use too many CPU cycles */ + if (t - prevt < 30) + sleep(30); + + pm_mnt = get_mountport(&mount_server_addr, + mountprog, + mountvers, + proto, + mountport); + + /* contact the mount daemon via TCP */ + mount_server_addr.sin_port = htons(pm_mnt->pm_port); + msock = RPC_ANYSOCK; + + switch (pm_mnt->pm_prot) { + case IPPROTO_UDP: + mclient = clntudp_create(&mount_server_addr, + pm_mnt->pm_prog, + pm_mnt->pm_vers, + retry_timeout, + &msock); + if (mclient) + break; + mount_server_addr.sin_port = htons(pm_mnt->pm_port); + msock = RPC_ANYSOCK; + case IPPROTO_TCP: + mclient = clnttcp_create(&mount_server_addr, + pm_mnt->pm_prog, + pm_mnt->pm_vers, + &msock, 0, 0); + break; + default: + mclient = 0; + } + if (mclient) { + /* try to mount hostname:pathname */ + mclient->cl_auth = authunix_create_default(); + + /* make pointers in xdr_mountres3 NULL so + * that xdr_array allocates memory for us + */ + memset(&status, 0, sizeof(status)); + + if (pm_mnt->pm_vers == 3) + clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, + (xdrproc_t) xdr_dirpath, + (caddr_t) &pathname, + (xdrproc_t) xdr_mountres3, + (caddr_t) &status, + total_timeout); + else + clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, + (xdrproc_t) xdr_dirpath, + (caddr_t) &pathname, + (xdrproc_t) xdr_fhstatus, + (caddr_t) &status, + total_timeout); + + if (clnt_stat == RPC_SUCCESS) + break; /* we're done */ + if (errno != ECONNREFUSED) { + clnt_perror(mclient, "mount"); + goto fail; /* don't retry */ + } + if (!running_bg && prevt == 0) + clnt_perror(mclient, "mount"); + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + mclient = 0; + close(msock); + } else { + if (!running_bg && prevt == 0) + clnt_pcreateerror("mount"); + } + prevt = t; + } + if (!bg) + goto fail; + if (!running_bg) { + prev_bg_host = bb_xstrdup(hostname); + if (retry > 0) + retval = EX_BG; + goto fail; + } + t = time(NULL); + if (t >= timeout) + goto fail; + } + nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; + + if (nfsvers == 2) { + if (status.nfsv2.fhs_status != 0) { + bb_error_msg("%s:%s failed, reason given by server: %s", + hostname, pathname, + nfs_strerror(status.nfsv2.fhs_status)); + goto fail; + } + memcpy(data.root.data, + (char *) status.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); +#if NFS_MOUNT_VERSION >= 4 + data.root.size = NFS_FHSIZE; + memcpy(data.old_root.data, + (char *) status.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); +#endif + } else { +#if NFS_MOUNT_VERSION >= 4 + fhandle3 *my_fhandle; + if (status.nfsv3.fhs_status != 0) { + bb_error_msg("%s:%s failed, reason given by server: %s", + hostname, pathname, + nfs_strerror(status.nfsv3.fhs_status)); + goto fail; + } + my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle; + memset(data.old_root.data, 0, NFS_FHSIZE); + memset(&data.root, 0, sizeof(data.root)); + data.root.size = my_fhandle->fhandle3_len; + memcpy(data.root.data, + (char *) my_fhandle->fhandle3_val, + my_fhandle->fhandle3_len); + + data.flags |= NFS_MOUNT_VER3; +#endif + } + + /* create nfs socket for kernel */ + + if (tcp) { + if (nfs_mount_version < 3) { + printf(_("NFS over TCP is not supported.\n")); + goto fail; + } + fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + } else + fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fsock < 0) { + perror(_("nfs socket")); + goto fail; + } + if (bindresvport(fsock, 0) < 0) { + perror(_("nfs bindresvport")); + goto fail; + } + if (port == 0) { + server_addr.sin_port = PMAPPORT; + port = pmap_getport(&server_addr, nfsprog, nfsvers, + tcp ? IPPROTO_TCP : IPPROTO_UDP); + if (port == 0) + port = NFS_PORT; +#ifdef NFS_MOUNT_DEBUG + else + printf(_("used portmapper to find NFS port\n")); +#endif + } +#ifdef NFS_MOUNT_DEBUG + printf(_("using port %d for nfs deamon\n"), port); +#endif + server_addr.sin_port = htons(port); + /* + * connect() the socket for kernels 1.3.10 and below only, + * to avoid problems with multihomed hosts. + * --Swen + */ + if (get_kernel_revision() <= 66314 + && connect(fsock, (struct sockaddr *) &server_addr, + sizeof (server_addr)) < 0) { + perror(_("nfs connect")); + goto fail; + } + + /* prepare data structure for kernel */ + + data.fd = fsock; + memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); + strncpy(data.hostname, hostname, sizeof(data.hostname)); + + /* clean up */ + + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + close(msock); +copy_data_and_return: + *mount_opts = xrealloc(*mount_opts, sizeof(data)); + memcpy(*mount_opts, &data, sizeof(data)); + return 0; + + /* abort */ + +fail: + if (msock != -1) { + if (mclient) { + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + } + close(msock); + } + if (fsock != -1) + close(fsock); + return retval; +} + +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + * + * Andreas Schwab : 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, (unsigned int *) &objp->fhandle3_len, FHSIZE3)) + return FALSE; + return TRUE; +} + +bool_t +xdr_mountres3_ok (XDR *xdrs, mountres3_ok *objp) +{ + //register int32_t *buf; + + if (!xdr_fhandle3 (xdrs, &objp->fhandle)) + return FALSE; + if (!xdr_array (xdrs, (char **)&objp->auth_flavours.auth_flavours_val, (unsigned int *) &objp->auth_flavours.auth_flavours_len, ~0, + sizeof (int), (xdrproc_t) xdr_int)) + return FALSE; + return TRUE; +} + +bool_t +xdr_mountstat3 (XDR *xdrs, mountstat3 *objp) +{ + //register int32_t *buf; + + if (!xdr_enum (xdrs, (enum_t *) objp)) + return FALSE; + return TRUE; +} + +bool_t +xdr_mountres3 (XDR *xdrs, mountres3 *objp) +{ + //register int32_t *buf; + + if (!xdr_mountstat3 (xdrs, &objp->fhs_status)) + return FALSE; + switch (objp->fhs_status) { + case MNT_OK: + if (!xdr_mountres3_ok (xdrs, &objp->mountres3_u.mountinfo)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + diff --git a/busybox/util-linux/nfsmount.h b/busybox/util-linux/nfsmount.h new file mode 100644 index 000000000..78a1bdfc5 --- /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 { + unsigned int fhandle3_len; + char *fhandle3_val; +} fhandle3; + +enum mountstat3 { + MNT_OK = 0, + MNT3ERR_PERM = 1, + MNT3ERR_NOENT = 2, + MNT3ERR_IO = 5, + MNT3ERR_ACCES = 13, + MNT3ERR_NOTDIR = 20, + MNT3ERR_INVAL = 22, + MNT3ERR_NAMETOOLONG = 63, + MNT3ERR_NOTSUPP = 10004, + MNT3ERR_SERVERFAULT = 10006, +}; +typedef enum mountstat3 mountstat3; + +struct fhstatus { + unsigned int fhs_status; + union { + fhandle fhs_fhandle; + } fhstatus_u; +}; +typedef struct fhstatus fhstatus; + +struct mountres3_ok { + fhandle3 fhandle; + struct { + unsigned int auth_flavours_len; + int *auth_flavours_val; + } auth_flavours; +}; +typedef struct mountres3_ok mountres3_ok; + +struct mountres3 { + mountstat3 fhs_status; + union { + mountres3_ok mountinfo; + } mountres3_u; +}; +typedef struct mountres3 mountres3; + +typedef char *dirpath; + +typedef char *name; + +typedef struct mountbody *mountlist; + +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; +typedef struct mountbody mountbody; + +typedef struct groupnode *groups; + +struct groupnode { + name gr_name; + groups gr_next; +}; +typedef struct groupnode groupnode; + +typedef struct exportnode *exports; + +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; +typedef struct exportnode exportnode; + +struct ppathcnf { + int pc_link_max; + short pc_max_canon; + short pc_max_input; + short pc_name_max; + short pc_path_max; + short pc_pipe_buf; + u_char pc_vdisable; + char pc_xxx; + short pc_mask[2]; +}; +typedef struct ppathcnf ppathcnf; +#endif /*!_rpcsvc_mount_h*/ + +#define MOUNTPROG 100005 +#define MOUNTVERS 1 + +#define MOUNTPROC_NULL 0 +extern void * mountproc_null_1(void *, CLIENT *); +extern void * mountproc_null_1_svc(void *, struct svc_req *); +#define MOUNTPROC_MNT 1 +extern fhstatus * mountproc_mnt_1(dirpath *, CLIENT *); +extern fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_DUMP 2 +extern mountlist * mountproc_dump_1(void *, CLIENT *); +extern mountlist * mountproc_dump_1_svc(void *, struct svc_req *); +#define MOUNTPROC_UMNT 3 +extern void * mountproc_umnt_1(dirpath *, CLIENT *); +extern void * mountproc_umnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_UMNTALL 4 +extern void * mountproc_umntall_1(void *, CLIENT *); +extern void * mountproc_umntall_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORT 5 +extern exports * mountproc_export_1(void *, CLIENT *); +extern exports * mountproc_export_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORTALL 6 +extern exports * mountproc_exportall_1(void *, CLIENT *); +extern exports * mountproc_exportall_1_svc(void *, struct svc_req *); +extern int mountprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +#define MOUNTVERS_POSIX 2 + +extern void * mountproc_null_2(void *, CLIENT *); +extern void * mountproc_null_2_svc(void *, struct svc_req *); +extern fhstatus * mountproc_mnt_2(dirpath *, CLIENT *); +extern fhstatus * mountproc_mnt_2_svc(dirpath *, struct svc_req *); +extern mountlist * mountproc_dump_2(void *, CLIENT *); +extern mountlist * mountproc_dump_2_svc(void *, struct svc_req *); +extern void * mountproc_umnt_2(dirpath *, CLIENT *); +extern void * mountproc_umnt_2_svc(dirpath *, struct svc_req *); +extern void * mountproc_umntall_2(void *, CLIENT *); +extern void * mountproc_umntall_2_svc(void *, struct svc_req *); +extern exports * mountproc_export_2(void *, CLIENT *); +extern exports * mountproc_export_2_svc(void *, struct svc_req *); +extern exports * mountproc_exportall_2(void *, CLIENT *); +extern exports * mountproc_exportall_2_svc(void *, struct svc_req *); +#define MOUNTPROC_PATHCONF 7 +extern ppathcnf * mountproc_pathconf_2(dirpath *, CLIENT *); +extern ppathcnf * mountproc_pathconf_2_svc(dirpath *, struct svc_req *); +extern int mountprog_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +#define MOUNT_V3 3 + +#define MOUNTPROC3_NULL 0 +extern void * mountproc3_null_3(void *, CLIENT *); +extern void * mountproc3_null_3_svc(void *, struct svc_req *); +#define MOUNTPROC3_MNT 1 +extern mountres3 * mountproc3_mnt_3(dirpath *, CLIENT *); +extern mountres3 * mountproc3_mnt_3_svc(dirpath *, struct svc_req *); +#define MOUNTPROC3_DUMP 2 +extern mountlist * mountproc3_dump_3(void *, CLIENT *); +extern mountlist * mountproc3_dump_3_svc(void *, struct svc_req *); +#define MOUNTPROC3_UMNT 3 +extern void * mountproc3_umnt_3(dirpath *, CLIENT *); +extern void * mountproc3_umnt_3_svc(dirpath *, struct svc_req *); +#define MOUNTPROC3_UMNTALL 4 +extern void * mountproc3_umntall_3(void *, CLIENT *); +extern void * mountproc3_umntall_3_svc(void *, struct svc_req *); +#define MOUNTPROC3_EXPORT 5 +extern exports * mountproc3_export_3(void *, CLIENT *); +extern exports * mountproc3_export_3_svc(void *, struct svc_req *); +extern int mountprog_3_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +/* the xdr functions */ + +static bool_t xdr_fhandle (XDR *, fhandle); +extern bool_t xdr_fhandle3 (XDR *, fhandle3*); +extern bool_t xdr_mountstat3 (XDR *, mountstat3*); +extern bool_t xdr_fhstatus (XDR *, fhstatus*); +extern bool_t xdr_mountres3_ok (XDR *, mountres3_ok*); +extern bool_t xdr_mountres3 (XDR *, mountres3*); +extern bool_t xdr_dirpath (XDR *, dirpath*); +extern bool_t xdr_name (XDR *, name*); +extern bool_t xdr_mountlist (XDR *, mountlist*); +extern bool_t xdr_mountbody (XDR *, mountbody*); +extern bool_t xdr_groups (XDR *, groups*); +extern bool_t xdr_groupnode (XDR *, groupnode*); +extern bool_t xdr_exports (XDR *, exports*); +extern bool_t xdr_exportnode (XDR *, exportnode*); +extern bool_t xdr_ppathcnf (XDR *, ppathcnf*); + +#ifdef __cplusplus +} +#endif + +#endif /* !_NFSMOUNT_H_RPCGEN */ diff --git a/busybox/util-linux/pivot_root.c b/busybox/util-linux/pivot_root.c new file mode 100644 index 000000000..85e180c46 --- /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) + bb_show_usage(); + + if (pivot_root(argv[1],argv[2]) < 0) + bb_perror_msg_and_die("pivot_root"); + + return EXIT_SUCCESS; + +} + + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ diff --git a/busybox/util-linux/rdate.c b/busybox/util-linux/rdate.c new file mode 100644 index 000000000..a73e8eebf --- /dev/null +++ b/busybox/util-linux/rdate.c @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +/* + * The Rdate command will ask a time server for the RFC 868 time + * and optionally set the system time. + * + * by Sterling Huxley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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" + + +static const int RFC_868_BIAS = 2208988800UL; + +static void socket_timeout(int sig) +{ + bb_error_msg_and_die("timeout connecting to time server"); +} + +static time_t askremotedate(const char *host) +{ + unsigned long int nett, localt; + struct sockaddr_in s_in; + int fd; + + bb_lookup_host(&s_in, host); + s_in.sin_port = bb_lookup_port("time", "tcp", 37); + + /* Add a timeout for dead or non accessable servers */ + alarm(10); + signal(SIGALRM, socket_timeout); + + fd = xconnect(&s_in); + + if (safe_read(fd, (void *)&nett, 4) != 4) /* read time from server */ + bb_error_msg_and_die("%s did not send the complete time", host); + + close(fd); + + /* convert from network byte order to local byte order. + * RFC 868 time is the number of seconds + * since 00:00 (midnight) 1 January 1900 GMT + * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT + * Subtract the RFC 868 time to get Linux epoch + */ + localt= ntohl(nett) - RFC_868_BIAS; + + return(localt); +} + +int rdate_main(int argc, char **argv) +{ + time_t remote_time; + int opt; + int setdate = 1; + int printdate = 1; + + /* Interpret command line args */ + while ((opt = getopt(argc, argv, "sp")) > 0) { + switch (opt) { + case 's': + printdate = 0; + setdate = 1; + break; + case 'p': + printdate = 1; + setdate = 0; + break; + default: + bb_show_usage(); + } + } + + if (optind == argc) + bb_show_usage(); + + remote_time = askremotedate(argv[optind]); + + if (setdate) { + time_t current_time; + + time(¤t_time); + if (current_time == remote_time) + bb_error_msg("Current time matches remote time."); + else + if (stime(&remote_time) < 0) + bb_perror_msg_and_die("Could not set time of day"); + } + + if (printdate) + printf("%s", ctime(&remote_time)); + + return EXIT_SUCCESS; +} diff --git a/busybox/util-linux/swaponoff.c b/busybox/util-linux/swaponoff.c new file mode 100644 index 000000000..7c7031bce --- /dev/null +++ b/busybox/util-linux/swaponoff.c @@ -0,0 +1,120 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini swapon/swapoff implementation for busybox + * + * Copyright (C) 1999-2004 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" + +static int whichApp; /* default SWAPON_APP */ + +static const int SWAPON_APP = 0; +static const int SWAPOFF_APP = 1; + + +static int swap_enable_disable(const char *device) +{ + int status; + struct stat st; + + if (stat(device, &st) < 0) { + bb_perror_msg_and_die("cannot stat %s", device); + } + + /* test for holes */ + if (S_ISREG(st.st_mode)) { + if (st.st_blocks * 512 < st.st_size) { + bb_error_msg_and_die("swap file has holes"); + } + } + + if (whichApp == SWAPON_APP) + status = swapon(device, 0); + else + status = swapoff(device); + + if (status != 0) { + bb_perror_msg("%s", device); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static int do_em_all(void) +{ + struct mntent *m; + FILE *f = setmntent("/etc/fstab", "r"); + int err = 0; + + if (f == NULL) + bb_perror_msg_and_die("/etc/fstab"); + while ((m = getmntent(f)) != NULL) { + if (strcmp(m->mnt_type, MNTTYPE_SWAP)==0) { + if(swap_enable_disable(m->mnt_fsname) == EXIT_FAILURE) + err++; + } + } + endmntent(f); + return err; +} + + +extern int swap_on_off_main(int argc, char **argv) +{ + if (bb_applet_name[5] == 'f') { /* "swapoff" */ + whichApp = SWAPOFF_APP; + } + + if (argc != 2) { + goto usage_and_exit; + } + argc--; + argv++; + + /* Parse any options */ + while (**argv == '-') { + while (*++(*argv)) + switch (**argv) { + case 'a': + { + struct stat statBuf; + + if (stat("/etc/fstab", &statBuf) < 0) + bb_error_msg_and_die("/etc/fstab file missing"); + } + return do_em_all(); + break; + default: + goto usage_and_exit; + } + } + return swap_enable_disable(*argv); + + usage_and_exit: + bb_show_usage(); +} diff --git a/busybox/util-linux/umount.c b/busybox/util-linux/umount.c new file mode 100644 index 000000000..21c2e6e4d --- /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-2004 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 CONFIG_FEATURE_MOUNT_FORCE +static int doForce = FALSE; +#endif +#if defined CONFIG_FEATURE_MOUNT_LOOP +static int freeLoop = TRUE; +#endif +#if defined CONFIG_FEATURE_MTAB_SUPPORT +static int useMtab = TRUE; +#endif +static int umountAll = FALSE; +static int doRemount = FALSE; + + + +/* These functions are here because the getmntent functions do not appear + * to be re-entrant, which leads to all sorts of problems when we try to + * use them recursively - randolph + * + * TODO: Perhaps switch to using Glibc's getmntent_r + * -Erik + */ +static void mtab_read(void) +{ + struct _mtab_entry_t *entry = NULL; + struct mntent *e; + FILE *fp; + + if (mtab_cache != NULL) + return; + + if ((fp = setmntent(bb_path_mtab_file, "r")) == NULL) { + bb_error_msg("Cannot open %s", bb_path_mtab_file); + return; + } + while ((e = getmntent(fp))) { + entry = xmalloc(sizeof(struct _mtab_entry_t)); + entry->device = strdup(e->mnt_fsname); + entry->mountpt = strdup(e->mnt_dir); + entry->next = mtab_cache; + mtab_cache = entry; + } + endmntent(fp); +} + +static char *mtab_getinfo(const char *match, const char which) +{ + struct _mtab_entry_t *cur = mtab_cache; + + while (cur) { + if (strcmp(cur->mountpt, match) == 0 || + strcmp(cur->device, match) == 0) { + if (which == MTAB_GETMOUNTPT) { + return cur->mountpt; + } else { +#if !defined CONFIG_FEATURE_MTAB_SUPPORT + if (strcmp(cur->device, "rootfs") == 0) { + continue; + } else if (strcmp(cur->device, "/dev/root") == 0) { + /* Adjusts device to be the real root device, + * or leaves device alone if it can't find it */ + cur->device = find_real_root_device_name(); + } +#endif + return cur->device; + } + } + cur = cur->next; + } + return NULL; +} + +static char *mtab_next(void **iter) +{ + char *mp; + + if (iter == NULL || *iter == NULL) + return NULL; + mp = ((struct _mtab_entry_t *) (*iter))->mountpt; + *iter = (void *) ((struct _mtab_entry_t *) (*iter))->next; + return mp; +} + +static char *mtab_first(void **iter) +{ + struct _mtab_entry_t *mtab_iter; + + if (!iter) + return NULL; + mtab_iter = mtab_cache; + *iter = (void *) mtab_iter; + return mtab_next(iter); +} + +/* Don't bother to clean up, since exit() does that + * automagically, so we can save a few bytes */ +#ifdef CONFIG_FEATURE_CLEAN_UP +static void mtab_free(void) +{ + struct _mtab_entry_t *this, *next; + + this = mtab_cache; + while (this) { + next = this->next; + free(this->device); + free(this->mountpt); + free(this); + this = next; + } +} +#endif + +static int do_umount(const char *name) +{ + int status; + char *blockDevice = mtab_getinfo(name, MTAB_GETDEVICE); + + if (blockDevice && strcmp(blockDevice, name) == 0) + name = mtab_getinfo(blockDevice, MTAB_GETMOUNTPT); + + status = umount(name); + +#if defined CONFIG_FEATURE_MOUNT_LOOP + if (freeLoop && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9)) + /* this was a loop device, delete it */ + del_loop(blockDevice); +#endif +#if defined CONFIG_FEATURE_MOUNT_FORCE + if (status != 0 && doForce) { + status = umount2(blockDevice, MNT_FORCE); + if (status != 0) { + bb_error_msg_and_die("forced umount of %s failed!", blockDevice); + } + } +#endif + if (status != 0 && doRemount && errno == EBUSY) { + status = mount(blockDevice, name, NULL, + MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); + if (status == 0) { + bb_error_msg("%s busy - remounted read-only", blockDevice); + } else { + bb_error_msg("Cannot remount %s read-only", blockDevice); + } + } + if (status == 0) { +#if defined CONFIG_FEATURE_MTAB_SUPPORT + if (useMtab) + erase_mtab(name); +#endif + return (TRUE); + } + return (FALSE); +} + +static int umount_all(void) +{ + int status = TRUE; + char *mountpt; + void *iter; + + for (mountpt = mtab_first(&iter); mountpt; mountpt = mtab_next(&iter)) { + /* Never umount /proc on a umount -a */ + if (strstr(mountpt, "proc")!= NULL) + continue; + if (!do_umount(mountpt)) { + /* Don't bother retrying the umount on busy devices */ + if (errno == EBUSY) { + bb_perror_msg("%s", mountpt); + status = FALSE; + continue; + } + if (!do_umount(mountpt)) { + printf("Couldn't umount %s on %s: %s\n", + mountpt, mtab_getinfo(mountpt, MTAB_GETDEVICE), + strerror(errno)); + status = FALSE; + } + } + } + return (status); +} + +extern int umount_main(int argc, char **argv) +{ + char path[PATH_MAX], result = 0; + + if (argc < 2) { + bb_show_usage(); + } +#ifdef CONFIG_FEATURE_CLEAN_UP + atexit(mtab_free); +#endif + + /* Parse any options */ + while (--argc > 0 && **(++argv) == '-') { + while (*++(*argv)) + switch (**argv) { + case 'a': + umountAll = TRUE; + break; +#if defined CONFIG_FEATURE_MOUNT_LOOP + case 'l': + freeLoop = FALSE; + break; +#endif +#ifdef CONFIG_FEATURE_MTAB_SUPPORT + case 'n': + useMtab = FALSE; + break; +#endif +#ifdef CONFIG_FEATURE_MOUNT_FORCE + case 'f': + doForce = TRUE; + break; +#endif + case 'r': + doRemount = TRUE; + break; + case 'v': + break; /* ignore -v */ + default: + bb_show_usage(); + } + } + + mtab_read(); + if (umountAll) { + if (umount_all()) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } + + do { + if (realpath(*argv, path) != NULL) + if (do_umount(path)) + continue; + bb_perror_msg("%s", path); + result++; + } while (--argc > 0 && ++argv); + return result; +} -- 2.25.1
+
+ + + + +
BUSYBOX
+
+ + BusyBox
+
+
About +
Screenshot +
Mailing Lists +
Latest News +
Download +
FAQ +
Accessing CVS +
Browse CVS +
Documentation +
Products +
Hall of Shame +
License + +

Related Sites +
uClibc.org +
udhcp +
tinylogin +
uCdot +
LinuxDevices +
Slashdot +
Freshmeat +
Linux Today +
Linux Weekly News +
Linux HOWTOs + + + +

+ diff --git a/busybox/docs/busybox.net/images/back.png b/busybox/docs/busybox.net/images/back.png new file mode 100644 index 0000000000000000000000000000000000000000..79923869bf32cfee14ad6a1d8abaee3e9f231cab GIT binary patch literal 322 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz0!VDyh@)w!{DWL$L5Z5#RL15<0nQ3Wh#>U1# z;af%-tU!vfB*-tA!Qt7BG$2Q;#5JNMI6tkVJh3R1Aw4tAs30$0!AQ?U&%if8O$n$X z45Y$2KQ}iuuY@5aBePf`v8Y4=NM+_Jlw_nT6qF|AWF{-5LrY6jpBG<_0Bwl!ba4!+n3HjE z56cCG6n=Xv2X~DkJ%zLXR^K>M;kal0W7Rn)%w^Z`N`!n{>X2Ea;q~=r!R9*>w_je> zKYM2N$we!&X130ZtPorq%zyI!&fmNCsg_AjjnA7eB(KAlGItix76wmOKbLh*2~7ZK CHF7rq literal 0 HcmV?d00001 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/busybox.png b/busybox/docs/busybox.net/images/busybox.png new file mode 100644 index 0000000000000000000000000000000000000000..b1eb92f3811c3fc8a029248bf95f61ad8a53d67c GIT binary patch literal 34014 zcmV)TK(W7xP)Icc5TG{{```OfpcoM*I`UNnQ~2le&dp2MMO0$kY-1Thr^_h zqlbFSlYXXNKP-}n#(;wDtblWCT3LsIa#chvAk3(YesaOPl5S2iC4F;g zzL$4{h4tREj);u*YdtH|!N;a@Q~37yi*?DQc2k#fjjMHA(b@TZZnd_a#$z-bo^nxp zR7Hi_@>>j8p`4O##I00(qQO+^RN0R#mHB_pmW2mk;e07*naRCt{1 zy$e9qWw!obJ4#d0lu}251T@$UN*)#S!k}SkCb7klsT6Vm>7s*zf;c-xAq!2z6fzYE z2T7Czh@#+SFeC)EgaoA+1{qLEydZ`!gxLRi*8AN^P17`!=bSm`|9kh|-+cj1K0Ir! zXRY_UclX@$|JMIo9jjN59*>-UWarY^3mum(?eRbTule7&diD7F^z5BW6B84cW@ctq zR%T}QHS50cfBIkbziRb*WcJdfOC1wE0C7ZOqDN+?Ri;&NwJzAz%<;$m+}FMSyZ(j$ z<_j@Ad#7WDqob9Fhlit;l~phR@-EoQ)it=fT36Yn@{u3;^C0grV(ICx|J(k-|9-1i zFEU)dv~C1~*sL-$N&u!?r3a_$bbJV+1XEHc3dd&9l=-$11rAMOU>F53%|Ly-C zt5=WbM23!tVGk=05CpN>SAVk`o(nK2-0W;q-MIx#J?B5J!U&*Ms?|aqVhzS zeu;@Y|NkS)UOgW98U%N$T$t%$Wi3FJ@UvxAROW02!5F!9#)bGM+93L42EDm zR=VggJ%;zjbOD*};bBa_Xl7=Xc>0I`($h;Ft?=b8-Mg5Xb?;KyrTatO`#H}3KWdjK z!qYoj+;2xJe|4x<&KRv)a_UBpGF3pPN9dV^kX z)aV&wVmxs0z%eRj+{Gxoq#vg>6Fs^Bq!}n`k9 zFO#M)JrikB7hEkrKS)B8$cH+%&MHF!mfTb;jc>Xr&(ec+3b5Q*U`*LkXUwEsf+91` ze!%}MbWE(I8^06SDN2#*$=RsQzN|cz{RHLzd<_S~r>Q`o6FbBBZ8K!(B?pcm3q5NB z@ad}|FP&arT}?Sj07Iw24CY`NMX=sbeB`PLQiK|uu0?yW2_kXEd(8z1v5N65Xz_|#x|=A zBf=NeB$%TvU}Pj$SJ&yO!fL$^WJQoLKEc8w*ci;%#&;{SDA_RMQn~dk&QvH1x?D{e zRFBs()jdJ}t&yC))C!S}N@I4XX#0K-=m~)dj3C;t-)n!T(fqF>C_Nq1pd%dNhz!Rz z#|$gu2+$j0Wi)!wR>SZG>y3ImBrt#!A`~R$NkYNmXAr=_IwL%|72P9pWiW|RpjJYX ze@HKf;B>9aju9zJ;?%_iQ^J)>k>8(^_gd{3YO~*O>yF`G8d$B)gk^-$8H%^oMZ4|?tN6aTdgpOda`>2k-V%L^Qb4+lcW;cbp>!`lR-F@ske z@Jt_rAF`SKsFb z;!dn4N*1#&$~zUQtL$Rt{M~ZWy?HO&3g* zXaMOA$#{5Z2Tk=oug^qts?n#0z32ajT|MU9Ey$ zK@qt#nDtncDJ5Q~6IU)32MVPkFEX}eL~d4T_I)Ec+tG^q6N+RPU<6Ot#}janLQjcu zvWX->LYRB!O+NccKaZCaJreIi2}c}5g{Nn~>?mu*8{jy6X+c3oLBa5X3`nn_zP=42 z%xD{4(8h;&l3@yQO_lhejd}xE@+QSWr)TjHs(2!1QA!0uo4O5Fyn{1?GeIviI3!c~ zCcVB+lPvx5qeK_Ki#Gh5fdz{|M#m3lCf*6Whiu?NEZIE9<0a>;c zAgqxb3wU5|q$VXxc3u*d27#c{gCM7j=`!h7By}t*$aAhb71`+|$(O-Wr07cA)zt)7 zov!g`^3_XHg@93J($ke8H{&F2cIVh$=x8P8hl}8lDx&}rEg@R%%_q)IKI=RAtWu<} zZ;h|-Uf;cp`MrD3&YSniiOKUueE9Mci68yF!TXOdP8J*!6Z<75CW;hMQHZi0Hv$|! z94Z{nn1&+p7zgG^d_z6&>W!U3n+eir2w^3rDyt1ratt9x^3aKy#9g&Mgs%e|wdM+w zRLyN_UW0>~FbOo>m0xmY=|-w_ zI4a3-4Iv@o5ia!w!;K(VfD1U**T)QxG1ePz;sKs>UdW*$5E>1NWw4kWBV}o%9ipu? z=!IrF;hFGJh&mJ$+&C9wX{ipDNOmPm$S<8=VGYNPGOu>!w3eRktaIlsP5J7vuip6Zwcq~}9D6xF)V+K6 zewCFTi5?jv95XUTkl}D)s5mOB4EEvV2*7POjl+$&*0;rwB#k(CS*#2OktMv38cl#g zdazV&J#>ZwOgYlpxWXD%2P;KVoj~J?N3PYvQcrSt4u$GmDM~$GB4PX;!D1kpH}(8O z0@7+)$B*;o>=7IZNrp5t1I2NiREQo~Z4lbNN|F>MC{9ihMFM9{3Z9uro_poxDG$8- zGOj<_TRxFUkd>KvS3ss47@pDL-AcK%*-4)SD($zAUu$a%IOJtg2PDDnZ0ekez0` zkDz3CQG!Hz+zgK6P_m;?ZXiq0+pHLB(T#PMiUdaA$>doz8BxCX*-KTU_g;GAtKUEU z+!L?8_7hp2Ui#}_|GIl6dSw{Jf)4!^6r8*xNcDP#fXx^#I5UJGqFO1^h!(L;uk=Wq z)%;p=tw7xw=1u@km~A?kPzZS6^1BXU4<#WN7PuY#l47@`G5fr zmhvSrl32?-ew^8fR=O^#sbPya&P)x@s7P5Ia+DBfY)i3`3SLD?PNpE=zjWymZm+%k z)i=*Qk@&?=GDMFoedt%e{&l8DU4aVdH&j-v?<7d$a4FguR3xtqY9n(bAOd4O5wg9O zO+UHnr7Nk^hv<1l!3lv;qyR&h7eURH=s?j`A{bX!tZGSr_!^(M>QpI`*Of}EsZ)_C zD-id-@Q8w}>==^wnC&4IyNhbInInD2L6IOCMSgNPYoZC9VuNZF&ApSQ?Cia?_x)Ec zy?<`+rE~MDK79H2U-o+7xu4L7f8Dq8*T4CX%}bXrC&-%&?2`p2ObhXpz>}blDYd>f&2vT8GB*#IM5M&e<(j-_;RwPB3leOZlt~Dq&Xc$?& znIq3t?S22!-e=$VaPNm-eY54uK_Bh>M=j6SOS=Q4SwX>aa1zHeoF)fMS)Fk)zA+p@ z4{OPK&hTcItY@{U7l`%gjA+Dm2gVR(zN__hA^JK)T^*v@iV*RI5CnHHg-G=UYl?JL zg;fDFVUfSF5qtjn~^Phvh{>6`z?3?)O-~6h(aXBOjJ=UMRd6L-? zPKWx0!4WOqG~N_%e$$wN8Ur`3Ql!2f-0}>0dF%?U^cqTI2W%0Lf6K+7wVCi#f|M?=gO(;$%Y=sjRV6d$}#G!Bua)h%J^n+L0WsG8ppl9^JIu2iaduSh+f)z?h=l#ef8z< zcYgT9kD`2pARmuHPAs@t{{}rGV%wA))s^NFv3wI*vYzvbdMPui+Ty z*yzDcEv*HVzPma^BG^?>RhzI?yJ`?6xFx2w=juiEM+7>}dayb&(wRyjNmq4#)a(oo zlZT`Q8aEDCg4_%_lIB@pM1!dE{tIjYA}J2Vwav5~08BRetYI;rZsceP@*QLY|I5{e9# zhK@~5rddZ|M^TjH$W2w7&GbHEe}rljYmPLFN{{noA~a8EDu5)3=thj=ZtuA_zWU~L z;-DY#LOc8Y2C*H3aQ=ju5!^aJX0YXMlU_&kmqESO8}m5yVN=bJ#?6q2I@4hxS!L>o zFeHSvCYWZNvCZ-f_YMOySu55kqy9sfX=0=@K zHJc~&R!XI*M0Xf9PL}Vuvyvkb+M88sq%=>QI58RKoORpR#Bwq^MBe6!2ci-s!uJ9s zGG*1Ec_|Nkc;CvuU%($p{1sx`1JR2r0!7x>-!%E2n^JCY&lJfPv0gJqhj{w5G)jiTbN}X)&&2{sTEX%?e3)^L6@9=17X( zHN?&iK`j2K$B4{KE1f1tn9FfyfX5`c*<>QYP($g#hpKj~&;~&nSk9A~+FryrljNoM z-#_<06MVWaIK@#b*MNyQhay>Wm?zJk5K&T(G(fobtzxD;r&bL) zRw2CiKKsd~uYPQV?)d9p{p#@(M&N?;&iIaXG!EC4INTVc$r1kBpht~?5&XBZLIA&g~6oyKVm7C2}WN!vY^ofKDj!{vQ&u;eE%s8JM#i1H_PDY?B zmRfYU;payE*>Ka2-5L;M*OC@?|N0GV}xXV zo@%P=c~qgTDY9BGdjoY$gsd+55Q#u>k**Tnd_|MoK$7fSoOO3zuoKeS>vlR7+5?$Y z)$bvbf4%(OuBh5Nv&@AH<;^!{W||4Vtl@Vtw8 z^G^8A)AFP$-RJsMi6pN|2f54+^ta_h0?y@h;=q+AXN$ZJJzPe zlp;f_LlDLgSBD@)of^hY6;@Jf&-OG>D{fp)IA$CH6flSDbtPGd)S3Z%OKPVU4L>CM$# z2rmz0*|rMSUDPm|jWa{Vz8IwMQV%6T7nyMPC(EvFJHga@T)yE%AdEkjZ`~k`E zp(RW1T~a*i;one|5B$Ld@{z=c9)GC2v0!-tFghA#!lMRfMghrz8Mj^~J&Ga~Nm@oW zga+K<5Mn@U$g&&(^B62eH3UB_Q(3T*q-tTPKs$yvQA&6V)PZ(7!N>>N#T4TmBni__ zbwkuSb?-_i*~LT=7i&Ug3vZFLtes&yXNrb-aY!KEol4u%FZ0>g!ay)@X zv1(pTO3h?)gdFh@@`NL=`smVwDPOEi83bZa+_&T{kepMzf8>2D4^%y2YQY^Jdg$@) zRt3wK8+krOy;5Yo)R^HUNH^YQC;-GZqke=z7LsHg3O1@tohVYwBm0;TgR`@<3Ss=X zC3Pl6iXfp#!Dws#ZV$eGes8sm`-xw7GkT*wggmyDen!(|k zdo*$c$q_C=vS90FPC+xt=&sJItBWx3hvFWW!I>yIgAA5=4g5}tw16RYasx@=88~pD z9j|tZT#21ZhrFY|tn3h?pj0FWno@xptwD?tbs;hsQUFcnr}83XiTJK0$ctLhrXhkg zrxl_cH*@>J*EEoXrG%e6Ax4tJiz?rmy|`4#jX822E>%?@Rjou?eBebA`{hIAxMWTC zvXK{Zq0)~YcupCh#~<$!Wwo3;gv^E_Nw(P>Ot!I^;mxSk8bb!aaZ!zcSSNzSbG9(S z!Ok63MTs)R*)GJ64DFmloQcy}h}fy@Xh$hhnRceE2y%*^-9Vjsq6Ky}z>9_@sg^*L z&is$aA2CystVCV9cv0T;gy2ShGY-0pQq@}y=x(?_E25OR22B!V)rK0;Z2P9L@t)^f zGdTrmlDSg!h%`AmW#z-8KDtL`#3v6F-}_c^F*LcqWn}KkhX%dIot<8ZzkYm>nMa#r zTS10IcAnBBvSNXRcAK<@M!f6c_LVNW#QVE>%V; zkwIKC@Qxz2GCi=8@r-*@Y>OZzeGX*x>hPK@TzR@naq+1Z>`!$qU2<9 zGf}E!$;$~bW^;tLmksxzmAQjbIx6&Y53$}9uPNTY|LCIJ z<0}V!2$H>CL$&FW-qt1qT-npAmy;@RK@XFcmsfz0Hs-aVuVJ|f$#8B%{F02WB5g$yY%L1c0?rNV)BcNR#B5Ji#N5(!g`0|%B26xWG^ z!su-0Wv6C9@IaNiOi(07BuX>Zn~QhK4S>obNmg!O)2?p%%X zk7|uhNjdOZr@`(o4~)9^ttG|#i;MRkMR>0q^xA1C@}b8c?;gcde7JE_f3rSDwruKA zUqp?0^?5vxf*BH$L>Sk}oouXVw-D$m)Vo zPwEuP5NCsgGZo2rmL)$h*=hJf z{CAw?4^#qUCFLlZlw1jnmFegll?md>E27D>auQ3FFMT-5wjwE;vhX~VW7RgY+w<{? z6)XCrew=Fi@y8!uv#ok?8}(u+JRvv$LA!8#=BCoBm`-S zZ8H`dF!lA_%r`HIAWf%A*2oDf%IUn)XUYp}KRmAy6Z@4fWO zvr}GJ{rR`w9y;{))vMdL@9}i;FLH2*PhK?s!5^58KQQXvd*6EN`t|EJHZ~)VgCrIC zxGrybLAq3HDK;RgC(1S>cU%kd&=b~^Ww1!n8Pyw?Y6J5louhMv6dSu>I|Y&w#LpfA zi{q@K+7-l8Y-`GW$!DibdEs+FeEZPbhpuivwP}-|e|c@7 zk5BUP~-y*&Dk_Fe zO;l8+cJ3t4z(7QQ!n9cPe(L*sR=lv_>QzMZRj$?B=Z^KuIddkgHlV`Cr^4DNdFA-~ z4m|J!9Pha&5qa|7x2^-^)UC^sQ}+5ot9uNx001BWNkl!SBP|h=bYTo~N%Bcm1CcQgpl3-l0;MA$PbH`dH1$hrnD|2e90LkR!-DWZDJj0b-MgemEYG7C&&XhM)B_+IY(u@wc!)Dktao07%yHSw zgh=(SD~W(2#Yl!|1WQWlN}NmR$N5CqA-4geA{ox5-620JJ$h-`&?L`FQ;IA>0w~cq zvx1lK58;=_<_0ZyL_|c1Hbv(tEII+GGvkSXx~Qnk%8TPn8?)oM%^+LdXORqFdFA6N zFCdhuLd9^wsa?m~+s)5}h3($$?VXk%ke}ZaUr|wdymZmZzaz+d?s@c`kxKw_&h_j8 zbGB}66_n!)1rsI|jA-L|XAq-oy2{?Qj2~4811;tmG?EdJCsHFHbTo#v-uU@s2}o_( zQIFvzUIaNXToh^N#rqO3NQ@URQ(gH^n5!h|WfH8&5`Rev(=Gn~2wZ@2V70c{sFOC) zw9+%9JffnAR-sf|*n8i9Wy+Mt9s|Qklm0RZ3k-MdIt7ZW+AV|3Lw7efHxs0TcdbJ_ zK>8fd&HcL^;mlt|oDeSEZY$#^9VCjA|xY9!s`@C)?+p#3~gKlB!mcc5+y6mKrgx2b#TOMW~OIS z)V)la<=++VH9dTKxHDSDbtTp7B1)q_j@er=^p zfivkT2~(2gxOAn)y?b9d`N~NOanif8CNZ2vhVAX;?f#*mK|#9*@7}$;`C!>Wha&HT z2M;>LA3yF>fdqN{$d8;DaL+wYd~uzUoO7L;T#x9Ud~^5+XXmAD96q)!jo=%U^t3?o z1|*i6Iytk4_uMQDAxIffKzzzLvN(q;bn1tcV>rbKSS4CU6d{rw$ie~H4fGO0dJQyz zlP8d*22~bNs(V$drt`PJzovu_q-^tsWhYJu$@1d3m~k<<`d<3v*;j5(d29uQILWk_ z5&@Aq1jo>FfBz(Jhr!+sShXodwY+yY2t^)0zVi4N2mbV99Phd3qmgUCkr;7Uzdk-? z?sTn)dgw4BVgg8_aKiscf9)(2ig0l{)Sr+X zC|OXVdL8d5D6e(GU>zN$mt6-tlNh~BPfRb!*TYR9y}X>grcVdUb@H3Qt1{+DR3tOm zl>anTPuOpYabuR&*FVc<_S`4rKu;2M^XB zba>EV{PFnX$+`C(c<~>-NAZD0_g( z*9eYo4bH+b#5p`7JYsqTx(t|cJ7-!%s&Ji><8%}nyLI7IoshKCl!en+n;+8~}wD&7$&8O#v|@!%oB)gd5>2oDLzi_@ob8X+K^ zX?4O&M1B+|$2Llf@=OcTfdt7rI}M{rc_wxP@4WOKknrCFoxLLau8WTD+c&arU#~>G znCO-0oGBCF>hyZF6^v!%Lzu&NrK(Vh3sg9Z49m-b($X9`ad%K?IY{niScB&1iA95p zya{qVK_0*F_<LSyL<7R>^V}sTLX(~PGTuXE^nMM){6xvSB`Td3L*?r zV?yc-&JhvL(<7k9h;S&9fgSG5GCa`a5X0B0J1=<*r$Y@&gGeb3RQl40$>Yu(wU;}B zo%kbVv7z*u9?jLaZ{MY)7}?hg{}O#QKWXVCWH{xq7hauYsw!GSESG3D=L*Gbcj#`E z7lPygk_5*G9~?Y-;$ZJ0hqAK#V-iD7T*s8D zB}GktUhAUQbrzeJC_5C`5$;}!A^&KkJn*t}_KH3{v+qohgg_DBOXaN>!>7!A>@l>< zlir@BVI;$2N`>X-W5STiIHhvuZNO-I{#gfjl1W6JMk6stvnO$ULXJ{rBes?L9B&hy?s1mg%IvOMw z&R!Z3!LDmL5dQx2SHFGgFDf0redy}yQ@i$=ii#X9fibiwGyxbBnu`WI5F!=mJ(x5h z$+GKi>_CO|HqIeTWo%$bi(`+Uo{pH2FV7W>_I53ysmn7hy3-rT&SIJYB~rD-WjXii8N zoIoj#9!->kp+O`_kzo@!237=CG&NaU7g{Hm9=~tmPZA{7bMNHtzkYqq8h4vD2yG@x zpQh30o;_J0=RWGwQEUuth79Kr$6%#OXN}_oA&97kB5@FqtfA8x%F5))EjoN%bfn;; zd5nzg9L-+q2uJbyKFIHDRsrnI8&-65#NnA-Pfz>u^KZWe#lQST6_iuER+*QZLwxqK zSenhv(Xb>cPbY78Dk zDvs0H^obNpv?Ajb9U1K(>Vi7_hZxvO~kiU!mf_ zCOR8J^l~;uL7icQ>}J;~loKXQN2mi}NnKs|bQJCBeW!bQ@jBhkOXWmqlo{M0DX{GD zvZ?Yaj_b7Y5qU>?o#kkXWj=!pBNz7W+ZPb00pVlceyepUZ?9dwdci4+vG$hc_M$_` zv0WY^x-H#qxivO6Ol-($NZ=)*p$L*hiaaRYOBu8nk2pqVEJGt>ZJnH)KR!P{xv;gc zHQ74%_&cxubddKvu@VL6y8CseNvd+d)?tO-IoDn}2@iyJ1tdA_iHK<9%q&FO8I>ei zdZzc~a00c4ivd2}3({0&dY~yoMzcH+az}`QBq-r4B-}gFq`b&Gio6378YKAW-iH?+ zo;Gdvv}xaL+4AjQu-+AKICr16djYE2kl=p&=*79~8l| zi0fc&Ks;7H80N==UPZEXQ@(YDPiZMCjCFEha$#Z5*3!~1{{GbGht@~;jatL`1r*7S zalqE8!wMZ5uHC$O(`e+%W=Z8FYPMVo@0K#mipN0dZFtc#ewZu&Y_Y_I8k z8PV$mB*)wEj*;X=PT}E^k$0eBMXRrvly|2pcY1*ljnq0}0utiWU-x?SsXzZkC?X?n z-xYiLvb}jZ%%G)0c=@vBzSw0CJ^FAHxxj`eBERjBuG`D6C7%+8MsddApH(o9PVCB zRJx4peW$SC=;hV7_soSeXD*+%<Co>ur{~6 z42+lg!16MJ+J14|ty^)6-Li5>Q4YZ9pHK@qmc=6%#>XGGj%QG#NDE9{Dk`jDd`b&j zeW=dkAN@4%)3N?=U{o;&7}9qmz}*M9Po0(Tup zL^L)O&T%!uk}a15si8QU_PS;rh1O>ohw*qBTV6DA69Z&MwK>_m{yeQ+Es+2&Dw1y07d-I^nL-7PGhv0VO#*AAt#?BbKI4jHTmhCOb z5`xT05JkoZ1mrWOp~!q@Me7QM(gzC&3lY)CkcB>_ExG?_?DMzl0iLHOGqnM0vWwB^ z46vCxt2F+=ITY`RJgBgm7TnRf&N*Ycs@e!_RP4SGVsym1k_hN;dSB^doIwx^K`VVV ztM)o+=e$IG>j*Mjy&|ED``(>aXO9EBxNsqi_Lk4T{p_>P{xa$9L#r+3&))}oxO+Es zN9A1}dRZQfopIT6=#Zh7ix)3$Y`oQ&)Y#CN)p+aHkgUcTwzohsr-3HCsJ7O-DL){- zNwqHd$Mdb5kRK~5d~jf9tYCKZDQx9xIsVZ<{n1pv_~_v!=oQ_syRUK2wwY>^Jr##p zr3VwE zb7OBMxZaU+?Zi>Net6-+(_d4H-*O!~boK447W<&XSo?N*Lwnk7MD)-kSwn{|o-qVQ z2a6m*5La6q5Z!T$Z#0IXo$)TK&8O*x<8ERwt5pFVA~m)mI|3(er9M8bJ;`y=ga2HH zl=a*JHr;TO*WYr-n&UoT>eQ*Tl2dBlKUn~8UR{munm*FmF+$uV5Jpc2I%G+ZnJyrs z;USqF5%CISxL9uLFH-c^`!HX{_e*{}tRU_XiaRuny(!Y(y=TvURZ+cWC?+$Q_y>m+t)i)9^R1e*5{iP~O&Q}oJkMY1GOFyLy!tVD$di-GYcTI8XIkGg&;`w?|%)C0JpKB0nN04Ef7bhStUuhhkZa`Y<4->*;A)(9WZQtZi@6c6F7wAlmh0bj#;uVVj2gg%4j^I-Uv}{xJDlaHZ58y z$-}^SSR_deI`_prC}K_9F>P9pEil?oJq3q&=<2CcbGa*}?Mt<8l9Nj- zNVC;PP?je9lmcT*Ztlu|uB-Vj$$RgW*j}Sr94yFkef%p^IKg1!?W~Q$(F;P8>$EYo zq(?sPjhI#SLRoE;7bHh`N+F3wx^fpC0gFBD(=A)3ZF%m?Ux4AOudY5dckbMM`^L^a zwrN9ZyNhZ3)qdu3kmb;zp+hZ2l$VD9C{Y4rmZvR9dfM8irp{08Z%d4DKr)>0b|^bo zR>s*_S%9qke3?i1$Wa!08eEcjm+X^FA<~NblqR>rNnTjA=wFR&uF=K!UWc887~SV& z%LD_R_j-pZk3ELcEi>HdEZEa0NK*|(Mrs^Y22?#thYQ3tS~M6)h7T+zX42|HHk*A6ZYd$d=-qHSBHJ<_ti$V{N|Tg(*|&^V&G;ffy%DM}|}+r)vd6MJ@fK4jDoULed z222gGwAeo7**6N>3L=&|F9pcH6QtC5NvZBFfjU#s6iEui!@V`HeOQr1Y(t8N7k>I_ zkKgtB=IJeue);IDuM*;1G%EJ7u^a3+L4N)am`j2S$}sa@)-9&ITW*W9Kr-u=TjLBN zd86I)w#abCtt@meSuSk-{F`~6iMNA;_ihKCw!^%LCB_@UY#k^peFP)AKL#?xSzCfp z5+rXei&{r6O8J*djjY!mo^$WLXcVuz3(u`{<{a%AHviOWi~if*ua7ZA^yS=9Mm`Zz zjQbvro&b{3I}S&Sv7suzl_-}kiPEtZcPFPrTSaWDI z^rsddsHE5=2PcVYA5Dd(J9(JQD@qF$iL{u^ux=&IW!57{KKw74@jL4Y?mI}!t;QEv z>jOMjzy0=Vi;vMP=1o|Na9#@AGo2jKULWq=H+ov{35Q9N(i4V+cQ~@cWh;aD$n2-T zeDu-JK6`5IYUq$vc&t5($7R3Uw`1+&{BE4dic4kELtJNp6C(CVmaTxi#)1-uFOMB- zyLfDB9KPC+)DYG@xEWO?&08o2ILXn((Mc#eks|kC3eo(TFq&YBK5?m_3|m#P5t93p zM~?jBXMUJ=uazTFI6p_PcLypMug|0Mi54gC-k2_BxJvq8Gv)PKzeV z!^$B>?obh}6dAp+@4}~h^!ReimtTJN*{iG1&j{L$E=49@_Lna&9(vjB@?uLiC~oQjaDR}z<#y{#ShKh6r)mo1D#OLwTT-Lap}GcQ6;hJKs909` z1WN5jw$x%9P)k*W%W7Sg+cNU!R2o?aaz|#*(X^;|LX$zKCQVwfV#*YFAb@o4+qZA@ z1Qg;4>(Cf72||v@e<&uON<{be>OJl7;h8fRe)`C?r@wjZ^KU2pWzx6XEnITk7Sj*z zhMVEsX7S~rZbMO!Sxgq+iW?g@c19d@cS|T9$;v_+q#$iwT*Tv0uC`f8(4jXCwm0*k zHw})rcljNpAlW1&omi?F*G=4m31ruz#K@9^yW|QHBMOd`>`ow)(bPQrGpvtVgGP?r zZ>$aEjV=O>Ygc63L+--B}TS0EHBfY5zjk7abfRIN%GSjqDW{I=H}_= zzWMT-&$oQb^F<~t*tLmWGAeTr0(u`wqWBJR8#)xe8l}YT7F%Z9I8h_*H9)4Os(V{4 zS>9?y!#CJlH7aFgX=x4))L&W|Z%UJCT4?7xRXCwqtFV?N8F(kCA~Bn#3z zAd{09wdAh+S(QcBDn&&N60xv6ZOce$^V!_yX8Xewx7#UKrUOhY(M-DO?mbJDLY#O zS6!WSX!VMl1@Jx*km5{U7Xl=B3c$mM-$ZTx^i8QRGpB9&=CM~lJG6HDicM(p0MV(u z-LKu>+#KCr&}GY{GeaAbCN3sJ*c)5~B`GRXb87~?k4qyUPHZkhF02)`v1fKD^F}C( z`asQ_Vl3;t0`kT7sPtG7*i^_pYStPbrAZNFASaL&g-zB?)>@8aKLdn|O3`AsE^1kn z`=ck8{-pKAsQos}S}xdRuTfJVn(_f8x#{YptLHy{WBG&$5q)R&Ua0!Kz5p4$y);{n+i&{&0$o<(I?|Ct|xOo4_Mayi8v$N;8!%tq9{$@bW z@?D1xJ!$ds%QGiTm=HblFj+1<9L)xMos1sd{Pd+ycYHeLQ^?cnaF3^_^x1xXewI4z zrW{T?=a$Smh7&wv0^JP10|05ltpi!50~ykRCQWu&Nn}UzqYvWVLT;%K+Cwxr zBku;tQN=dJ@Tre{N-St9Xq~YOsEc&pJ{Pd+UhhG{4 zX}%e`u*X+@KJIh=c0zf1O?i8bOG6`Y-CEq}wixRccTjL0!a3|M&R}I0JLB4rMj6{o zZ04!lX!0D`prj#waVe9mNQ~nT=ChbIm6g@jO4SKCDEM$4F92^qkiV)_^IM9sLOXGS zIWo|vq9eEim4_5z%H~Y24=%0oKFPdm&9!;!t)CC_iADPXa{r={vqsw3*a$}V0b3c| z!`dy@uC?ehW#-J8eWMxM%#28tXt9wSxw`FmsT(roOMMsidd;HGsrjA>wTNn$oFk1# z8XL0~yWP0a?&sI;N2{GRNQmaDkDoD*?_4c741BGLtly4|8*T*M59)iR@5DLc9 zTYmhkIh^NlYMyM!f6O))kyCk z#+Tmg29_W>rgx7&Kk($K)${E`i^^*}J#&uau-o(WBSpXVc3WHJwPkByACn|cDUmap zh>=qC-VKr*fFXKum&U6@{&;8(!~$wLUM#Cs<)v0-NyU(7OEhVk)W(lEu